code cleanup + finished contact page

This commit is contained in:
2023-02-21 20:23:19 +01:00
parent a55c60d88a
commit 484294e611
28 changed files with 318 additions and 77 deletions

38
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "portfolio",
"version": "0.0.0",
"version": "2.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "portfolio",
"version": "0.0.0",
"version": "2.0.0",
"dependencies": {
"@angular/animations": "^15.1.0",
"@angular/cdk": "^15.1.4",
@@ -20,12 +20,13 @@
"@angular/platform-server": "^15.1.0",
"@angular/router": "^15.1.0",
"@nguniversal/express-engine": "^15.1.0",
"@types/chart.js": "^2.9.37",
"@sweetalert2/theme-dark": "^5.0.15",
"chart.js": "^4.2.1",
"express": "^4.15.2",
"ngx-device-detector": "^5.0.1",
"pocketbase": "^0.11.0",
"rxjs": "~7.8.0",
"sweetalert2": "^11.7.2",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
@@ -34,6 +35,7 @@
"@angular/cli": "~15.1.5",
"@angular/compiler-cli": "^15.1.0",
"@nguniversal/builders": "^15.1.0",
"@types/chart.js": "^2.9.37",
"@types/express": "^4.17.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^14.15.0",
@@ -3931,6 +3933,11 @@
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
"dev": true
},
"node_modules/@sweetalert2/theme-dark": {
"version": "5.0.15",
"resolved": "https://registry.npmjs.org/@sweetalert2/theme-dark/-/theme-dark-5.0.15.tgz",
"integrity": "sha512-g1QCwQVOkiAz5hIEBOIvvu0580lubu4KuQlod+48QetYzGIEXNlHEH36QihCDnGVgE6vx48iO48w9q0WrZWyHQ=="
},
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -3962,6 +3969,7 @@
"version": "2.9.37",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz",
"integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==",
"dev": true,
"dependencies": {
"moment": "^2.10.2"
}
@@ -9933,6 +9941,7 @@
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"dev": true,
"engines": {
"node": "*"
}
@@ -12444,6 +12453,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sweetalert2": {
"version": "11.7.2",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.2.tgz",
"integrity": "sha512-atPjDa3fv/4xwZpiAt7FZUgAhR5VAASiLP2hu7HUeVDXx+v4/9nD1W0u8xal1e9f2/qGh0DwTxPXPV9XoZIBvg==",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/limonte"
}
},
"node_modules/symbol-observable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
@@ -16544,6 +16562,11 @@
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
"dev": true
},
"@sweetalert2/theme-dark": {
"version": "5.0.15",
"resolved": "https://registry.npmjs.org/@sweetalert2/theme-dark/-/theme-dark-5.0.15.tgz",
"integrity": "sha512-g1QCwQVOkiAz5hIEBOIvvu0580lubu4KuQlod+48QetYzGIEXNlHEH36QihCDnGVgE6vx48iO48w9q0WrZWyHQ=="
},
"@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -16572,6 +16595,7 @@
"version": "2.9.37",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz",
"integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==",
"dev": true,
"requires": {
"moment": "^2.10.2"
}
@@ -21214,7 +21238,8 @@
"moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"dev": true
},
"ms": {
"version": "2.1.2",
@@ -23111,6 +23136,11 @@
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"sweetalert2": {
"version": "11.7.2",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.2.tgz",
"integrity": "sha512-atPjDa3fv/4xwZpiAt7FZUgAhR5VAASiLP2hu7HUeVDXx+v4/9nD1W0u8xal1e9f2/qGh0DwTxPXPV9XoZIBvg=="
},
"symbol-observable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "portfolio",
"version": "0.0.0",
"version": "2.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
@@ -26,12 +26,13 @@
"@angular/platform-server": "^15.1.0",
"@angular/router": "^15.1.0",
"@nguniversal/express-engine": "^15.1.0",
"@types/chart.js": "^2.9.37",
"@sweetalert2/theme-dark": "^5.0.15",
"chart.js": "^4.2.1",
"express": "^4.15.2",
"ngx-device-detector": "^5.0.1",
"pocketbase": "^0.11.0",
"rxjs": "~7.8.0",
"sweetalert2": "^11.7.2",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
@@ -40,6 +41,7 @@
"@angular/cli": "~15.1.5",
"@angular/compiler-cli": "^15.1.0",
"@nguniversal/builders": "^15.1.0",
"@types/chart.js": "^2.9.37",
"@types/express": "^4.17.0",
"@types/jasmine": "~4.3.0",
"@types/node": "^14.15.0",

View File

@@ -3,11 +3,15 @@ import { RouterModule, Routes } from '@angular/router';
import {HomeComponent} from "./sites/home/home.component";
import {ProjectsComponent} from "./sites/projects/projects.component";
import {TechnologiesComponent} from "./sites/technologies/technologies.component";
import {AboutComponent} from "./sites/about/about.component";
import {ContactComponent} from "./sites/contact/contact.component";
const routes: Routes = [
{path: "", component: HomeComponent},
{path: "projects", component: ProjectsComponent},
{path: "technologies", component: TechnologiesComponent},
{path: "about", component: AboutComponent},
{path: "contact", component: ContactComponent},
{path: "**", pathMatch: "full", redirectTo: ""}
];

View File

@@ -8,7 +8,7 @@ import { NavigationComponent } from './components/navigation/navigation.componen
import {MatIconModule} from "@angular/material/icon";
import {MatButtonModule} from "@angular/material/button";
import { HomeComponent } from './sites/home/home.component';
import { FeaturedProjectsPipe } from './pipes/featured-projects.pipe';
import { FeaturedPipe } from './pipes/featured.pipe';
import { ProjectsComponent } from './sites/projects/projects.component';
import {MatTooltipModule} from "@angular/material/tooltip";
import { FancyButtonComponent } from './components/fancy-button/fancy-button.component';
@@ -18,13 +18,17 @@ import { TechnologyComponent } from './components/technology/technology.componen
import { LanguagesPipe } from './pipes/languages.pipe';
import { FrameworksPipe } from './pipes/frameworks.pipe';
import { SkillsPipe } from './pipes/skills.pipe';
import { ContactComponent } from './sites/contact/contact.component';
import { AboutComponent } from './sites/about/about.component';
import {MatInputModule} from "@angular/material/input";
import {ReactiveFormsModule} from "@angular/forms";
@NgModule({
declarations: [
AppComponent,
NavigationComponent,
HomeComponent,
FeaturedProjectsPipe,
FeaturedPipe,
ProjectsComponent,
FancyButtonComponent,
ProjectComponent,
@@ -32,7 +36,9 @@ import { SkillsPipe } from './pipes/skills.pipe';
TechnologyComponent,
LanguagesPipe,
FrameworksPipe,
SkillsPipe
SkillsPipe,
ContactComponent,
AboutComponent
],
imports: [
BrowserModule.withServerTransition({appId: 'serverApp'}),
@@ -40,7 +46,9 @@ import { SkillsPipe } from './pipes/skills.pipe';
BrowserAnimationsModule,
MatIconModule,
MatButtonModule,
MatTooltipModule
MatTooltipModule,
MatInputModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]

View File

@@ -1,9 +1,10 @@
$border-color: #2d2d2d;
@use "src/theme";
@use "sass:map";
.header {
width: 100vw;
height: 50px;
border-bottom: 1px solid $border-color;
border-bottom: 1px solid theme.$border-color;
overflow: hidden;
display: flex;
user-select: none;
@@ -67,7 +68,8 @@ $border-color: #2d2d2d;
transform: translateY(-100%);
height: 50px;
width: 100%;
border-top: 1px solid $border-color;
border-top: 1px solid theme.$border-color;
background-color: map.get(theme.$background, 'background');
display: grid;
grid-auto-columns: minmax(0, 1fr);
@@ -85,7 +87,7 @@ $border-color: #2d2d2d;
position: absolute;
inset: 0;
content: '';
border-right: 1px solid $border-color;
border-right: 1px solid theme.$border-color;
transition: background-color 0.3s ease-in-out;
}

View File

@@ -1,15 +1,16 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {DeviceDetectorService} from "ngx-device-detector";
import {Router} from "@angular/router";
import {BackendService} from "../../services/backend.service";
@Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss']
})
export class NavigationComponent {
export class NavigationComponent implements OnInit {
public constructor(public deviceService: DeviceDetectorService, public router: Router) {}
public constructor(public deviceService: DeviceDetectorService, public router: Router, private backend: BackendService) {}
public navLinks: {label: string, href: string, icon?: string}[] = [
{label: 'Home', href: '/', icon: 'home'},
@@ -19,11 +20,7 @@ export class NavigationComponent {
{label: 'Kontakt', href: '/contact', icon: 'mail'}
];
public socialLinks: {href: string, image: string}[] = [
{href: 'https://www.instagram.com/leonh.23/', image: 'https://instagram.com/favicon.ico'},
{href: 'https://git.leon-hoppe.de/leon.hoppe', image: 'https://git.leon-hoppe.de/favicon.ico'},
{href: 'mailto://leon@ladenbau-hoppe.de', image: 'https://webmail.strato.de/favicon.ico'}
];
public socialLinks: {href: string, image: string}[];
public cleanUrl(url: string): string {
try {
@@ -39,4 +36,8 @@ export class NavigationComponent {
}
}
async ngOnInit() {
this.socialLinks = await this.backend.getSocials();
}
}

View File

@@ -0,0 +1,5 @@
export interface Message {
name: string;
email: string;
message: string;
}

4
src/app/models/social.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface Social {
href: string;
image: string;
}

View File

@@ -0,0 +1,5 @@
export interface Timestamp {
date: number,
description: string;
featured?: boolean;
}

View File

@@ -1,18 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
import {Project} from "../models/project";
@Pipe({
name: 'featuredProjects'
})
export class FeaturedProjectsPipe implements PipeTransform {
transform(objects: Project[]): Project[] {
const newObjects: Project[] = [];
objects?.forEach(obj => {
if (obj?.featured)
newObjects.push(obj);
})
return newObjects;
}
}

View File

@@ -0,0 +1,12 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'featured'
})
export class FeaturedPipe implements PipeTransform {
transform(objects: (any & {featured: boolean})[]): any[] {
return objects?.filter(obj => obj.featured);
}
}

View File

@@ -7,12 +7,7 @@ import {Technology} from "../models/technology";
export class FrameworksPipe implements PipeTransform {
transform(objects: Technology[]): Technology[] {
const newObjects: Technology[] = [];
objects?.forEach(obj => {
if (obj?.type == "Framework")
newObjects.push(obj);
})
return newObjects;
return objects?.filter(obj => obj.type == "Framework");
}
}

View File

@@ -7,12 +7,7 @@ import {Technology} from "../models/technology";
export class LanguagesPipe implements PipeTransform {
transform(objects: Technology[]): Technology[] {
const newObjects: Technology[] = [];
objects?.forEach(obj => {
if (obj?.type == "Language")
newObjects.push(obj);
})
return newObjects;
return objects?.filter(obj => obj.type == "Language");
}
}

View File

@@ -7,12 +7,7 @@ import {Technology} from "../models/technology";
export class SkillsPipe implements PipeTransform {
transform(objects: Technology[]): Technology[] {
const newObjects: Technology[] = [];
objects?.forEach(obj => {
if (obj?.type == "Additional")
newObjects.push(obj);
})
return newObjects;
return objects?.filter(obj => obj.type == "Additional");
}
}

View File

@@ -2,6 +2,9 @@ import { Injectable } from '@angular/core';
import PocketBase from 'pocketbase';
import {Language, Project} from "../models/project";
import {Technology} from "../models/technology";
import {Timestamp} from "../models/timestamp";
import {Social} from "../models/social";
import {Message} from "../models/message";
@Injectable({
@@ -46,4 +49,20 @@ export class BackendService {
return await this.pb?.collection('technologies').getFullList();
}
public async getTimeline(): Promise<Timestamp[]> {
return await this.pb?.collection('timeline').getFullList();
}
public async getSocials(): Promise<Social[]> {
return [
{href: 'https://www.instagram.com/leonh.23/', image: 'https://instagram.com/favicon.ico'},
{href: 'https://git.leon-hoppe.de/leon.hoppe', image: 'https://git.leon-hoppe.de/favicon.ico'},
{href: 'mailto://leon@ladenbau-hoppe.de', image: 'https://webmail.strato.de/favicon.ico'}
];
}
public async sendMessage(message: Message) {
await this.pb?.collection('messages').create(message);
}
}

View File

@@ -0,0 +1 @@
<p>about works!</p>

View File

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent {
}

View File

@@ -0,0 +1,41 @@
<div class="hider" [ngClass]="{'mobile': device.isMobile()}">
<div class="form-wrapper" [ngClass]="{'mobile': device.isMobile()}">
<form [formGroup]="form" (ngSubmit)="sendMessage()">
<section id="contact-info">
<h1>Kontakt</h1>
<span>leon@ladenbau-hoppe.de</span>
<span>+49 1575 8839776</span>
</section>
<section id="contact-form">
<h1>Schreiben Sie mir!</h1>
<div id="fields">
<mat-form-field appearance="fill">
<mat-label>Name</mat-label>
<input matInput formControlName="name">
<mat-error>Name ist erforderlich</mat-error>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Email</mat-label>
<input matInput formControlName="email">
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
</mat-error>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Nachricht</mat-label>
<textarea matInput formControlName="message"></textarea>
<mat-error>Nachricht ist erforderlich</mat-error>
</mat-form-field>
</div>
<button mat-button type="submit">Senden</button>
</section>
</form>
</div>
</div>

View File

@@ -0,0 +1,89 @@
@use "src/theme";
.hider {
overflow: hidden;
height: 100%;
&.mobile {
overflow-y: auto;
}
}
.form-wrapper {
width: 100%;
height: 100vh;
display: grid;
place-items: center;
form {
width: 700px;
height: 500px;
border: 1px solid theme.$border-color;
border-radius: 20px;
display: grid;
grid-template-columns: 250px 1fr;
section {
padding: 15px;
box-sizing: border-box;
}
h1 {
margin: 0 0 20px;
font-size: 30px;
user-select: none;
}
#contact-info {
border-right: 1px solid theme.$border-color;
display: flex;
flex-direction: column;
gap: 15px;
}
#contact-form {
display: flex;
flex-direction: column;
#fields {
display: flex;
flex-direction: column;
flex-grow: 1;
gap: 10px;
textarea {
resize: none;
height: 120px;
}
}
button {
margin-top: auto;
align-self: center;
}
}
}
&.mobile {
display: block;
height: max-content;
form {
border: 0;
width: 100%;
height: max-content;
grid-template-columns: unset;
grid-template-rows: max-content 1fr;
#contact-info {
border-right: 0;
border-bottom: 1px solid theme.$border-color;
}
button {
margin-top: 20px;
}
}
}
}

