Started with about page and my cv

This commit is contained in:
2025-11-09 12:21:34 +01:00
parent b80d70d4df
commit 5969e66872
36 changed files with 637 additions and 169 deletions

View File

@@ -0,0 +1,110 @@
<section class="about">
<mat-card class="hero">
<div class="photo">
<img
[ngSrc]="AssetsConstants.ME"
width="320" height="400"
alt="{{ 'ABOUT.ALT.PROFILE' | translate }}"
draggable="false"
oncontextmenu="return false;"
priority />
</div>
<div class="intro">
<h1>{{ 'ABOUT.HELLO' | translate }}</h1>
<p class="lead">
{{ 'ABOUT.LEAD' | translate }}
</p>
<div class="meta">
<div class="row">
<mat-icon aria-hidden="true">work</mat-icon>
<span>{{ 'ABOUT.ROLE' | translate }}</span>
</div>
<div class="row">
<mat-icon aria-hidden="true">location_on</mat-icon>
<span>{{ 'ABOUT.LOCATION' | translate }}</span>
</div>
<div class="row">
<mat-icon aria-hidden="true">email</mat-icon>
<a href="" (click)="openMail($event)">
{{ 'ABOUT.CONTACT_ME' | translate }}
</a>
</div>
<div class="row">
<mat-icon aria-hidden="true">link</mat-icon>
<a href="{{UrlConstants.GIT_HUB}}" target="_blank" rel="noopener">GitHub</a>
<span>·</span>
<a href="{{UrlConstants.LINKED_IN}}" target="_blank" rel="noopener">LinkedIn</a>
</div>
</div>
<div class="actions">
<a mat-flat-button color="primary" [href]="cvHref" target="_blank" rel="noopener">
<mat-icon>picture_as_pdf</mat-icon>
{{ 'ABOUT.DOWNLOAD_CV' | translate }}
</a>
<a mat-stroked-button routerLink="/projects">
<mat-icon>work_outline</mat-icon>
{{ 'ABOUT.VIEW_PROJECTS' | translate }}
</a>
</div>
</div>
</mat-card>
<mat-card class="skills">
<h2>{{ 'ABOUT.SECTION.SKILLS' | translate }}</h2>
<div class="chip-groups">
<div>
<h3>{{ 'ABOUT.SECTION.PRIMARY' | translate }}</h3>
<mat-chip-set aria-label="Primary skills">
@for (s of primarySkills; track primarySkills) {
<mat-chip >{{ s | translate }}</mat-chip>
}
</mat-chip-set>
</div>
<div>
<h3>{{ 'ABOUT.SECTION.TOOLSET' | translate }}</h3>
<mat-chip-set aria-label="Toolset">
@for (t of toolset; track toolset) {
<mat-chip>{{ t | translate }}</mat-chip>
}
</mat-chip-set>
</div>
</div>
</mat-card>
<mat-card class="experience">
<h2>{{ 'ABOUT.SECTION.EXPERIENCE' | translate }}</h2>
<div class="xp-list">
<div class="xp-item">
<div class="xp-head">
<strong>{{ 'ABOUT.XP.T1.ROLE' | translate }}</strong>
<span class="time">{{ 'ABOUT.XP.T1.TIME' | translate }}</span>
</div>
<div class="xp-sub">{{ 'ABOUT.XP.T1.COMPANY' | translate }}</div>
<ul>
<li>{{ 'ABOUT.XP.T1.P1' | translate }}</li>
<li>{{ 'ABOUT.XP.T1.P2' | translate }}</li>
<li>{{ 'ABOUT.XP.T1.P3' | translate }}</li>
</ul>
</div>
<mat-divider></mat-divider>
<div class="xp-item">
<div class="xp-head">
<strong>{{ 'ABOUT.XP.T2.ROLE' | translate }}</strong>
<span class="time">{{ 'ABOUT.XP.T2.TIME' | translate }}</span>
</div>
<div class="xp-sub">{{ 'ABOUT.XP.T2.COMPANY' | translate }}</div>
<ul>
<li>{{ 'ABOUT.XP.T2.P1' | translate }}</li>
<li>{{ 'ABOUT.XP.T2.P2' | translate }}</li>
</ul>
</div>
</div>
</mat-card>
</section>

View File

