pimped the site a little but and fixed impress

This commit is contained in:
Andreas Dahm committed 2026-08-26 10:48:39 +02:00
1 parent f543a5e2f3
commit 888fb6289d
14 files changed
+1502 -1162

No files matched your search

+163 -64
View File
@@ -1,4 +1,4 @@
import { Component} from '@angular/core';
import { Component, computed, signal } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatChipsModule } from '@angular/material/chips';
@@ -11,6 +11,18 @@ import {AssetsConstants} from '../../constants/AssetsConstants';
import {RouterLink} from '@angular/router';
import {SharedFunctions} from '../../shared/SharedFunctions';
/** One role. Consecutive entries sharing a `company` render as a single employer block. */
interface ExperienceEntry {
key: string;
company: string;
logo: string;
highlightCount: number;
}
interface SkillGroup {
titleKey: string;
skillKeys: string[];
}
@Component({
selector: 'app-about',
@@ -29,51 +41,83 @@ import {SharedFunctions} from '../../shared/SharedFunctions';
})
export class AboutComponent {
xpKeys = [
/** Roles older than the current decade are hidden until the reader asks for them. */
readonly showEarlierExperience = signal(false);
readonly showEarlierEducation = signal(false);
readonly recentExperience: ExperienceEntry[] = [
{
key: 'ABOUT.XP.COMPANY9',
company: 'colordigital',
logo: AssetsConstants.COLORDIGITAL_LOGO,
no_highlights: false
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY8',
logo: AssetsConstants.TERAPORT_LOGO
company: 'teraport',
logo: AssetsConstants.TERAPORT_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY7',
logo: AssetsConstants.COLORDIGITAL_LOGO
company: 'colordigital-earlier',
logo: AssetsConstants.COLORDIGITAL_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY6',
logo: AssetsConstants.COLORDIGITAL_LOGO
company: 'colordigital-earlier',
logo: AssetsConstants.COLORDIGITAL_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY5',
logo: AssetsConstants.ASSYST_LOGO
company: 'assyst',
logo: AssetsConstants.ASSYST_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY4',
logo: AssetsConstants.ASSYST_LOGO
company: 'assyst',
logo: AssetsConstants.ASSYST_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY3',
logo: AssetsConstants.ASSYST_LOGO
},
{
key: 'ABOUT.XP.COMPANY2',
logo: AssetsConstants.TH_BINGEN_LOGO
},
{
key: 'ABOUT.XP.COMPANY1',
logo: AssetsConstants.TH_BINGEN_LOGO
},
{
key: 'ABOUT.XP.COMPANY0',
logo: AssetsConstants.CHAMAELEON_LOGO
company: 'assyst',
logo: AssetsConstants.ASSYST_LOGO,
highlightCount: 3
}
];
projectKeys = [
readonly earlierExperience: ExperienceEntry[] = [
{
key: 'ABOUT.XP.COMPANY2',
company: 'th-bingen',
logo: AssetsConstants.TH_BINGEN_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY1',
company: 'th-bingen',
logo: AssetsConstants.TH_BINGEN_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY0',
company: 'chamaeleon',
logo: AssetsConstants.CHAMAELEON_LOGO,
highlightCount: 3
}
];
readonly projectKeys = [
{
key: 'ABOUT.PROJECT.CLAUDE_BOX',
externalLink: '',
internalLink: 'projects',
identifier: 'claude-in-a-box',
},
{
key: 'ABOUT.PROJECT.P2',
externalLink: 'https://andreas-dahm.eu/',
@@ -106,59 +150,114 @@ export class AboutComponent {
}
];
educationKeys = [
readonly recentEducationKeys = [
'ABOUT.EDUCATION.E6',
'ABOUT.EDUCATION.E5',
'ABOUT.EDUCATION.E4'
];
readonly earlierEducationKeys = [
'ABOUT.EDUCATION.E3',
'ABOUT.EDUCATION.E2',
'ABOUT.EDUCATION.E1',
'ABOUT.EDUCATION.E0'
];
/**
* Heading and chips travel together so the two can no longer drift apart —
* they used to be declared in separate places and had ended up mismatched.
*/
readonly skillGroups: SkillGroup[] = [
{
key: 'ABOUT.EDUCATION.E6'
titleKey: 'ABOUT.SECTION.LANGUAGES',
skillKeys: [
'ABOUT.SKILLS.JAVA',
'ABOUT.SKILLS.SPRING',
'ABOUT.SKILLS.TYPESCRIPT',
'ABOUT.SKILLS.ANGULAR',
'ABOUT.SKILLS.CSHARP',
'ABOUT.SKILLS.PYTHON',
'ABOUT.SKILLS.VUE',
'ABOUT.SKILLS.REACT'
]
},
{
key: 'ABOUT.EDUCATION.E5'
titleKey: 'ABOUT.SECTION.ARCH_CLOUD',
skillKeys: [
'ABOUT.SKILLS.ARCH_MICROSERVICES',
'ABOUT.SKILLS.ARCH_CLOUD',
'ABOUT.TOOLS.DOCKER',
'ABOUT.TOOLS.K8S',
'ABOUT.TOOLS.POSTGRES',
'ABOUT.TOOLS.MONGO',
'ABOUT.TOOLS.GITLAB',
'ABOUT.TOOLS.JENKINS'
]
},
{
key: 'ABOUT.EDUCATION.E4'
titleKey: 'ABOUT.SECTION.SIM_ALGO',
skillKeys: [
'ABOUT.SKILLS.ENG_ALGO',
'ABOUT.SKILLS.ENG_SIM',
'ABOUT.SKILLS.ENG_GPU',
'ABOUT.SKILLS.ENG_PERF',
'ABOUT.SKILLS.UNITY'
]
},
{
key: 'ABOUT.EDUCATION.E3'
},
{
key: 'ABOUT.EDUCATION.E2'
},
{
key: 'ABOUT.EDUCATION.E1'
},
{
key: 'ABOUT.EDUCATION.E0'
titleKey: 'ABOUT.SECTION.AI_ENG',
skillKeys: [
'ABOUT.SKILLS.AI_AGENTS',
'ABOUT.SKILLS.AI_MCP',
'ABOUT.SKILLS.AI_CONTEXT',
'ABOUT.SKILLS.AI_HARNESS',
'ABOUT.SKILLS.AI_GATES'
]
}
]
skillsCore = [
'ABOUT.SKILLS.JAVA',
'ABOUT.SKILLS.SPRING',
'ABOUT.SKILLS.ANGULAR',
'ABOUT.SKILLS.VUE',
'ABOUT.SKILLS.REACT',
'ABOUT.SKILLS.TYPESCRIPT',
'ABOUT.SKILLS.CSHARP',
'ABOUT.SKILLS.PYTHON'
];
skillsArchitecture = [
'ABOUT.SKILLS.ARCH_MICROSERVICES',
'ABOUT.SKILLS.ARCH_CLOUD',
'ABOUT.TOOLS.DOCKER',
'ABOUT.TOOLS.K8S',
'ABOUT.TOOLS.JENKINS',
'ABOUT.TOOLS.POSTGRES',
'ABOUT.TOOLS.MONGO',
'ABOUT.TOOLS.GITLAB'
];
readonly visibleExperience = computed(() => {
if (this.showEarlierExperience()) {
return [...this.recentExperience, ...this.earlierExperience];
}
return this.recentExperience;
});
skillsEngineering = [
'ABOUT.SKILLS.ENG_ALGO',
'ABOUT.SKILLS.ENG_SIM',
'ABOUT.SKILLS.ENG_GPU',
'ABOUT.SKILLS.UNITY',
'ABOUT.SKILLS.ENG_PERF'
];
readonly visibleEducationKeys = computed(() => {
if (this.showEarlierEducation()) {
return [...this.recentEducationKeys, ...this.earlierEducationKeys];
}
return this.recentEducationKeys;
});
toggleEarlierExperience(): void {
this.showEarlierExperience.update(shown => !shown);
}
toggleEarlierEducation(): void {
this.showEarlierEducation.update(shown => !shown);
}
/** The employer header is drawn once per run of consecutive roles at the same company. */
isFirstRoleAtCompany(index: number): boolean {
if (index === 0) {
return true;
}
const entries = this.visibleExperience();
return entries[index - 1].company !== entries[index].company;
}
isLastRoleAtCompany(index: number): boolean {
const entries = this.visibleExperience();
if (index === entries.length - 1) {
return true;
}
return entries[index + 1].company !== entries[index].company;
}
highlightKeys(entry: ExperienceEntry): string[] {
return Array.from({ length: entry.highlightCount }, (_, i) => `${entry.key}.HIGHLIGHTS.P${i + 1}`);
}
protected readonly UrlConstants = UrlConstants;
protected readonly AssetsConstants = AssetsConstants;