diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dd86fd9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +# Ignore the node_modules directory +node_modules + +# Ignore the dist directory +dist + +# Ignore the .git directory +.git + +# Ignore the .gitignore file +.gitignore + +# Ignore the .dockerignore file +.dockerignore diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7924f62 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +#stage 1 +FROM node:18-slim as node +WORKDIR /app +COPY . . +RUN npm install +RUN npm run build:ssr --omit=dev +#stage 2 +FROM node:18-slim +COPY --from=node /app/dist /app/dist +WORKDIR /app +CMD ["node", "dist/Portfolio/server/main.js"] diff --git a/Inspiration/Github style.jpg b/Inspiration/Github style.jpg deleted file mode 100644 index 77e0e8c..0000000 Binary files a/Inspiration/Github style.jpg and /dev/null differ diff --git a/Inspiration/pie chart.png b/Inspiration/pie chart.png deleted file mode 100644 index 5030459..0000000 Binary files a/Inspiration/pie chart.png and /dev/null differ diff --git a/angular.json b/angular.json index 1dbfd13..ef45ca8 100644 --- a/angular.json +++ b/angular.json @@ -27,7 +27,9 @@ "inlineStyleLanguage": "scss", "assets": [ "src/favicon.ico", - "src/assets" + "src/assets", + "src/robots.txt", + "src/sitemap.xml" ], "styles": [ "src/styles.scss", @@ -40,13 +42,13 @@ "budgets": [ { "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" + "maximumWarning": "5mb", + "maximumError": "10mb" }, { "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" + "maximumWarning": "100kb", + "maximumError": "100kb" } ], "outputHashing": "all" diff --git a/package-lock.json b/package-lock.json index cf063d1..d4dc4d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,13 +20,10 @@ "@angular/platform-server": "^15.1.0", "@angular/router": "^15.1.0", "@nguniversal/express-engine": "^15.1.0", - "@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" }, @@ -3933,11 +3930,6 @@ "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", @@ -10036,18 +10028,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/ngx-device-detector": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ngx-device-detector/-/ngx-device-detector-5.0.1.tgz", - "integrity": "sha512-hVKaGzyXzy6zeliYyN7runz3eOOsh3tmZ8A6P5MSpHIjVjSx3pUJcobFTKNyHGn/zGS4JFWuhSSb7QmNwmqK9w==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "^15.0.0", - "@angular/core": "^15.0.0" - } - }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -12453,15 +12433,6 @@ "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", @@ -16562,11 +16533,6 @@ "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", @@ -21312,14 +21278,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "ngx-device-detector": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ngx-device-detector/-/ngx-device-detector-5.0.1.tgz", - "integrity": "sha512-hVKaGzyXzy6zeliYyN7runz3eOOsh3tmZ8A6P5MSpHIjVjSx3pUJcobFTKNyHGn/zGS4JFWuhSSb7QmNwmqK9w==", - "requires": { - "tslib": "^2.0.0" - } - }, "nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -23136,11 +23094,6 @@ "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", diff --git a/package.json b/package.json index f8ec32e..7fe9fea 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,10 @@ "@angular/platform-server": "^15.1.0", "@angular/router": "^15.1.0", "@nguniversal/express-engine": "^15.1.0", - "@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" }, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d3d64a8..061c24f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import {SeoService} from "./services/seo.service"; @Component({ selector: 'app-root', @@ -7,4 +8,6 @@ import { Component } from '@angular/core'; }) export class AppComponent { + public constructor(private seo: SeoService) {} + } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8fb016a..633258f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -22,6 +22,11 @@ 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"; +import {MatSnackBarModule} from "@angular/material/snack-bar"; +import { TimestampComponent } from './components/timestamp/timestamp.component'; +import { CarrierPipe } from './pipes/carrier.pipe'; +import { ExperiencePipe } from './pipes/experience.pipe'; +import { FooterComponent } from './components/footer/footer.component'; @NgModule({ declarations: [ @@ -38,7 +43,11 @@ import {ReactiveFormsModule} from "@angular/forms"; FrameworksPipe, SkillsPipe, ContactComponent, - AboutComponent + AboutComponent, + TimestampComponent, + CarrierPipe, + ExperiencePipe, + FooterComponent ], imports: [ BrowserModule.withServerTransition({appId: 'serverApp'}), @@ -48,7 +57,8 @@ import {ReactiveFormsModule} from "@angular/forms"; MatButtonModule, MatTooltipModule, MatInputModule, - ReactiveFormsModule + ReactiveFormsModule, + MatSnackBarModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/components/fancy-button/fancy-button.component.scss b/src/app/components/fancy-button/fancy-button.component.scss index 37b00b6..1109770 100644 --- a/src/app/components/fancy-button/fancy-button.component.scss +++ b/src/app/components/fancy-button/fancy-button.component.scss @@ -1,4 +1,14 @@ -$text-move: hover-text-move 300ms forwards ease-out; +@keyframes hover-text { + from { + opacity: var(--opa-1); + transform: translateY(0); + } + + to { + opacity: var(--opa-2); + transform: translateY(-20px); + } +} .button { display: flex; @@ -13,42 +23,27 @@ $text-move: hover-text-move 300ms forwards ease-out; border-radius: 20px; text-decoration: none; font-size: 13px; + overflow: hidden; .text-1 { + --opa-2: 0; + --opa-1: 1; margin-top: 12px; } .text-2 { + --opa-2: 1; + --opa-1: 0; opacity: 0; } &:hover { .text-1 { - animation: hover-text 250ms forwards ease-out reverse, $text-move; + animation: hover-text 300ms forwards ease-out; } .text-2 { - animation: hover-text 300ms forwards ease-out, $text-move; + animation: hover-text 300ms forwards ease-out; } } } - -@keyframes hover-text { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -@keyframes hover-text-move { - from { - transform: translateY(0); - } - - to { - transform: translateY(-20px); - } -} diff --git a/src/app/components/footer/footer.component.html b/src/app/components/footer/footer.component.html new file mode 100644 index 0000000..e7382b1 --- /dev/null +++ b/src/app/components/footer/footer.component.html @@ -0,0 +1,11 @@ + diff --git a/src/app/components/footer/footer.component.scss b/src/app/components/footer/footer.component.scss new file mode 100644 index 0000000..58e44ae --- /dev/null +++ b/src/app/components/footer/footer.component.scss @@ -0,0 +1,39 @@ +@use "src/theme"; + +#footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30px; + margin-top: 120px; + user-select: unset; + + .footer-title { + background: theme.$gradient; + background-clip: text; + color: transparent; + font-weight: bold; + user-select: none; + } + + a { + text-decoration: none; + } + + #social-media { + display: flex; + gap: 5px; + user-select: none; + + .header-social > img { + width: 25px; + height: 25px; + } + } +} + +@media screen and (max-width: theme.$mobile-width) { + #footer *:not(.footer-title) { + display: none; + } +} diff --git a/src/app/components/footer/footer.component.ts b/src/app/components/footer/footer.component.ts new file mode 100644 index 0000000..fd0e3eb --- /dev/null +++ b/src/app/components/footer/footer.component.ts @@ -0,0 +1,20 @@ +import {Component, OnInit} from '@angular/core'; +import {BackendService} from "../../services/backend.service"; +import {Social} from "../../models/social"; + +@Component({ + selector: 'app-footer', + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'] +}) +export class FooterComponent implements OnInit { + + public socialLinks: Social[]; + + public constructor(private backend: BackendService) {} + + async ngOnInit() { + this.socialLinks = await this.backend.getSocials(); + } + +} diff --git a/src/app/components/navigation/navigation.component.html b/src/app/components/navigation/navigation.component.html index 24728b4..b321e1a 100644 --- a/src/app/components/navigation/navigation.component.html +++ b/src/app/components/navigation/navigation.component.html @@ -2,22 +2,22 @@ Leon Hoppe -