Redesign portfolio around systems case studies
This commit is contained in:
25
src/app/features/cv/cv-page.component.spec.ts
Normal file
25
src/app/features/cv/cv-page.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { CvPageComponent } from './cv-page.component';
|
||||
|
||||
describe('CvPageComponent', () => {
|
||||
it('renders a complete, contactable CV with curated learning', async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CvPageComponent],
|
||||
providers: [provideRouter([])]
|
||||
}).compileComponents();
|
||||
|
||||
const fixture = TestBed.createComponent(CvPageComponent);
|
||||
fixture.detectChanges();
|
||||
const page = fixture.nativeElement as HTMLElement;
|
||||
|
||||
expect(page.querySelector('h1')?.textContent).toContain('Senior full-stack engineer');
|
||||
expect(page.textContent).toContain('Senior Software Engineer');
|
||||
expect(page.textContent).toContain('Product Owner');
|
||||
expect(page.textContent).toContain('Software Developer');
|
||||
expect(page.textContent).toContain('Junior Software Developer');
|
||||
expect(page.querySelectorAll('[data-testid="course"]').length).toBe(8);
|
||||
expect(page.querySelector('a[href="mailto:amar@dzanan.net"]')).toBeTruthy();
|
||||
expect(page.querySelector('a[href="https://www.linkedin.com/in/amardzanan/"]')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
93
src/app/features/cv/cv-page.component.ts
Executable file → Normal file
93
src/app/features/cv/cv-page.component.ts
Executable file → Normal file
@@ -1,19 +1,104 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { PageShellComponent } from '../../core/layout/page-shell.component';
|
||||
import { CvSectionComponent } from './cv-section.component';
|
||||
import { SeoService } from '../../core/services/seo.service';
|
||||
import { MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cv-page',
|
||||
standalone: true,
|
||||
imports: [PageShellComponent, CvSectionComponent],
|
||||
imports: [PageShellComponent, RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-page-shell [sections]="sections">
|
||||
<app-cv-section />
|
||||
<header class="cv-hero shell">
|
||||
<nav class="breadcrumb mono" aria-label="Breadcrumb"><a routerLink="/">Home</a><span>/</span><span>CV</span></nav>
|
||||
<div class="cv-heading">
|
||||
<div><p class="eyebrow mono">Curriculum vitae / Amar Džanan</p><h1>Senior full-stack engineer with product judgment.</h1></div>
|
||||
<div class="cv-contact">
|
||||
<p>Sarajevo, Bosnia and Herzegovina<br>Available for senior engineering work across Europe.</p>
|
||||
<a href="mailto:amar@dzanan.net">amar@dzanan.net ↗</a>
|
||||
<a href="https://www.linkedin.com/in/amardzanan/">LinkedIn ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="cv-section shell" aria-labelledby="summary-title">
|
||||
<p class="case-index mono">01 / Summary</p>
|
||||
<div><h2 id="summary-title">Profile</h2><p class="large-copy">I help teams understand and improve mature software. My work spans product decisions, Angular interfaces, .NET services, data, integrations, delivery, and production diagnostics—with recent focus on safe AI access to business systems.</p></div>
|
||||
</section>
|
||||
|
||||
<section class="cv-section shell" aria-labelledby="cv-experience-title">
|
||||
<p class="case-index mono">02 / Career</p>
|
||||
<div><h2 id="cv-experience-title">Experience</h2>
|
||||
<div class="cv-roles">
|
||||
@for (role of roles; track role.period + role.title) {
|
||||
<article><div class="cv-role-head"><time class="mono">{{ role.period }}</time><div><h3>{{ role.title }}</h3><p>{{ role.company }}</p></div></div><p>{{ role.description }}</p></article>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="cv-section shell" aria-labelledby="skills-title">
|
||||
<p class="case-index mono">03 / Practice</p>
|
||||
<div><h2 id="skills-title">Capabilities & tools</h2>
|
||||
<div class="cv-skill-grid">
|
||||
@for (group of skillGroups; track group.title) { <article><h3>{{ group.title }}</h3><p>{{ group.items }}</p></article> }
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="cv-section shell" aria-labelledby="learning-title">
|
||||
<p class="case-index mono">04 / Learning</p>
|
||||
<div><h2 id="learning-title">Selected courses</h2>
|
||||
<div class="course-list">
|
||||
@for (course of courses; track course.title) {
|
||||
<article data-testid="course"><time class="mono">{{ course.period }}</time><h3>{{ course.title }}</h3><p>{{ course.provider }}</p></article>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="contact-block shell" aria-labelledby="cv-contact-title">
|
||||
<p class="eyebrow mono">Contact / Direct</p>
|
||||
<h2 id="cv-contact-title">Let’s talk about the system you need to move forward.</h2>
|
||||
<div class="contact-actions"><a class="button button-light" href="mailto:amar@dzanan.net">amar@dzanan.net ↗</a><a class="text-link light" routerLink="/">Selected work →</a></div>
|
||||
</section>
|
||||
</app-page-shell>
|
||||
`
|
||||
})
|
||||
export class CvPageComponent {
|
||||
readonly sections = MAIN_NAVIGATION;
|
||||
readonly roles = [
|
||||
{ period: '2025—Present', company: 'Unija Smart Accounting BiH', title: 'Senior Software Engineer', description: 'Modernize enterprise platforms and deliver full-stack workflows, operational automation, secure integration paths, and AI-assisted capabilities.' },
|
||||
{ period: 'Aug 2023—Present', company: 'COMP-2000', title: 'Product Owner', description: 'Shape product direction, manage priorities and backlog, align stakeholders, run reviews and retrospectives, and turn user feedback into delivery decisions.' },
|
||||
{ period: 'Nov 2020—Present', company: 'COMP-2000', title: 'Software Developer', description: 'Design, implement, test, and maintain applications across .NET, C#, Angular, Blazor, Ionic, Web API, SQL Server, and .NET MAUI.' },
|
||||
{ period: 'Nov 2018—2020', company: 'COMP-2000', title: 'Junior Software Developer', description: 'Built core engineering experience in C#, object-oriented design, SQL, Angular, Xamarin Forms, WPF, Windows Forms, and application lifecycle practices.' }
|
||||
];
|
||||
readonly skillGroups = [
|
||||
{ title: 'Backend & data', items: '.NET, C#, ASP.NET Core, Web API, Entity Framework Core, SQL Server, PostgreSQL, PostGIS' },
|
||||
{ title: 'Frontend & mobile', items: 'Angular, TypeScript, RxJS, Blazor, Ionic, .NET MAUI, Xamarin Forms, OpenLayers' },
|
||||
{ title: 'Architecture & integration', items: 'CQRS, multitenancy, SignalR, identity, SOAP and HTTP integrations, Model Context Protocol' },
|
||||
{ title: 'Delivery & operations', items: 'Docker, CI/CD, IIS, TLS, diagnostics, automated testing, incremental modernization' },
|
||||
{ title: 'Product practice', items: 'Product ownership, backlog shaping, stakeholder alignment, sprint reviews, retrospectives, user feedback' },
|
||||
{ title: 'Working style', items: 'End-to-end ownership, clear boundaries, pragmatic design, production awareness, cross-functional communication' }
|
||||
];
|
||||
readonly courses = [
|
||||
{ title: 'Learn Go Course', provider: 'Codecademy', period: 'Apr 2024' },
|
||||
{ title: 'Reactive Angular RxJS (Angular 17)', provider: 'Udemy', period: 'Mar 2024' },
|
||||
{ title: 'C# Advanced Topics: Prepare for Technical Interviews', provider: 'Udemy', period: 'Sep 2021' },
|
||||
{ title: 'Learning Docker', provider: 'LinkedIn Learning', period: 'Sep 2021' },
|
||||
{ title: 'Learning Entity Framework Core', provider: 'LinkedIn Learning', period: 'May 2021' },
|
||||
{ title: 'Building Web APIs with ASP.NET Core', provider: 'LinkedIn Learning', period: 'Aug 2020' },
|
||||
{ title: 'Software Architecture Foundations', provider: 'LinkedIn Learning', period: 'Jul 2020' },
|
||||
{ title: 'Linux Command Line', provider: 'Udemy', period: 'Jul 2018' }
|
||||
];
|
||||
|
||||
constructor() {
|
||||
inject(SeoService).update({
|
||||
title: 'CV — Amar Džanan, Senior Software Engineer',
|
||||
description: 'Experience, capabilities, and selected learning from senior full-stack engineer and product owner Amar Džanan.',
|
||||
path: '/cv'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SectionComponent } from '../../shared/ui/section/section.component';
|
||||
|
||||
type CourseItem = {
|
||||
title: string;
|
||||
provider: string;
|
||||
period: string;
|
||||
};
|
||||
|
||||
@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/70 bg-[#fbfcfa]/80 p-4 shadow-sm shadow-zinc-200/40 dark:border-slate-800/60 dark:bg-slate-900/40 dark:shadow-none"
|
||||
>
|
||||
<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>• Software architecture, product ownership, and team support.</li>
|
||||
<li>• Full-stack development across .NET, Angular, and mobile apps.</li>
|
||||
<li>• Designing, testing, and maintaining reliable C# services.</li>
|
||||
<li>• Turning user feedback into prioritized product delivery.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200/70 bg-[#fbfcfa]/80 p-4 shadow-sm shadow-zinc-200/40 dark:border-slate-800/60 dark:bg-slate-900/40 dark:shadow-none"
|
||||
>
|
||||
<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-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
Angular
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
TypeScript
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
.NET
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
Blazor
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
Web API
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
MSSQL
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
.NET MAUI
|
||||
</span>
|
||||
<span class="rounded-full bg-emerald-50/80 px-3 py-1 text-zinc-700 ring-1 ring-emerald-100/70 dark:bg-slate-800/80 dark:text-zinc-200 dark:ring-0">
|
||||
Ionic
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200/70 bg-[#fbfcfa]/80 p-4 shadow-sm shadow-zinc-200/40 dark:border-slate-800/60 dark:bg-slate-900/40 dark:shadow-none"
|
||||
>
|
||||
<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>• Shape product roadmaps through research and prioritization.</li>
|
||||
<li>• Manage backlogs, reviews, and retrospectives.</li>
|
||||
<li>• Collaborate with UX/UI, sales, marketing, and engineering.</li>
|
||||
<li>
|
||||
• Monitor KPIs and user feedback for continuous improvement.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-6 rounded-xl border border-zinc-200/70 bg-[#fbfcfa]/80 p-4 shadow-sm shadow-zinc-200/40 dark:border-slate-800/60 dark:bg-slate-900/40 dark:shadow-none"
|
||||
>
|
||||
<div class="flex flex-col gap-1 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-emerald-600 dark:text-emerald-300">
|
||||
Courses & certifications
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-300">
|
||||
Publicly listed learning history from LinkedIn-derived profile data.
|
||||
</p>
|
||||
</div>
|
||||
<span class="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
2018 - 2024
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
||||
@for (course of courses; track course.title) {
|
||||
<article
|
||||
class="rounded-lg border border-zinc-200/70 bg-white/55 p-3 dark:border-slate-800/70 dark:bg-slate-950/30"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<h3 class="text-sm font-semibold text-zinc-800 dark:text-zinc-50">
|
||||
{{ course.title }}
|
||||
</h3>
|
||||
<span class="shrink-0 text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{{ course.period }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{{ course.provider }}
|
||||
</p>
|
||||
</article>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</app-section>
|
||||
`
|
||||
})
|
||||
export class CvSectionComponent {
|
||||
readonly sectionId = 'cv';
|
||||
|
||||
readonly courses: CourseItem[] = [
|
||||
{
|
||||
title: 'Learn Go Course',
|
||||
provider: 'Codecademy',
|
||||
period: 'Apr 2024'
|
||||
},
|
||||
{
|
||||
title: 'Reactive Angular RxJS (Angular 17)',
|
||||
provider: 'Udemy',
|
||||
period: 'Mar 2024'
|
||||
},
|
||||
{
|
||||
title: 'C# Advanced Topics: Prepare for Technical Interviews',
|
||||
provider: 'Udemy',
|
||||
period: 'Sep 2021'
|
||||
},
|
||||
{
|
||||
title: 'Learning Docker',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Sep 2021'
|
||||
},
|
||||
{
|
||||
title: 'Applying MVVM in Xamarin.Forms Applications',
|
||||
provider: 'Pluralsight',
|
||||
period: 'May 2021'
|
||||
},
|
||||
{
|
||||
title: 'Architecting Xamarin.Forms Applications for code reuse',
|
||||
provider: 'Pluralsight',
|
||||
period: 'May 2021'
|
||||
},
|
||||
{
|
||||
title: 'Learning Entity Framework Core',
|
||||
provider: 'LinkedIn',
|
||||
period: 'May 2021'
|
||||
},
|
||||
{
|
||||
title: 'C# Intermediate: Classes, Objects and OOP',
|
||||
provider: 'Udemy',
|
||||
period: 'Oct 2020'
|
||||
},
|
||||
{
|
||||
title: 'Xamarin Forms: Build Native Cross-platform Apps with C#',
|
||||
provider: 'Udemy',
|
||||
period: 'Sep 2020'
|
||||
},
|
||||
{
|
||||
title: 'Building Web APIs with ASP.NET Core',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Aug 2020'
|
||||
},
|
||||
{
|
||||
title: 'Database Foundations: Storage',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Jul 2020'
|
||||
},
|
||||
{
|
||||
title: 'Software Architecture Foundations',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Jul 2020'
|
||||
},
|
||||
{
|
||||
title: 'C# Essential Training: 2 Flow Control, Arrays, and Exception Handling',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Jan 2020'
|
||||
},
|
||||
{
|
||||
title: 'C#: Interfaces and Generics',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Jan 2020'
|
||||
},
|
||||
{
|
||||
title: 'C# Essential Training: 1 Syntax and Object Oriented Programming',
|
||||
provider: 'LinkedIn',
|
||||
period: 'Dec 2019'
|
||||
},
|
||||
{
|
||||
title: 'SQL Server - Easy Install & Configure a Developer Instance',
|
||||
provider: 'Udemy',
|
||||
period: 'Jan 2019'
|
||||
},
|
||||
{
|
||||
title: 'C# Fundamentals by Coding',
|
||||
provider: 'Udemy',
|
||||
period: 'Dec 2018'
|
||||
},
|
||||
{
|
||||
title: 'Javascript Essentials',
|
||||
provider: 'Udemy',
|
||||
period: 'Jul 2018'
|
||||
},
|
||||
{
|
||||
title: 'Linux Command Line',
|
||||
provider: 'Udemy',
|
||||
period: 'Jul 2018'
|
||||
}
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user