@@ -0,0 +1,86 @@
.about {
display: grid;
gap: 1rem;
}
/* Hero block: Photo + Intro */
.hero {
display: grid;
grid-template-columns: 240px 1fr;
gap: 1.25rem;
border-radius: 16px;
background: var(--app-card-background);
.photo {
align-items:flex-start; justify-content:center;
img {
display:block;
width: 100%; height: auto;
max-width: 220px;
border-radius: 12px;
box-shadow: 0 6px 24px rgba(0,0,0,.25);
object-fit: cover;
}
}
.intro {
display:flex; flex-direction:column; gap:.5rem;
h1 { margin-top: .25rem }
.lead { opacity:.9; margin: .25rem 0 0.5rem; }
.meta {
display:flex; flex-direction:column; gap:.25rem;
.row {
display:flex; align-items:center; gap:.4rem;
a { color: inherit; }
}
}
.actions {
display:flex; gap:.5rem; flex-wrap:wrap; margin-top:.5rem; margin-bottom: .25rem;
.mat-icon { margin-right:.25rem; }
}
}
}
/* Skills block */
.skills {
border-radius: 16px;
background: var(--app-card-background);
h2 { margin-top: .25rem; margin-left: .25rem; }
.chip-groups {
margin-left: .25rem;
display:grid; gap:1rem;
grid-template-columns: 1fr 1fr;
h3 { margin: .2rem 0 .4rem; font-size: .95rem; opacity:.85; }
mat-chip-set {
display:flex; flex-wrap:wrap; gap:.4rem;
}
}
}
/* Experience block */
.experience {
border-radius: 16px;
background: var(--app-card-background);
h2 { margin-top: .25rem;margin-left: .25rem; }
.xp-list {
margin-left: .25rem;
display: grid; gap: .75rem;
}
.xp-item {
.xp-head {
display:flex; align-items:baseline; gap:.5rem;
.time { opacity:.75; font-size:.9rem; }
}
.xp-sub { opacity:.9; margin-bottom:.25rem; }
ul { margin: .25rem 0 .5rem 1.15rem; }
}
}
/* Responsive */
@media (max-width: 900px) {
.hero { grid-template-columns: 1fr; }
.hero .photo { justify-content: flex-start; }
.skills .chip-groups { grid-template-columns: 1fr; }
}

View File

@@ -0,0 +1,60 @@
import { Component, inject } from '@angular/core';
import { CommonModule, 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 { TranslateModule } from '@ngx-translate/core';
import {RouterLink} from '@angular/router';
import {UrlConstants} from '../../constants/UrlConstants';
import {AssetsConstants} from '../../constants/AssetsConstants';
@Component({
selector: 'app-about',
standalone: true,
imports: [
CommonModule, NgOptimizedImage,
MatCardModule, MatChipsModule, MatIconModule, MatButtonModule, MatDividerModule,
TranslateModule, RouterLink
],
templateUrl: './about.component.html',
styleUrl: './about.component.scss'
})
export class AboutComponent {
cvHref = 'assets/cv/andreas-dahm-cv.pdf';
primarySkills = [
'ABOUT.SKILLS.JAVA',
'ABOUT.SKILLS.SPRING',
'ABOUT.SKILLS.ANGULAR',
'ABOUT.SKILLS.DOCKER',
'ABOUT.SKILLS.UNITY',
'ABOUT.SKILLS.PYTHON',
'ABOUT.SKILLS.CSHARP',
'ABOUT.SKILLS.TYPESCRIPT'
];
toolset = [
'ABOUT.TOOLS.GIT',
'ABOUT.TOOLS.GITHUB',
'ABOUT.TOOLS.JENKINS',
'ABOUT.TOOLS.K8S',
'ABOUT.TOOLS.POSTGRES',
'ABOUT.TOOLS.MONGO',
'ABOUT.TOOLS.GRAFANA',
];
openMail(event: Event) {
event.preventDefault();
const user = 'andreas.dahm';
const domain = 'gmail.com';
globalThis.location.href = `mailto:${user}@${domain}`;
}
protected readonly UrlConstants = UrlConstants;
protected readonly AssetsConstants = AssetsConstants;
}

View File

@@ -0,0 +1 @@
<p>project-details works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-project-details',
imports: [],
templateUrl: './project-details.component.html',
styleUrl: './project-details.component.scss',
})
export class ProjectDetailsComponent {
}

View File

@@ -0,0 +1 @@
<p>projects works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-projects',
imports: [],
templateUrl: './projects.component.html',
styleUrl: './projects.component.scss',
})
export class ProjectsComponent {
}