feat(ui): reconnect evidence-thread and persona workspace routes [SPRINT-021]

Mount evidence-thread, auditor-workspace, and developer-workspace routes
under canonical /evidence family as drill-in lenses, not standalone shells.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
master
2026-03-08 19:25:32 +02:00
parent 900b291560
commit 1b934ad47a
7 changed files with 261 additions and 5 deletions

View File

@@ -4,6 +4,14 @@
import { Routes } from '@angular/router';
/**
* Evidence Thread Routes
*
* Mounted under /evidence/threads by evidence.routes.ts (Sprint 021).
* Canonical URLs:
* /evidence/threads - Thread list
* /evidence/threads/:artifactDigest - Thread detail for a specific artifact
*/
export const EVIDENCE_THREAD_ROUTES: Routes = [
{
path: '',
@@ -11,7 +19,8 @@ export const EVIDENCE_THREAD_ROUTES: Routes = [
import('./components/evidence-thread-list/evidence-thread-list.component').then(
(m) => m.EvidenceThreadListComponent
),
title: 'Evidence Threads'
title: 'Evidence Threads',
data: { breadcrumb: 'Threads' },
},
{
path: ':artifactDigest',
@@ -19,6 +28,7 @@ export const EVIDENCE_THREAD_ROUTES: Routes = [
import('./components/evidence-thread-view/evidence-thread-view.component').then(
(m) => m.EvidenceThreadViewComponent
),
title: 'Evidence Thread Detail'
title: 'Evidence Thread Detail',
data: { breadcrumb: 'Thread Detail' },
}
];

View File

@@ -1,7 +1,11 @@
/**
* Auditor Workspace Routes
* Sprint: SPRINT_0127_0001_FE_sbom_vex_persona_views
* Updated: Sprint 021 - Mounted under /evidence/workspaces/auditor
* Task: FE-PERSONA-05 - Auditor Workspace Layout
*
* Canonical URL: /evidence/workspaces/auditor/:artifactDigest
* Acts as an evidence lens, not a parallel shell.
*/
import { Routes } from '@angular/router';
@@ -14,5 +18,6 @@ export const AUDITOR_WORKSPACE_ROUTES: Routes = [
(m) => m.AuditorWorkspaceComponent
),
title: 'Auditor Workspace',
data: { breadcrumb: 'Auditor' },
},
];

View File

@@ -1,7 +1,11 @@
/**
* Developer Workspace Routes
* Sprint: SPRINT_0127_0001_FE_sbom_vex_persona_views
* Updated: Sprint 021 - Mounted under /evidence/workspaces/developer
* Task: FE-PERSONA-04 - Developer Workspace Layout
*
* Canonical URL: /evidence/workspaces/developer/:artifactDigest
* Acts as an evidence lens, not a parallel shell.
*/
import { Routes } from '@angular/router';
@@ -13,8 +17,7 @@ export const DEVELOPER_WORKSPACE_ROUTES: Routes = [
import('./components/developer-workspace/developer-workspace.component').then(
(m) => m.DeveloperWorkspaceComponent
),
data: {
title: 'Developer Workspace',
},
title: 'Developer Workspace',
data: { breadcrumb: 'Developer' },
},
];

View File