View File

@@ -0,0 +1,40 @@
import { Component } from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import Swal from 'sweetalert2/dist/sweetalert2.js';
import {BackendService} from "../../services/backend.service";
import {DeviceDetectorService} from "ngx-device-detector";
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.scss']
})
export class ContactComponent {
public form: FormGroup = new FormGroup({
name: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email]),
message: new FormControl('', [Validators.required])
});
public constructor(public backend: BackendService, public device: DeviceDetectorService) {}
public async sendMessage() {
if (!this.form.valid) return;
await this.backend.sendMessage({
name: this.form.get('name').value,
email: this.form.get('email').value,
message: this.form.get('message').value
});
this.form.reset();
Swal.fire({
icon: 'success',
title: 'Nachricht gesendet',
showConfirmButton: false,
timer: 1500
});
}
}

View File

@@ -19,7 +19,7 @@
<h1 class="title">Projekte</h1>
<a routerLink="/projects">alle ansehen</a>
<div id="projects-wrapper" #projectsWrapper>
<app-project *ngFor="let project of projects | featuredProjects; let i = index" [project]="project" [ngStyle]="{'animation-delay': getAnimationDelay(i)}" />
<app-project *ngFor="let project of projects | featured; let i = index" [project]="project" [ngStyle]="{'animation-delay': getAnimationDelay(i)}" />
</div>
</section>
@@ -27,7 +27,7 @@
<h1 class="title">Technologien</h1>
<a routerLink="/technologies">mehr erfahren</a>
<div class="technologies-wrapper">
<app-technology *ngFor="let technology of technologies" [technology]="technology" />
<app-technology *ngFor="let technology of technologies | featured" [technology]="technology" />
</div>
</section>
@@ -35,7 +35,7 @@
<h1 class="title">Über mich</h1>
<a routerLink="/about">mehr erfahren</a>
<div id="timeline" #timelineElement>
<div class="timestamp" *ngFor="let timestamp of timeline; let i = index" [ngStyle]="{'--delay': getAnimationDelay(i, 500)}">
<div class="timestamp" *ngFor="let timestamp of timeline | featured; let i = index" [ngStyle]="{'--delay': getAnimationDelay(i, 500)}">
<h2>{{timestamp.date}}</h2>
<span>{{timestamp.description}}</span>
</div>

