This commit is contained in:
@@ -3,7 +3,7 @@ import { provideRouter } from '@angular/router';
|
||||
import { CvPageComponent } from './cv-page.component';
|
||||
|
||||
describe('CvPageComponent', () => {
|
||||
it('renders a complete, contactable CV with curated learning', async () => {
|
||||
it('embeds the PDF and offers a download link', async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CvPageComponent],
|
||||
providers: [provideRouter([])]
|
||||
@@ -13,13 +13,7 @@ describe('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();
|
||||
expect(page.querySelector('object[data="/cv.pdf"]')).toBeTruthy();
|
||||
expect(page.querySelector('a[href="/cv.pdf"][download]')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { PageShellComponent } from '../../core/layout/page-shell.component';
|
||||
import { SeoService } from '../../core/services/seo.service';
|
||||
import { MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
import { CV_URL, MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cv-page',
|
||||
@@ -11,93 +11,41 @@ import { MAIN_NAVIGATION } from '../../shared/config/navigation.config';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<app-page-shell [sections]="sections">
|
||||
<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>
|
||||
<header class="page-head shell">
|
||||
<nav class="breadcrumb" aria-label="Breadcrumb"><a routerLink="/">~/</a><span>cv.pdf</span></nav>
|
||||
<span class="label">// curriculum vitae</span>
|
||||
<h1>Amar Džanan — CV</h1>
|
||||
<p class="lead">Read it here, or take the PDF with you.</p>
|
||||
<div class="actions">
|
||||
<a class="btn btn-accent" [href]="cvUrl" download="Amar-Dzanan-CV.pdf">download cv.pdf
|
||||
<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M8 2v9M4.5 7.5 8 11l3.5-3.5M3 13.5h10"/></svg>
|
||||
</a>
|
||||
<a class="btn btn-line" [href]="cvUrl" target="_blank" rel="noopener">open in new tab ↗</a>
|
||||
</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>
|
||||
<section class="section tight shell" aria-label="CV document">
|
||||
<div class="panel cv-frame">
|
||||
<div class="terminal-bar"><div class="terminal-dots" aria-hidden="true"><span></span><span></span><span></span></div><span class="terminal-title">cv.pdf</span><span class="terminal-spacer"></span></div>
|
||||
<object data="/cv.pdf" type="application/pdf" aria-label="Amar Džanan CV (PDF)">
|
||||
<div class="cv-fallback">
|
||||
<p>Your browser doesn't display PDFs inline.</p>
|
||||
<a class="btn btn-accent" [href]="cvUrl" download="Amar-Dzanan-CV.pdf">download cv.pdf</a>
|
||||
</div>
|
||||
</object>
|
||||
</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' }
|
||||
];
|
||||
readonly cvUrl = CV_URL;
|
||||
|
||||
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.',
|
||||
description: 'Curriculum vitae of Amar Džanan: senior full-stack engineer and product owner working with .NET, Angular, and AI integration.',
|
||||
path: '/cv'
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user