code cleanup + finished contact page
This commit is contained in:
38
package-lock.json
generated
38
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "portfolio",
|
"name": "portfolio",
|
||||||
"version": "0.0.0",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "portfolio",
|
"name": "portfolio",
|
||||||
"version": "0.0.0",
|
"version": "2.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^15.1.0",
|
"@angular/animations": "^15.1.0",
|
||||||
"@angular/cdk": "^15.1.4",
|
"@angular/cdk": "^15.1.4",
|
||||||
@@ -20,12 +20,13 @@
|
|||||||
"@angular/platform-server": "^15.1.0",
|
"@angular/platform-server": "^15.1.0",
|
||||||
"@angular/router": "^15.1.0",
|
"@angular/router": "^15.1.0",
|
||||||
"@nguniversal/express-engine": "^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",
|
"chart.js": "^4.2.1",
|
||||||
"express": "^4.15.2",
|
"express": "^4.15.2",
|
||||||
"ngx-device-detector": "^5.0.1",
|
"ngx-device-detector": "^5.0.1",
|
||||||
"pocketbase": "^0.11.0",
|
"pocketbase": "^0.11.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
|
"sweetalert2": "^11.7.2",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.12.0"
|
"zone.js": "~0.12.0"
|
||||||
},
|
},
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
"@angular/cli": "~15.1.5",
|
"@angular/cli": "~15.1.5",
|
||||||
"@angular/compiler-cli": "^15.1.0",
|
"@angular/compiler-cli": "^15.1.0",
|
||||||
"@nguniversal/builders": "^15.1.0",
|
"@nguniversal/builders": "^15.1.0",
|
||||||
|
"@types/chart.js": "^2.9.37",
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
"@types/jasmine": "~4.3.0",
|
"@types/jasmine": "~4.3.0",
|
||||||
"@types/node": "^14.15.0",
|
"@types/node": "^14.15.0",
|
||||||
@@ -3931,6 +3933,11 @@
|
|||||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/@tootallnate/once": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||||
@@ -3962,6 +3969,7 @@
|
|||||||
"version": "2.9.37",
|
"version": "2.9.37",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz",
|
||||||
"integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==",
|
"integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"moment": "^2.10.2"
|
"moment": "^2.10.2"
|
||||||
}
|
}
|
||||||
@@ -9933,6 +9941,7 @@
|
|||||||
"version": "2.29.4",
|
"version": "2.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
@@ -12444,6 +12453,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/symbol-observable": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
"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==",
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
|
||||||
"dev": true
|
"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": {
|
"@tootallnate/once": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||||
@@ -16572,6 +16595,7 @@
|
|||||||
"version": "2.9.37",
|
"version": "2.9.37",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.37.tgz",
|
||||||
"integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==",
|
"integrity": "sha512-9bosRfHhkXxKYfrw94EmyDQcdjMaQPkU1fH2tDxu8DWXxf1mjzWQAV4laJF51ZbC2ycYwNDvIm1rGez8Bug0vg==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"moment": "^2.10.2"
|
"moment": "^2.10.2"
|
||||||
}
|
}
|
||||||
@@ -21214,7 +21238,8 @@
|
|||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.29.4",
|
"version": "2.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@@ -23111,6 +23136,11 @@
|
|||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
"dev": true
|
"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": {
|
"symbol-observable": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "portfolio",
|
"name": "portfolio",
|
||||||
"version": "0.0.0",
|
"version": "2.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
@@ -26,12 +26,13 @@
|
|||||||
"@angular/platform-server": "^15.1.0",
|
"@angular/platform-server": "^15.1.0",
|
||||||
"@angular/router": "^15.1.0",
|
"@angular/router": "^15.1.0",
|
||||||
"@nguniversal/express-engine": "^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",
|
"chart.js": "^4.2.1",
|
||||||
"express": "^4.15.2",
|
"express": "^4.15.2",
|
||||||
"ngx-device-detector": "^5.0.1",
|
"ngx-device-detector": "^5.0.1",
|
||||||
"pocketbase": "^0.11.0",
|
"pocketbase": "^0.11.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
|
"sweetalert2": "^11.7.2",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.12.0"
|
"zone.js": "~0.12.0"
|
||||||
},
|
},
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
"@angular/cli": "~15.1.5",
|
"@angular/cli": "~15.1.5",
|
||||||
"@angular/compiler-cli": "^15.1.0",
|
"@angular/compiler-cli": "^15.1.0",
|
||||||
"@nguniversal/builders": "^15.1.0",
|
"@nguniversal/builders": "^15.1.0",
|
||||||
|
"@types/chart.js": "^2.9.37",
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
"@types/jasmine": "~4.3.0",
|
"@types/jasmine": "~4.3.0",
|
||||||
"@types/node": "^14.15.0",
|
"@types/node": "^14.15.0",
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
import {HomeComponent} from "./sites/home/home.component";
|
import {HomeComponent} from "./sites/home/home.component";
|
||||||
import {ProjectsComponent} from "./sites/projects/projects.component";
|
import {ProjectsComponent} from "./sites/projects/projects.component";
|
||||||
import {TechnologiesComponent} from "./sites/technologies/technologies.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 = [
|
const routes: Routes = [
|
||||||
{path: "", component: HomeComponent},
|
{path: "", component: HomeComponent},
|
||||||
{path: "projects", component: ProjectsComponent},
|
{path: "projects", component: ProjectsComponent},
|
||||||
{path: "technologies", component: TechnologiesComponent},
|
{path: "technologies", component: TechnologiesComponent},
|
||||||
|
{path: "about", component: AboutComponent},
|
||||||
|
{path: "contact", component: ContactComponent},
|
||||||
{path: "**", pathMatch: "full", redirectTo: ""}
|
{path: "**", pathMatch: "full", redirectTo: ""}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { NavigationComponent } from './components/navigation/navigation.componen
|
|||||||
import {MatIconModule} from "@angular/material/icon";
|
import {MatIconModule} from "@angular/material/icon";
|
||||||
import {MatButtonModule} from "@angular/material/button";
|
import {MatButtonModule} from "@angular/material/button";
|
||||||
import { HomeComponent } from './sites/home/home.component';
|
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 { ProjectsComponent } from './sites/projects/projects.component';
|
||||||
import {MatTooltipModule} from "@angular/material/tooltip";
|
import {MatTooltipModule} from "@angular/material/tooltip";
|
||||||
import { FancyButtonComponent } from './components/fancy-button/fancy-button.component';
|
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 { LanguagesPipe } from './pipes/languages.pipe';
|
||||||
import { FrameworksPipe } from './pipes/frameworks.pipe';
|
import { FrameworksPipe } from './pipes/frameworks.pipe';
|
||||||
import { SkillsPipe } from './pipes/skills.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({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
NavigationComponent,
|
NavigationComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
FeaturedProjectsPipe,
|
FeaturedPipe,
|
||||||
ProjectsComponent,
|
ProjectsComponent,
|
||||||
FancyButtonComponent,
|
FancyButtonComponent,
|
||||||
ProjectComponent,
|
ProjectComponent,
|
||||||
@@ -32,7 +36,9 @@ import { SkillsPipe } from './pipes/skills.pipe';
|
|||||||
TechnologyComponent,
|
TechnologyComponent,
|
||||||
LanguagesPipe,
|
LanguagesPipe,
|
||||||
FrameworksPipe,
|
FrameworksPipe,
|
||||||
SkillsPipe
|
SkillsPipe,
|
||||||
|
ContactComponent,
|
||||||
|
AboutComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule.withServerTransition({appId: 'serverApp'}),
|
BrowserModule.withServerTransition({appId: 'serverApp'}),
|
||||||
@@ -40,7 +46,9 @@ import { SkillsPipe } from './pipes/skills.pipe';
|
|||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatTooltipModule
|
MatTooltipModule,
|
||||||
|
MatInputModule,
|
||||||
|
ReactiveFormsModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
$border-color: #2d2d2d;
|
@use "src/theme";
|
||||||
|
@use "sass:map";
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid theme.$border-color;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@@ -67,7 +68,8 @@ $border-color: #2d2d2d;
|
|||||||
transform: translateY(-100%);
|
transform: translateY(-100%);
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-top: 1px solid $border-color;
|
border-top: 1px solid theme.$border-color;
|
||||||
|
background-color: map.get(theme.$background, 'background');
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-columns: minmax(0, 1fr);
|
grid-auto-columns: minmax(0, 1fr);
|
||||||
@@ -85,7 +87,7 @@ $border-color: #2d2d2d;
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
content: '';
|
content: '';
|
||||||
border-right: 1px solid $border-color;
|
border-right: 1px solid theme.$border-color;
|
||||||
transition: background-color 0.3s ease-in-out;
|
transition: background-color 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {DeviceDetectorService} from "ngx-device-detector";
|
import {DeviceDetectorService} from "ngx-device-detector";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
|
import {BackendService} from "../../services/backend.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-navigation',
|
selector: 'app-navigation',
|
||||||
templateUrl: './navigation.component.html',
|
templateUrl: './navigation.component.html',
|
||||||
styleUrls: ['./navigation.component.scss']
|
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}[] = [
|
public navLinks: {label: string, href: string, icon?: string}[] = [
|
||||||
{label: 'Home', href: '/', icon: 'home'},
|
{label: 'Home', href: '/', icon: 'home'},
|
||||||
@@ -19,11 +20,7 @@ export class NavigationComponent {
|
|||||||
{label: 'Kontakt', href: '/contact', icon: 'mail'}
|
{label: 'Kontakt', href: '/contact', icon: 'mail'}
|
||||||
];
|
];
|
||||||
|
|
||||||
public socialLinks: {href: string, image: string}[] = [
|
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 cleanUrl(url: string): string {
|
public cleanUrl(url: string): string {
|
||||||
try {
|
try {
|
||||||
@@ -39,4 +36,8 @@ export class NavigationComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.socialLinks = await this.backend.getSocials();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/app/models/message.ts
Normal file
5
src/app/models/message.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface Message {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
4
src/app/models/social.ts
Normal file
4
src/app/models/social.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface Social {
|
||||||
|
href: string;
|
||||||
|
image: string;
|
||||||
|
}
|
||||||
5
src/app/models/timestamp.ts
Normal file
5
src/app/models/timestamp.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface Timestamp {
|
||||||
|
date: number,
|
||||||
|
description: string;
|
||||||
|
featured?: boolean;
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
12
src/app/pipes/featured.pipe.ts
Normal file
12
src/app/pipes/featured.pipe.ts
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,12 +7,7 @@ import {Technology} from "../models/technology";
|
|||||||
export class FrameworksPipe implements PipeTransform {
|
export class FrameworksPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(objects: Technology[]): Technology[] {
|
transform(objects: Technology[]): Technology[] {
|
||||||
const newObjects: Technology[] = [];
|
return objects?.filter(obj => obj.type == "Framework");
|
||||||
objects?.forEach(obj => {
|
|
||||||
if (obj?.type == "Framework")
|
|
||||||
newObjects.push(obj);
|
|
||||||
})
|
|
||||||
return newObjects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,7 @@ import {Technology} from "../models/technology";
|
|||||||
export class LanguagesPipe implements PipeTransform {
|
export class LanguagesPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(objects: Technology[]): Technology[] {
|
transform(objects: Technology[]): Technology[] {
|
||||||
const newObjects: Technology[] = [];
|
return objects?.filter(obj => obj.type == "Language");
|
||||||
objects?.forEach(obj => {
|
|
||||||
if (obj?.type == "Language")
|
|
||||||
newObjects.push(obj);
|
|
||||||
})
|
|
||||||
return newObjects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,7 @@ import {Technology} from "../models/technology";
|
|||||||
export class SkillsPipe implements PipeTransform {
|
export class SkillsPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(objects: Technology[]): Technology[] {
|
transform(objects: Technology[]): Technology[] {
|
||||||
const newObjects: Technology[] = [];
|
return objects?.filter(obj => obj.type == "Additional");
|
||||||
objects?.forEach(obj => {
|
|
||||||
if (obj?.type == "Additional")
|
|
||||||
newObjects.push(obj);
|
|
||||||
})
|
|
||||||
return newObjects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { Injectable } from '@angular/core';
|
|||||||
import PocketBase from 'pocketbase';
|
import PocketBase from 'pocketbase';
|
||||||
import {Language, Project} from "../models/project";
|
import {Language, Project} from "../models/project";
|
||||||
import {Technology} from "../models/technology";
|
import {Technology} from "../models/technology";
|
||||||
|
import {Timestamp} from "../models/timestamp";
|
||||||
|
import {Social} from "../models/social";
|
||||||
|
import {Message} from "../models/message";
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -46,4 +49,20 @@ export class BackendService {
|
|||||||
return await this.pb?.collection('technologies').getFullList();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/app/sites/about/about.component.html
Normal file
1
src/app/sites/about/about.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<p>about works!</p>
|
||||||
0
src/app/sites/about/about.component.scss
Normal file
0
src/app/sites/about/about.component.scss
Normal file
10
src/app/sites/about/about.component.ts
Normal file
10
src/app/sites/about/about.component.ts
Normal 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 {
|
||||||
|
|
||||||
|
}
|
||||||
41
src/app/sites/contact/contact.component.html
Normal file
41
src/app/sites/contact/contact.component.html
Normal 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>
|
||||||
89
src/app/sites/contact/contact.component.scss
Normal file
89
src/app/sites/contact/contact.component.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/app/sites/contact/contact.component.ts
Normal file
40
src/app/sites/contact/contact.component.ts
Normal 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<h1 class="title">Projekte</h1>
|
<h1 class="title">Projekte</h1>
|
||||||
<a routerLink="/projects">alle ansehen</a>
|
<a routerLink="/projects">alle ansehen</a>
|
||||||
<div id="projects-wrapper" #projectsWrapper>
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<h1 class="title">Technologien</h1>
|
<h1 class="title">Technologien</h1>
|
||||||
<a routerLink="/technologies">mehr erfahren</a>
|
<a routerLink="/technologies">mehr erfahren</a>
|
||||||
<div class="technologies-wrapper">
|
<div class="technologies-wrapper">
|
||||||
<app-technology *ngFor="let technology of technologies" [technology]="technology" />
|
<app-technology *ngFor="let technology of technologies | featured" [technology]="technology" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<h1 class="title">Über mich</h1>
|
<h1 class="title">Über mich</h1>
|
||||||
<a routerLink="/about">mehr erfahren</a>
|
<a routerLink="/about">mehr erfahren</a>
|
||||||
<div id="timeline" #timelineElement>
|
<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>
|
<h2>{{timestamp.date}}</h2>
|
||||||
<span>{{timestamp.description}}</span>
|
<span>{{timestamp.description}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -115,8 +115,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
box-sizing: border-box;
|
||||||
color: theme.$desc-color;
|
color: theme.$desc-color;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {Project} from "../../models/project";
|
|||||||
import {Technology} from "../../models/technology";
|
import {Technology} from "../../models/technology";
|
||||||
import {BackendService} from "../../services/backend.service";
|
import {BackendService} from "../../services/backend.service";
|
||||||
import {AnimatorService} from "../../services/animator.service";
|
import {AnimatorService} from "../../services/animator.service";
|
||||||
|
import {Timestamp} from "../../models/timestamp";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
@@ -16,29 +17,20 @@ export class HomeComponent implements OnInit, AfterViewInit {
|
|||||||
@ViewChild('timelineElement') timelineElement: ElementRef;
|
@ViewChild('timelineElement') timelineElement: ElementRef;
|
||||||
public projects: Project[];
|
public projects: Project[];
|
||||||
public technologies: Technology[];
|
public technologies: Technology[];
|
||||||
|
public timeline: Timestamp[];
|
||||||
|
public socialLinks: {href: string, image: string}[];
|
||||||
|
|
||||||
public constructor(public deviceService: DeviceDetectorService, private backend: BackendService, private animator: AnimatorService) {}
|
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 {
|
public getAnimationDelay(index: number, multiplier = 150): string {
|
||||||
return `${index * multiplier}ms`;
|
return `${index * multiplier}ms`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.projects = await this.backend.getProjects();
|
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 {
|
ngAfterViewInit(): void {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<h1 class="title">Technologien in Projekten</h1>
|
<h1 class="title">Technologien in Projekten</h1>
|
||||||
<div class="chart">
|
<div class="chart">
|
||||||
<div class="chart-container"><canvas #chard></canvas></div>
|
<div class="chart-container"><canvas #chart></canvas></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {DeviceDetectorService} from "ngx-device-detector";
|
|||||||
})
|
})
|
||||||
export class TechnologiesComponent implements AfterViewInit {
|
export class TechnologiesComponent implements AfterViewInit {
|
||||||
|
|
||||||
@ViewChild('chard') chartRef: ElementRef;
|
@ViewChild('chart') chartRef: ElementRef;
|
||||||
public technologies: Technology[];
|
public technologies: Technology[];
|
||||||
|
|
||||||
public constructor(public deviceService: DeviceDetectorService, private backend: BackendService) {}
|
public constructor(public deviceService: DeviceDetectorService, private backend: BackendService) {}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
@use "sass:map";
|
@use "sass:map";
|
||||||
@use "theme";
|
@use "theme";
|
||||||
|
|
||||||
|
@import '@sweetalert2/theme-dark/dark.scss';
|
||||||
|
|
||||||
html, body { height: 100vh; }
|
html, body { height: 100vh; }
|
||||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||||
*, html {scroll-behavior: smooth !important;}
|
*, html {scroll-behavior: smooth !important;}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ $padding: 12.5vw;
|
|||||||
$padding-small: 5vw;
|
$padding-small: 5vw;
|
||||||
|
|
||||||
$desc-color: #7c8393;
|
$desc-color: #7c8393;
|
||||||
|
$border-color: #2d2d2d;
|
||||||
|
|
||||||
$color-config: mat.get-color-config($angular-theme);
|
$color-config: mat.get-color-config($angular-theme);
|
||||||
$background: map.get($color-config, 'background');
|
$background: map.get($color-config, 'background');
|
||||||
@@ -84,3 +85,7 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-mdc-text-field-wrapper {
|
||||||
|
background-color: map.get($background, 'background') !important;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user