View File

@@ -115,8 +115,10 @@
}
span {
box-sizing: border-box;
color: theme.$desc-color;
font-size: 14px;
padding-right: 10px;
}
&:after {

View File

@@ -4,6 +4,7 @@ import {Project} from "../../models/project";
import {Technology} from "../../models/technology";
import {BackendService} from "../../services/backend.service";
import {AnimatorService} from "../../services/animator.service";
import {Timestamp} from "../../models/timestamp";
@Component({
selector: 'app-home',
@@ -16,29 +17,20 @@ export class HomeComponent implements OnInit, AfterViewInit {
@ViewChild('timelineElement') timelineElement: ElementRef;
public projects: Project[];
public technologies: Technology[];
public timeline: Timestamp[];
public socialLinks: {href: string, image: string}[];
public constructor(public deviceService: DeviceDetectorService, private backend: BackendService, private animator: AnimatorService) {}
public timeline: {date: number, description: string}[] = [
{date: 2010, description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?"},
{date: 2015, description: "Lorem ipsum dolor sit amet"},
{date: 2017, description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?"},
{date: 2022, description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur excepturi facere, fuga maxime nulla qui voluptas voluptates? Adipisci asperiores dolor error iste sunt tempore. Blanditiis illum mollitia nostrum quae vero?"},
];
public socialLinks: {href: string, image: string}[] = [
{href: 'https://www.instagram.com/leonh.23/', image: 'https://instagram.com/favicon.ico'},
{href: 'https://git.leon-hoppe.de/leon.hoppe', image: 'https://git.leon-hoppe.de/favicon.ico'},
{href: 'mailto://leon@ladenbau-hoppe.de', image: 'https://webmail.strato.de/favicon.ico'}
];
public getAnimationDelay(index: number, multiplier = 150): string {
return `${index * multiplier}ms`;
}
async ngOnInit() {
this.projects = await this.backend.getProjects();
this.technologies = (await this.backend.getTechnologies()).filter(tech => tech.featured);
this.technologies = await this.backend.getTechnologies();
this.timeline = await this.backend.getTimeline();
this.socialLinks = await this.backend.getSocials();
}
ngAfterViewInit(): void {

View File

@@ -6,7 +6,7 @@
</div>
<h1 class="title">Technologien in Projekten</h1>
<div class="chart">
<div class="chart-container"><canvas #chard></canvas></div>
<div class="chart-container"><canvas #chart></canvas></div>
</div>
</section>

View File

@@ -11,7 +11,7 @@ import {DeviceDetectorService} from "ngx-device-detector";
})
export class TechnologiesComponent implements AfterViewInit {
@ViewChild('chard') chartRef: ElementRef;
@ViewChild('chart') chartRef: ElementRef;
public technologies: Technology[];
public constructor(public deviceService: DeviceDetectorService, private backend: BackendService) {}

View File

@@ -1,6 +1,8 @@
@use "sass:map";
@use "theme";
@import '@sweetalert2/theme-dark/dark.scss';
html, body { height: 100vh; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
*, html {scroll-behavior: smooth !important;}

View File

@@ -52,6 +52,7 @@ $padding: 12.5vw;
$padding-small: 5vw;
$desc-color: #7c8393;
$border-color: #2d2d2d;
$color-config: mat.get-color-config($angular-theme);
$background: map.get($color-config, 'background');
@@ -84,3 +85,7 @@ body {
}
}
}
.mat-mdc-text-field-wrapper {
background-color: map.get($background, 'background') !important;
}