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
-
-
+