diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..13a1b79 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://dzanan.net/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..c5c0aa7 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,9 @@ + + + https://dzanan.net/ + https://dzanan.net/cv + https://dzanan.net/work/secure-ai-integration-gateway + https://dzanan.net/work/travel-operations-platform + https://dzanan.net/work/service-operations-platform + https://dzanan.net/work/biodiversity-data-platform + diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts index 4c42d34..ed40c59 100755 --- a/src/app/app.routes.server.ts +++ b/src/app/app.routes.server.ts @@ -1,8 +1,16 @@ import { RenderMode, ServerRoute } from '@angular/ssr'; -export const serverRoutes: ServerRoute[] = [ - { - path: '**', - renderMode: RenderMode.Prerender - } -]; +export const serverRoutes: ServerRoute[] = [ + { path: '', renderMode: RenderMode.Prerender }, + { path: 'cv', renderMode: RenderMode.Prerender }, + { path: 'work/secure-ai-integration-gateway', renderMode: RenderMode.Prerender }, + { path: 'work/travel-operations-platform', renderMode: RenderMode.Prerender }, + { path: 'work/service-operations-platform', renderMode: RenderMode.Prerender }, + { path: 'work/biodiversity-data-platform', renderMode: RenderMode.Prerender }, + { path: 'portfolio', renderMode: RenderMode.Server }, + { + path: '**', + renderMode: RenderMode.Server, + status: 404 + } +]; diff --git a/src/app/app.routes.spec.ts b/src/app/app.routes.spec.ts index df48739..1ea43b3 100755 --- a/src/app/app.routes.spec.ts +++ b/src/app/app.routes.spec.ts @@ -1,7 +1,43 @@ import { routes } from './app.routes'; describe('app routes', () => { - it('keeps the public pages registered', () => { - expect(routes.map(route => route.path)).toEqual(['', 'cv', 'portfolio']); + it('registers every public portfolio route', () => { + expect(routes.map(route => route.path)).toEqual([ + '', + 'cv', + 'work/secure-ai-integration-gateway', + 'work/travel-operations-platform', + 'work/service-operations-platform', + 'work/biodiversity-data-platform', + 'portfolio', + '**' + ]); + }); + + it('redirects the legacy portfolio route to the homepage', () => { + const legacyRoute = routes.find(route => route.path === 'portfolio'); + + expect(legacyRoute?.redirectTo).toBe(''); + expect(legacyRoute?.pathMatch).toBe('full'); + }); + + it('maps each work route to a stable project identifier', () => { + const projectIds = routes + .filter(route => route.path?.startsWith('work/')) + .map(route => route.data?.['projectId']); + + expect(projectIds).toEqual([ + 'ai-integration', + 'travel-operations', + 'service-operations', + 'biodiversity-data' + ]); + }); + + it('renders a real not-found page for unknown URLs', () => { + const wildcard = routes.find(route => route.path === '**'); + + expect(wildcard?.redirectTo).toBeUndefined(); + expect(wildcard?.loadComponent).toBeDefined(); }); }); diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 41b21e2..d58bf62 100755 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -11,7 +11,32 @@ export const routes: Routes = [ loadComponent: () => import('./features/cv/cv-page.component').then(m => m.CvPageComponent) }, { - path: 'portfolio', + path: 'work/secure-ai-integration-gateway', + data: { projectId: 'ai-integration' }, loadComponent: () => import('./features/portfolio/portfolio-page.component').then(m => m.PortfolioPageComponent) + }, + { + path: 'work/travel-operations-platform', + data: { projectId: 'travel-operations' }, + loadComponent: () => import('./features/portfolio/portfolio-page.component').then(m => m.PortfolioPageComponent) + }, + { + path: 'work/service-operations-platform', + data: { projectId: 'service-operations' }, + loadComponent: () => import('./features/portfolio/portfolio-page.component').then(m => m.PortfolioPageComponent) + }, + { + path: 'work/biodiversity-data-platform', + data: { projectId: 'biodiversity-data' }, + loadComponent: () => import('./features/portfolio/portfolio-page.component').then(m => m.PortfolioPageComponent) + }, + { + path: 'portfolio', + redirectTo: '', + pathMatch: 'full' + }, + { + path: '**', + loadComponent: () => import('./features/not-found/not-found-page.component').then(m => m.NotFoundPageComponent) } ]; diff --git a/src/app/core/layout/page-shell.component.ts b/src/app/core/layout/page-shell.component.ts index 44e7206..9446ac6 100755 --- a/src/app/core/layout/page-shell.component.ts +++ b/src/app/core/layout/page-shell.component.ts @@ -1,5 +1,5 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { CommonModule, NgOptimizedImage } from '@angular/common'; +import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'; +import { CommonModule } from '@angular/common'; import { RouterLink } from '@angular/router'; import { SectionNavItem } from '../../shared/models/section-nav.model'; import { ThemeToggleComponent } from '../../shared/ui/theme-toggle/theme-toggle.component'; @@ -7,88 +7,49 @@ import { ThemeToggleComponent } from '../../shared/ui/theme-toggle/theme-toggle. @Component({ selector: 'app-page-shell', standalone: true, - imports: [CommonModule, RouterLink, ThemeToggleComponent, NgOptimizedImage], + imports: [CommonModule, RouterLink, ThemeToggleComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: ` -
-
-
-
- Dzanan -
- Senior Software Engineer - Amar Džanan -
-
+
+ + -
- -
- -
+
+
-