-
- @for (item of [
- { title: 'Explore the Docs', link: 'https://angular.dev' },
- { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
- { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
- { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
- { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
- ]; track item.title) {
-
- {{ item.title }}
-
-
- }
-
-
+
+
+
Axel Herrmann
+ Software Engineer
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {{ dataService.languagePack.education }}
+
+
+
+
+
+
+
+
+
+ {{ educationItem.content }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index e69de29..9ceb11e 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -0,0 +1,56 @@
+#main-background {
+
+}
+#main-heading {
+ font-size: 13vh;
+ color: #ffffff;
+ letter-spacing: 3vh;
+ font-weight: 10;
+ font-family: Copperplate, serif;
+ margin-bottom: 3vh;
+}
+#main-subheading {
+ @extend #main-heading;
+ font-size: 3vh;
+ letter-spacing: 1vh;
+ font-style: italic;
+ transform: translateX(-20vw);
+}
+
+.dot {
+ height: 25px;
+ width: 25px;
+ background-color: #ffffff;
+ border-radius: 50%;
+ border: 3px solid #a1a1a1;
+ display: inline-block;
+}
+
+:host ::ng-deep {
+ .p-card-content {
+ padding: 0;
+ }
+
+ .flag-icon {
+ width: 100%;
+ height: 100%;
+ }
+
+ // style parent of flag-active
+ a:has(> span.flag-active) {
+ border: 2px solid #ffffff;
+ }
+
+ span.flag-icon:not(.flag-active) {
+ opacity: 0.5;
+ }
+
+ span.flag-icon:not(.flag-active):hover {
+ opacity: 1;
+ }
+
+ button.p-speeddial-button:focus {
+ //box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px #9dc1fb, 0 1px 2px 0 black; // default setting
+ box-shadow: 0 0 0 2px #ffffff;
+ }
+}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index d708535..8ed62e9 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,13 +1,56 @@
-import { Component } from '@angular/core';
-import { CommonModule } from '@angular/common';
+import {Component, ElementRef, ViewChild} from '@angular/core';
+import {CommonModule, NgOptimizedImage} from '@angular/common';
+import {TimelineModule} from 'primeng/timeline';
+import {CardModule} from "primeng/card";
+import {ButtonModule} from "primeng/button";
+import {BadgeModule} from "primeng/badge";
+import gsap from 'gsap';
+import {ScrollTrigger} from "gsap/ScrollTrigger";
+import {DataService} from "./data/data.service";
+import {SpeedDialModule} from "primeng/speeddial";
+import "flag-icons/css/flag-icons.min.css";
+
+gsap.registerPlugin(ScrollTrigger);
@Component({
selector: 'app-root',
standalone: true,
- imports: [CommonModule],
+ imports: [CommonModule, NgOptimizedImage, TimelineModule, CardModule, ButtonModule, BadgeModule, SpeedDialModule],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
- title = 'CV';
+
+ @ViewChild("background") background: ElementRef;
+ @ViewChild("education_card") educationCard: ElementRef;
+ @ViewChild("lang_selector") langSelector: ElementRef;
+
+ dataService: DataService;
+
+ constructor(dataService: DataService) {
+ this.dataService = dataService;
+ }
+
+ ngAfterViewInit(): void {
+ const tl: gsap.core.Timeline = gsap.timeline({
+ scrollTrigger: {
+ start: 200,
+ trigger: this.educationCard.nativeElement,
+ end: "+=1000",
+ // markers: true, // TODO remove
+ scrub: true,
+ }
+ });
+ tl
+ .to(this.background.nativeElement, {
+ duration: 50,
+ filter: "blur(2px)"
+ })
+ .from(this.langSelector.nativeElement, {
+ duration: 20,
+ // scaleX: 0,
+ // x: 32,
+ opacity: 0
+ });
+ }
}
diff --git a/src/app/data/data.service.ts b/src/app/data/data.service.ts
new file mode 100644
index 0000000..1208917
--- /dev/null
+++ b/src/app/data/data.service.ts
@@ -0,0 +1,82 @@
+import {Injectable} from '@angular/core';
+import * as educationJson from '../../data/education.json';
+import * as languagesJson from '../../data/languages.json';
+import {EducationItem, LanguagePack} from "./model";
+import {MenuItem} from "primeng/api";
+
+@Injectable({
+ providedIn: 'root'
+})
+export class DataService {
+
+ defaultLang: string = languagesJson.defaultLanguage;
+ loadedLanguages: string[] = languagesJson.languages;
+ languagesMenuItems: MenuItem[] = [];
+
+ lang: string;
+ languagePack: LanguagePack;
+ education: EducationItem[];
+
+ constructor() {
+ this.determineLanguage();
+ }
+
+ loadData(): void {
+ console.log("Loading data for lang", this.lang);
+ // @ts-ignore
+ this.education = educationJson[this.lang];
+ // @ts-ignore
+ this.languagePack = languagesJson[this.lang];
+ this.fillLanguageButton();
+ }
+
+ determineLanguage(): void {
+ let lang = localStorage.getItem("lang");
+ if (lang) {
+ this.setLang(lang);
+ return;
+ }
+ lang = navigator.language;
+ if (lang) {
+ lang = lang.trim().split("-")[0];
+ this.setLang(lang);
+ return;
+ }
+ this.setLang(this.defaultLang);
+ }
+
+ fillLanguageButton(): void {
+ this.languagesMenuItems = [];
+ for (const lang of this.loadedLanguages) {
+ // @ts-ignore
+ const langPack: LanguagePack = languagesJson[lang];
+ let flagActive: string = "";
+ if (lang === this.lang) {
+ flagActive = " flag-active";
+ }
+ this.languagesMenuItems.push({
+ id: langPack.id,
+ icon: `fi fis fi-${langPack.isoAlpha2} flag-icon${flagActive}`,
+ command: () => this.setLang(lang)
+ })
+ }
+
+ // put active language first
+ this.languagesMenuItems.sort((a: MenuItem, b: MenuItem): number => {
+ if (a.id === this.lang) {
+ return -1;
+ }
+ return 1;
+ })
+ }
+
+ /**
+ * Sets new active language, writes it to local storage and (re-)loads data for language
+ * @param newLang
+ */
+ setLang(newLang: string): void {
+ this.lang = newLang;
+ localStorage.setItem("lang", this.lang);
+ this.loadData();
+ }
+}
diff --git a/src/app/data/model.ts b/src/app/data/model.ts
new file mode 100644
index 0000000..ba9f3e3
--- /dev/null
+++ b/src/app/data/model.ts
@@ -0,0 +1,15 @@
+export interface EducationItem {
+ heading: string;
+ subheading: string;
+ date: string;
+ content: string;
+ bulletPoints?: string[];
+}
+
+export interface LanguagePack {
+ id: string;
+ name: string;
+ isoAlpha2: string;
+ education: string;
+ experience: string;
+}
diff --git a/src/assets/avatar.png b/src/assets/avatar.png
new file mode 100644
index 0000000..fdb35e8
Binary files /dev/null and b/src/assets/avatar.png differ
diff --git a/src/data/education.json b/src/data/education.json
new file mode 100644
index 0000000..a2e2343
--- /dev/null
+++ b/src/data/education.json
@@ -0,0 +1,54 @@
+{
+ "de": [
+ {
+ "heading": "Grundschule",
+ "subheading": "Berkenschule Holzgerlingen",
+ "date": "09/2008-07/2012",
+ "content": "In der Grundschule hatte ich zwar noch keine Berührungen mit Informatik oder technischen Themen, doch Mathe hat sich schenll als mein Lieblingsfach herausgestellt."
+ },
+ {
+ "heading": "Allgemeine Hochschulreife",
+ "subheading": "Schönbuch-Gymnasium Holzgerlingen",
+ "date": "09/2012-07/2020",
+ "bulletPoints": [
+ "Abschlussnote: 1,4"
+ ],
+ "content": "Neben der allgemeinen Bildung habe ich hier meine ersten Erfahrungen in der Informatik sammeln können. Ab der Oberstufe habe ich Informatikunterricht besucht."
+ },
+ {
+ "heading": "Software Engineering B.Sc.",
+ "subheading": "Universität Stuttgart",
+ "date": "10/2020-04/2023",
+ "bulletPoints": [
+ "Voraussichtliche Abschlussnote: 2.0"
+ ],
+ "content": "In Abgrenzung zum verwandten Studiengang Informatik konnte ich hier einige praktische Erfahrungen mehr sammeln. Diese kleinen Programmierprojekte konnten mich im Studium bisher am meisten begeistern."
+ }
+ ],
+ "en": [
+ {
+ "heading": "Elementary School",
+ "subheading": "Berkenschule Holzgerlingen",
+ "date": "09/2008-07/2012",
+ "content": "Although I had no contact with computer science or technical subjects in elementary school, math quickly turned out to be my favorite subject."
+ },
+ {
+ "heading": "Abitur",
+ "subheading": "Schönbuch-Gymnasium Holzgerlingen",
+ "date": "09/2012-07/2020",
+ "bulletPoints": [
+ "Final Grade: 1,4"
+ ],
+ "content": "In addition to general education, I was able to gain my first experience in computer science here. I attended computer science classes from 11th year onwards."
+ },
+ {
+ "heading": "Software Engineering B.Sc.",
+ "subheading": "University of Stuttgart",
+ "date": "10/2020-04/2023",
+ "bulletPoints": [
+ "Expected Final Grade: 2.0"
+ ],
+ "content": "In contrast to the related computer science course, I was able to gain some more practical experience here. These small programming projects have inspired me the most during my studies so far."
+ }
+ ]
+}
diff --git a/src/data/languages.json b/src/data/languages.json
new file mode 100644
index 0000000..a899cea
--- /dev/null
+++ b/src/data/languages.json
@@ -0,0 +1,18 @@
+{
+ "languages": ["de", "en"],
+ "defaultLanguage": "en",
+ "de": {
+ "id": "de",
+ "name": "Deutsch",
+ "isoAlpha2": "de",
+ "education": "Bildungsweg",
+ "experience": "Erfahrung"
+ },
+ "en": {
+ "id": "en",
+ "name": "English",
+ "isoAlpha2": "us",
+ "education": "Education",
+ "experience": "Experience"
+ }
+}
diff --git a/src/styles.scss b/src/styles.scss
index 3b091de..a53b9b5 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,36 +1,37 @@
+@import "primeng/resources/themes/lara-light-blue/theme.css";
+@import "primeng/resources/primeng.css";
+@import "primeicons/primeicons.css";
-// Custom Theming for Angular Material
-// For more information: https://material.angular.io/guide/theming
-@use '@angular/material' as mat;
-// Plus imports for other components in your app.
-
-// Include the common styles for Angular Material. We include this here so that you only
-// have to load a single css file for Angular Material in your app.
-// Be sure that you only ever include this mixin once!
-@include mat.core();
+/* You can add global styles to this file, and also import other style files */
-// Define the palettes for your theme using the Material Design palettes available in palette.scss
-// (imported above). For each palette, you can optionally specify a default, lighter, and darker
-// hue. Available color palettes: https://material.io/design/color/
-$CV-primary: mat.define-palette(mat.$indigo-palette);
-$CV-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
+html, body { height: 100% !important }
+body {
+ margin: 0;
+ font-family: Roboto, "Helvetica Neue", sans-serif;
+ overflow-x: hidden;
+}
-// The warn palette is optional (defaults to red).
-$CV-warn: mat.define-palette(mat.$red-palette);
+// TODO remove once style theme is properly done: Anleitung dafür: https://angularindepth.com/posts/1320/custom-theme-for-angular-material-components-series-part-1-create-a-theme
+:root {
+ --color-grey-dark-1: #363C43;
+ --color-grey-dark-2: #747474;
+ --color-grey-medium-1: #CACACA;
+ --color-white-darker: #E9E8E7;
-// Create the theme object. A theme consists of configurations for individual
-// theming systems such as "color" or "typography".
-$CV-theme: mat.define-light-theme((
- color: (
- primary: $CV-primary,
- accent: $CV-accent,
- warn: $CV-warn,
- )
-));
+ --text-font-size: 2.1vh;
+ --text-color: var(--color-grey-dark-2);
+ --heading-color: var(--color-grey-dark-1);
-// Include theme styles for core and each component used in your app.
-// Alternatively, you can import and @include the theme mixins for each component
-// that you are using.
-@include mat.all-component-themes($CV-theme);
+ .text-color-grey-dark-1 { color: var(--color-grey-dark-1); }
+ .text-color-grey-dark-2 { color: var(--color-grey-dark-2); }
+ .text-color-grey-medium-1 { color: var(--color-grey-medium-1); }
+ .color-white-darker { color: var(--color-white-darker); }
+}
-/* You can add global styles to this file, and also import other style files */
+.text {
+ color: var(--color-grey-dark-2);
+ font-size: var(--text-font-size);
+ font-weight: 50;
+ text-align: justify;
+ line-height: 3.2vh;
+}
diff --git a/tsconfig.json b/tsconfig.json
index 775e06d..1aa5908 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,6 +19,7 @@
"module": "ES2022",
"useDefineForClassFields": false,
"strictPropertyInitialization": false,
+ "resolveJsonModule": true,
"lib": [
"ES2022",
"dom"