@@ -0,0 +1,72 @@
/**
* Evidence Routes - Route structure verification
* Sprint 021: FE-UET-004
*
* Validates that evidence-thread and persona-workspace routes are mounted
* under the canonical /evidence route family with correct breadcrumbs and
* lazy-loading configuration.
*/
import { EVIDENCE_ROUTES } from './evidence.routes';
describe('EVIDENCE_ROUTES', () => {
it('should export a non-empty route array', () => {
expect(EVIDENCE_ROUTES).toBeDefined();
expect(Array.isArray(EVIDENCE_ROUTES)).toBe(true);
expect(EVIDENCE_ROUTES.length).toBeGreaterThan(0);
});
it('should mount evidence threads at "threads"', () => {
const threadsRoute = EVIDENCE_ROUTES.find((r) => r.path === 'threads');
expect(threadsRoute).toBeDefined();
expect(threadsRoute!.loadChildren).toBeDefined();
expect(threadsRoute!.title).toBe('Evidence Threads');
expect(threadsRoute!.data?.['breadcrumb']).toBe('Threads');
});
it('should mount auditor workspace at "workspaces/auditor"', () => {
const auditorRoute = EVIDENCE_ROUTES.find((r) => r.path === 'workspaces/auditor');
expect(auditorRoute).toBeDefined();
expect(auditorRoute!.loadChildren).toBeDefined();
expect(auditorRoute!.title).toBe('Auditor Workspace');
expect(auditorRoute!.data?.['breadcrumb']).toBe('Auditor Workspace');
});
it('should mount developer workspace at "workspaces/developer"', () => {
const devRoute = EVIDENCE_ROUTES.find((r) => r.path === 'workspaces/developer');
expect(devRoute).toBeDefined();
expect(devRoute!.loadChildren).toBeDefined();
expect(devRoute!.title).toBe('Developer Workspace');
expect(devRoute!.data?.['breadcrumb']).toBe('Developer Workspace');
});
it('should not introduce a separate top-level persona menu', () => {
// All persona routes must be nested under workspaces/, not at root
const topLevelPaths = EVIDENCE_ROUTES.map((r) => r.path);
expect(topLevelPaths).not.toContain('auditor');
expect(topLevelPaths).not.toContain('developer');
});
it('should preserve the canonical evidence overview route', () => {
const overviewRoute = EVIDENCE_ROUTES.find((r) => r.path === '' || r.path === 'overview');
expect(overviewRoute).toBeDefined();
});
it('should preserve capsules, proofs, exports, and audit-log routes', () => {
const paths = EVIDENCE_ROUTES.map((r) => r.path);
expect(paths).toContain('capsules');
expect(paths).toContain('proofs');
expect(paths).toContain('exports');
expect(paths).toContain('audit-log');
});
it('should use loadChildren for lazy-loaded thread and workspace routes', () => {
const lazyRoutes = ['threads', 'workspaces/auditor', 'workspaces/developer'];
for (const path of lazyRoutes) {
const route = EVIDENCE_ROUTES.find((r) => r.path === path);
expect(route).toBeDefined();
expect(typeof route!.loadChildren).toBe('function');
// loadComponent should not be set when loadChildren is used
expect(route!.loadComponent).toBeUndefined();
}
});
});

View File

@@ -1,5 +1,23 @@
import { Routes } from '@angular/router';
/**
* Evidence Routes
*
* Canonical URL contract (Sprint 021):
* /evidence - Evidence overview
* /evidence/overview - Evidence overview (alias)
* /evidence/threads - Evidence thread list
* /evidence/threads/:artifactDigest - Evidence thread detail
* /evidence/workspaces/auditor/:artifactDigest - Auditor workspace lens
* /evidence/workspaces/developer/:artifactDigest - Developer workspace lens
* /evidence/capsules - Decision capsule list
* /evidence/capsules/:capsuleId - Decision capsule detail
* /evidence/verify-replay - Verify & Replay
* /evidence/proofs - Proof chains
* /evidence/exports - Evidence exports
* /evidence/proof-chain - Proof chain (alias)
* /evidence/audit-log - Audit log
*/
export const EVIDENCE_ROUTES: Routes = [
{
path: '',
@@ -19,6 +37,27 @@ export const EVIDENCE_ROUTES: Routes = [
(m) => m.EvidenceAuditOverviewComponent,
),
},
{
path: 'threads',
title: 'Evidence Threads',
data: { breadcrumb: 'Threads' },
loadChildren: () =>
import('../features/evidence-thread/evidence-thread.routes').then((m) => m.EVIDENCE_THREAD_ROUTES),
},
{
path: 'workspaces/auditor',
title: 'Auditor Workspace',
data: { breadcrumb: 'Auditor Workspace' },
loadChildren: () =>
import('../features/workspaces/auditor/auditor-workspace.routes').then((m) => m.AUDITOR_WORKSPACE_ROUTES),
},
{
path: 'workspaces/developer',
title: 'Developer Workspace',
data: { breadcrumb: 'Developer Workspace' },
loadChildren: () =>
import('../features/workspaces/developer/developer-workspace.routes').then((m) => m.DEVELOPER_WORKSPACE_ROUTES),
},
{
path: 'capsules',
title: 'Decision Capsules',