This commit is contained in:
85
src/app/features/about/about-it-section.component.ts
Normal file
85
src/app/features/about/about-it-section.component.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SectionComponent } from '../../shared/ui/section/section.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-about-it-section',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-section
|
||||
[id]="sectionId"
|
||||
eyebrow="About IT"
|
||||
title="Thoughtful engineering over novelty"
|
||||
lead="I gravitate toward choices that keep the codebase predictable, observable, and easy to evolve."
|
||||
>
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Architecture
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Lean compositions, clear data boundaries, and contracts that are
|
||||
easy to test. Small, composable modules beat monolith components.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">Front end</p>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Signals for straightforward state, typed APIs, accessibility baked
|
||||
into the UI, and styling that stays aligned with design systems.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Delivery
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Automated checks, preview builds, and incremental releases over
|
||||
big-bang launches. Measure impact; iterate with feedback.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Backend Integration
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Seamless integration with backend services, optimizing data flow, and ensuring efficient full-stack performance.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
API Contracts
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Defining clear, strongly-typed API contracts to ensure reliability, improve developer experience, and reduce integration friction.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Client Handling
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Proactive communication, understanding client needs, translating requirements into technical specifications, and ensuring timely delivery with clear expectations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</app-section>
|
||||
`
|
||||
})
|
||||
export class AboutItSectionComponent {
|
||||
readonly sectionId = 'about-it';
|
||||
}
|
||||
60
src/app/features/about/about-me-section.component.ts
Normal file
60
src/app/features/about/about-me-section.component.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SectionComponent } from '../../shared/ui/section/section.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-about-me-section',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-section
|
||||
[id]="sectionId"
|
||||
eyebrow="About me"
|
||||
title="Engineer, collaborator, perpetual learner"
|
||||
lead="I like to keep teams moving: establish a clear plan, align stakeholders, and turn good ideas into shipped, measurable outcomes."
|
||||
>
|
||||
<div class="grid gap-8 lg:grid-cols-2">
|
||||
<div class="space-y-4 text-zinc-600 dark:text-zinc-200">
|
||||
<p>
|
||||
I care about pairing good taste with solid engineering practices.
|
||||
Whether building a new product or refining an existing one, I work
|
||||
to keep the codebase tidy, the UI cohesive, and the delivery
|
||||
predictable.
|
||||
</p>
|
||||
<p>
|
||||
I thrive in cross-functional teams, translating between design,
|
||||
product, and engineering to keep efforts aligned and transparent.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Working principles
|
||||
</p>
|
||||
<ul class="mt-2 space-y-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<li>• Design for clarity, then refine for speed.</li>
|
||||
<li>• Prefer simple interfaces over clever abstractions.</li>
|
||||
<li>• Optimize for maintainability and team velocity.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">Ways I help</p>
|
||||
<ul class="mt-2 space-y-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<li>• Structuring design systems and component libraries.</li>
|
||||
<li>• Improving developer experience and tooling.</li>
|
||||
<li>• Coaching teams on pragmatic testing and reviews.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</app-section>
|
||||
`
|
||||
})
|
||||
export class AboutMeSectionComponent {
|
||||
readonly sectionId = 'about-me';
|
||||
}
|
||||
19
src/app/features/cv/cv-page.component.ts
Normal file
19
src/app/features/cv/cv-page.component.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { PageShellComponent } from '../../core/layout/page-shell.component';
|
||||
import { CvSectionComponent } from './cv-section.component';
|
||||
import { MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cv-page',
|
||||
standalone: true,
|
||||
imports: [PageShellComponent, CvSectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-page-shell [sections]="sections">
|
||||
<app-cv-section />
|
||||
</app-page-shell>
|
||||
`
|
||||
})
|
||||
export class CvPageComponent {
|
||||
readonly sections = MAIN_NAVIGATION;
|
||||
}
|
||||
80
src/app/features/cv/cv-section.component.ts
Normal file
80
src/app/features/cv/cv-section.component.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SectionComponent } from '../../shared/ui/section/section.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cv-section',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-section
|
||||
[id]="sectionId"
|
||||
eyebrow="CV"
|
||||
title="Snapshot of my experience"
|
||||
lead="Highlights of how I work and where I add value. Full CV is ready to share on request."
|
||||
>
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Experience focus
|
||||
</p>
|
||||
<ul class="mt-2 space-y-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<li>• Leading front-end builds for product teams.</li>
|
||||
<li>• Partnering with design on component systems.</li>
|
||||
<li>• Modernizing legacy code into maintainable stacks.</li>
|
||||
<li>• Coaching teams on delivery practices and reviews.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">Skills & tools</p>
|
||||
<div class="mt-3 flex flex-wrap gap-2 text-xs">
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
Angular
|
||||
</span>
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
TypeScript
|
||||
</span>
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
Node.js
|
||||
</span>
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
Tailwind
|
||||
</span>
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
Design systems
|
||||
</span>
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
Testing
|
||||
</span>
|
||||
<span class="rounded-full bg-zinc-100 px-3 py-1 text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200">
|
||||
CI/CD
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-white/60 p-4 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
How I structure work
|
||||
</p>
|
||||
<ul class="mt-2 space-y-2 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
<li>• Clarify requirements with fast prototypes.</li>
|
||||
<li>• Ship in small increments with measurable outcomes.</li>
|
||||
<li>• Automate tests and checks to keep releases calm.</li>
|
||||
<li>
|
||||
• Document interfaces and patterns for future teammates.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</app-section>
|
||||
`
|
||||
})
|
||||
export class CvSectionComponent {
|
||||
readonly sectionId = 'cv';
|
||||
}
|
||||
146
src/app/features/home/home-section.component.ts
Normal file
146
src/app/features/home/home-section.component.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SectionComponent } from '../../shared/ui/section/section.component';
|
||||
|
||||
type TimelineSide = 'left' | 'right';
|
||||
|
||||
type TimelineEntry = {
|
||||
step: string;
|
||||
phase: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
side: TimelineSide;
|
||||
pulsating?: boolean; // Make pulsating an optional boolean property
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-home-section',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-section
|
||||
[id]="sectionId"
|
||||
eyebrow="Career Journey"
|
||||
title="Building dependable software that feels effortless"
|
||||
lead="Here’s a timeline of my professional growth and key milestones, highlighting significant roles and contributions."
|
||||
>
|
||||
<div
|
||||
class="overflow-hidden rounded-3xl border border-zinc-200 bg-white/60 shadow-xl shadow-emerald-500/5 backdrop-blur-sm transition-colors duration-300 dark:border-slate-800/60 dark:bg-slate-950/40 dark:shadow-emerald-900/20"
|
||||
>
|
||||
<div class="relative px-4 py-16 md:px-12">
|
||||
<!-- Central Line -->
|
||||
<div
|
||||
class="absolute left-8 top-0 h-full w-px -translate-x-1/2 bg-gradient-to-b from-transparent via-zinc-300 to-transparent transition-colors duration-300 dark:via-slate-700 md:left-1/2"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
|
||||
<div class="flex flex-col gap-12 md:gap-20">
|
||||
@for (entry of timeline; track entry.title) {
|
||||
<div
|
||||
class="group relative grid grid-cols-[2rem_1fr] gap-8 md:grid-cols-[1fr_auto_1fr] md:items-center md:gap-0"
|
||||
>
|
||||
<!-- Left Side Content (Desktop) -->
|
||||
<div class="hidden md:block md:col-start-1 md:pr-12 md:text-right">
|
||||
@if (entry.side === 'left') {
|
||||
<div class="relative">
|
||||
<span class="mb-2 inline-block text-xs font-bold uppercase tracking-widest text-emerald-600/90 dark:text-emerald-500/80">
|
||||
{{ entry.step }} • {{ entry.phase }}
|
||||
</span>
|
||||
<h3 class="mb-2 text-xl font-semibold text-zinc-900 transition-colors group-hover:text-emerald-600 dark:text-slate-100 dark:group-hover:text-emerald-400">
|
||||
{{ entry.title }}
|
||||
</h3>
|
||||
<p class="text-base text-zinc-600 leading-relaxed dark:text-slate-400">
|
||||
{{ entry.subtitle }}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Center Marker -->
|
||||
<div class="relative col-start-1 flex items-center justify-center md:col-start-2">
|
||||
<div class="relative flex h-3 w-3 items-center justify-center">
|
||||
@if (entry.pulsating) {
|
||||
<span
|
||||
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-75"
|
||||
></span>
|
||||
}
|
||||
<div
|
||||
class="relative h-3 w-3 rounded-full bg-emerald-500 ring-4 ring-white shadow-[0_0_12px_rgba(16,185,129,0.6)] transition-all duration-300 group-hover:scale-125 dark:ring-slate-950"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Side Content (Desktop) & Mobile Content (All) -->
|
||||
<div class="col-start-2 md:col-start-3 md:pl-12">
|
||||
@if (entry.side === 'right') {
|
||||
<div class="relative">
|
||||
<span class="mb-2 inline-block text-xs font-bold uppercase tracking-widest text-emerald-600/90 dark:text-emerald-500/80">
|
||||
{{ entry.step }} • {{ entry.phase }}
|
||||
</span>
|
||||
<h3 class="mb-2 text-xl font-semibold text-zinc-900 transition-colors group-hover:text-emerald-600 dark:text-slate-100 dark:group-hover:text-emerald-400">
|
||||
{{ entry.title }}
|
||||
</h3>
|
||||
<p class="text-base text-zinc-600 leading-relaxed dark:text-slate-400">
|
||||
{{ entry.subtitle }}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
@else {
|
||||
<!-- Mobile only view for 'left' entries -->
|
||||
<div class="block md:hidden">
|
||||
<span class="mb-2 inline-block text-xs font-bold uppercase tracking-widest text-emerald-600/90 dark:text-emerald-500/80">
|
||||
{{ entry.step }} • {{ entry.phase }}
|
||||
</span>
|
||||
<h3 class="mb-2 text-xl font-semibold text-zinc-900 transition-colors group-hover:text-emerald-600 dark:text-slate-100 dark:group-hover:text-emerald-400">
|
||||
{{ entry.title }}
|
||||
</h3>
|
||||
<p class="text-base text-zinc-600 leading-relaxed dark:text-slate-400">
|
||||
{{ entry.subtitle }}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</app-section>
|
||||
`
|
||||
})
|
||||
export class HomeSectionComponent {
|
||||
readonly sectionId = 'home';
|
||||
|
||||
readonly timeline: TimelineEntry[] = [
|
||||
{
|
||||
step: '2000 - 2010',
|
||||
phase: 'COMP-2000 Ltd',
|
||||
title: 'Junior Developer',
|
||||
subtitle: 'Started my career focusing on foundational programming skills and contributing to small-scale projects.',
|
||||
side: 'left'
|
||||
},
|
||||
{
|
||||
step: '2010 - 2020',
|
||||
phase: 'COMP-2000 Ltd',
|
||||
title: 'Senior Developer',
|
||||
subtitle: 'Led development teams, implemented complex features, and mentored junior staff, specializing in backend systems.',
|
||||
side: 'right'
|
||||
},
|
||||
{
|
||||
step: '2020 - 2024',
|
||||
phase: 'COMP-2000 Ltd',
|
||||
title: 'Technical Lead',
|
||||
subtitle: 'Architected scalable solutions and managed the technical roadmap for critical product lines.',
|
||||
side: 'left'
|
||||
},
|
||||
{
|
||||
step: '2024 - Present',
|
||||
phase: 'Unija ETL Group',
|
||||
title: 'Senior Software Engineer',
|
||||
subtitle: 'Currently focused on developing robust ETL processes and data integration solutions within a dynamic financial environment.',
|
||||
side: 'right',
|
||||
pulsating: true
|
||||
}
|
||||
];
|
||||
}
|
||||
28
src/app/features/landing/landing-page.component.ts
Normal file
28
src/app/features/landing/landing-page.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { PageShellComponent } from '../../core/layout/page-shell.component';
|
||||
import { HomeSectionComponent } from '../home/home-section.component';
|
||||
import { AboutMeSectionComponent } from '../about/about-me-section.component';
|
||||
import { AboutItSectionComponent } from '../about/about-it-section.component';
|
||||
import { MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-landing-page',
|
||||
standalone: true,
|
||||
imports: [
|
||||
PageShellComponent,
|
||||
HomeSectionComponent,
|
||||
AboutMeSectionComponent,
|
||||
AboutItSectionComponent
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-page-shell [sections]="sections">
|
||||
<app-home-section />
|
||||
<app-about-me-section />
|
||||
<app-about-it-section />
|
||||
</app-page-shell>
|
||||
`
|
||||
})
|
||||
export class LandingPageComponent {
|
||||
readonly sections = MAIN_NAVIGATION;
|
||||
}
|
||||
19
src/app/features/portfolio/portfolio-page.component.ts
Normal file
19
src/app/features/portfolio/portfolio-page.component.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { PageShellComponent } from '../../core/layout/page-shell.component';
|
||||
import { PortfolioSectionComponent } from './portfolio-section.component';
|
||||
import { MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-portfolio-page',
|
||||
standalone: true,
|
||||
imports: [PageShellComponent, PortfolioSectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-page-shell [sections]="sections">
|
||||
<app-portfolio-section />
|
||||
</app-page-shell>
|
||||
`
|
||||
})
|
||||
export class PortfolioPageComponent {
|
||||
readonly sections = MAIN_NAVIGATION;
|
||||
}
|
||||
86
src/app/features/portfolio/portfolio-section.component.ts
Normal file
86
src/app/features/portfolio/portfolio-section.component.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SectionComponent } from '../../shared/ui/section/section.component';
|
||||
|
||||
type PortfolioItem = {
|
||||
title: string;
|
||||
summary: string;
|
||||
period: string;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-portfolio-section',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SectionComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-section
|
||||
[id]="sectionId"
|
||||
eyebrow="Portfolio"
|
||||
title="Representative work"
|
||||
lead="A few examples of the kind of outcomes I like to deliver. Swap in your own case studies here."
|
||||
>
|
||||
<div class="grid gap-6 md:grid-cols-2">
|
||||
@for (item of items; track item.title) {
|
||||
<article
|
||||
class="flex h-full flex-col gap-3 rounded-xl border border-zinc-200 bg-white/60 p-5 dark:border-slate-800/60 dark:bg-slate-900/40"
|
||||
>
|
||||
<div class="flex items-center justify-between text-sm text-zinc-500 dark:text-zinc-400">
|
||||
<span>{{ item.period }}</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
@for (tag of item.tags; track tag) {
|
||||
<span
|
||||
class="rounded-full bg-zinc-100 px-2 py-1 text-[11px] text-zinc-700 dark:bg-slate-800/80 dark:text-zinc-200"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-zinc-900 dark:text-zinc-50">
|
||||
{{ item.title }}
|
||||
</h3>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">
|
||||
{{ item.summary }}
|
||||
</p>
|
||||
</article>
|
||||
}
|
||||
</div>
|
||||
</app-section>
|
||||
`
|
||||
})
|
||||
export class PortfolioSectionComponent {
|
||||
readonly sectionId = 'portfolio';
|
||||
|
||||
readonly items: PortfolioItem[] = [
|
||||
{
|
||||
title: 'Design system foundation',
|
||||
period: '2024',
|
||||
summary:
|
||||
'Built a lightweight component system with clear tokens and documentation, reducing UI delivery time while keeping accessibility first.',
|
||||
tags: ['Angular', 'Tailwind', 'Design systems']
|
||||
},
|
||||
{
|
||||
title: 'Modernize a legacy SPA',
|
||||
period: '2023',
|
||||
summary:
|
||||
'Incrementally refactored a legacy front end into modular features with typed APIs, improving stability and developer velocity.',
|
||||
tags: ['Refactoring', 'TypeScript', 'DX']
|
||||
},
|
||||
{
|
||||
title: 'API-first product slice',
|
||||
period: '2022',
|
||||
summary:
|
||||
'Shipped a new product slice with clear contracts between front end and services, enabling parallel delivery across teams.',
|
||||
tags: ['Architecture', 'APIs', 'Collaboration']
|
||||
},
|
||||
{
|
||||
title: 'Performance and observability pass',
|
||||
period: '2021',
|
||||
summary:
|
||||
'Improved performance budgets, added monitoring, and set up alerting to catch regressions before they reached users.',
|
||||
tags: ['Performance', 'Monitoring', 'Quality']
|
||||
}
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user