Files
playground-frontend/src/app/pages/about/about.component.ts
T
lobo 775845b801
Build, Test & Push Frontend / quality-check (push) Failing after 35s
Build, Test & Push Frontend / docker (push) Skipped
build(angular): upgrade to angular 22, add to-be-played project
Move to Angular 22 and update all dependencies. TypeScript stays on 6.0
(7 is blocked by Angular 22). Drop the ip-address override, add
fast-uri and basic-ftp overrides for audit fixes.
Set ChangeDetectionStrategy.Eager on components, as the ng update
migration does, and turn off the new OnPush lint rule.
Add the To Be Played project to the projects page and the about page.
2026-10-12 09:48:29 +02:00

273 lines
7.2 KiB
TypeScript

import { Component, computed, signal, ChangeDetectionStrategy } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { TranslatePipe } from '@ngx-translate/core';
import {UrlConstants} from '../../constants/UrlConstants';
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',
imports: [
NgOptimizedImage,
MatCardModule,
MatChipsModule,
MatIconModule,
MatButtonModule,
MatDividerModule,
TranslatePipe,
RouterLink
],
templateUrl: './about.component.html',
changeDetection: ChangeDetectionStrategy.Eager,
styleUrl: './about.component.scss'
})
export class AboutComponent {
/** 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,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY8',
company: 'teraport',
logo: AssetsConstants.TERAPORT_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY7',
company: 'colordigital-earlier',
logo: AssetsConstants.COLORDIGITAL_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY6',
company: 'colordigital-earlier',
logo: AssetsConstants.COLORDIGITAL_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY5',
company: 'assyst',
logo: AssetsConstants.ASSYST_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY4',
company: 'assyst',
logo: AssetsConstants.ASSYST_LOGO,
highlightCount: 3
},
{
key: 'ABOUT.XP.COMPANY3',
company: 'assyst',
logo: AssetsConstants.ASSYST_LOGO,
highlightCount: 3
}
];
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.TBP',
externalLink: 'https://tbp.andreas-dahm.eu',
internalLink: 'projects',
identifier: 'to-be-played',
},
{
key: 'ABOUT.PROJECT.CLAUDE_BOX',
externalLink: '',
internalLink: 'projects',
identifier: 'claude-in-a-box',
},
{
key: 'ABOUT.PROJECT.P2',
externalLink: 'https://andreas-dahm.eu/',
internalLink: 'projects',
identifier: 'playground',
},
{
key: 'ABOUT.PROJECT.P1',
externalLink: 'https://store.steampowered.com/app/1532640/El_Mucho/',
internalLink: 'projects',
identifier: 'elmucho',
},
{
key: 'ABOUT.PROJECT.P0',
externalLink: 'https://itch.io/c/6628860/lobos-collection',
internalLink: 'projects',
identifier: 'gamejams',
},
{
key: 'ABOUT.PROJECT.DIPLOMA',
externalLink: '',
internalLink: 'projects',
identifier: 'diploma',
},
{
key: 'ABOUT.PROJECT.TRIBBLE',
externalLink: '',
internalLink: 'projects',
identifier: 'tribble-the-homeserver',
}
];
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[] = [
{
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'
]
},
{
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'
]
},
{
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'
]
},
{
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'
]
}
];
readonly visibleExperience = computed(() => {
if (this.showEarlierExperience()) {
return [...this.recentExperience, ...this.earlierExperience];
}
return this.recentExperience;
});
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;
protected readonly SharedFunctions = SharedFunctions;
}