diff --git a/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane.store.ts b/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane.store.ts index 505e60cca..8c523de12 100644 --- a/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane.store.ts +++ b/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane.store.ts @@ -296,7 +296,7 @@ export class ControlPlaneStore { description: 'Signing key needs rotation', severity: 'info', createdAt: new Date().toISOString(), - actionLink: '/settings/trust/keys', + actionLink: '/evidence-audit/trust-signing/keys', }, ], totalCount: 4, diff --git a/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts b/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts index e3158d0c0..efa2903d1 100644 --- a/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/evidence-audit/evidence-audit-overview.component.ts @@ -5,7 +5,7 @@ * Domain overview page for Evidence & Audit (V0). Routes users to the evidence surface * matching their need: promotion decision, bundle evidence, environment snapshot, * proof verification, or audit trail. - * Trust & Signing ownership remains in Administration; Evidence consumes trust state. + * Trust & Signing is owned within Evidence & Audit after v2 ownership transition. */ import { @@ -16,13 +16,12 @@ import { } from '@angular/core'; import { RouterLink } from '@angular/router'; -interface EvidenceEntryCard { +interface EvidenceQuickViewTile { + id: string; title: string; - description: string; - link: string; - linkLabel: string; - icon: string; - status?: 'ok' | 'warning' | 'info'; + window: string; + count: number; + detail: string; } type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; @@ -57,28 +56,72 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; } - -
-

Evidence Surfaces

- @if (entryCards().length === 0) { -
-

No evidence records are available yet.

- Open Release Control Promotions -
- } @else { -
- @for (card of entryCards(); track card.link) { - - -
-
{{ card.title }}
-
{{ card.description }}
-
-
{{ card.linkLabel }} →
-
- } -
- } +
+

Find Evidence

+
+ + + + +
+ +
+ +
+
+ +
+

Quick Views

+
+ @for (tile of quickViews(); track tile.id) { +
+
+

{{ tile.title }}

+ {{ tile.window }} +
+
{{ tile.count.toLocaleString() }}
+

{{ tile.detail }}

+
+ } +
+
+ +
+

Shortcuts

+
@@ -115,11 +158,11 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; - + @@ -150,9 +193,9 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; - Trust and signing operations are owned by - Administration > Trust & Signing. - Evidence & Audit consumes trust state as a read-only consumer. + Trust and signing operations are available at + Evidence > Trust & Signing + with permanent aliases from legacy settings/admin paths. `, @@ -227,83 +270,127 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; font-size: 0.84rem; } - /* Entry Cards */ - .entry-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 1rem; - } - - .empty-state { - border: 1px dashed var(--color-border-primary); - border-radius: var(--radius-md); - background: var(--color-surface-primary); - padding: 1rem; - } - - .empty-state p { - margin: 0 0 0.4rem; - color: var(--color-text-secondary); - font-size: 0.84rem; - } - - .empty-state a { - color: var(--color-brand-primary); - text-decoration: none; - font-size: 0.84rem; - } - - .entry-card { - display: flex; - align-items: flex-start; - gap: 1rem; - padding: 1.1rem 1.25rem; + .search-section { border: 1px solid var(--color-border-primary); border-radius: var(--radius-lg); background: var(--color-surface-primary); - text-decoration: none; - color: var(--color-text-primary); - transition: box-shadow 0.15s, border-color 0.15s; - position: relative; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.85rem; } - .entry-card:hover { - box-shadow: var(--shadow-md); - border-color: var(--color-brand-primary); + .search-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 0.75rem; } - .entry-card.ok { border-left: 4px solid var(--color-status-success); } - .entry-card.warning { border-left: 4px solid var(--color-status-warning); } - .entry-card.info { border-left: 4px solid var(--color-brand-primary); } - - .entry-icon { - font-size: 1.5rem; - flex-shrink: 0; - width: 2rem; - text-align: center; + .search-field, + .lookup-field { + display: flex; + flex-direction: column; + gap: 0.35rem; } - .entry-body { - flex: 1; - } - - .entry-title { - font-weight: var(--font-weight-semibold); - font-size: 0.95rem; - margin-bottom: 0.2rem; - } - - .entry-description { - font-size: 0.8rem; + .field-label { + font-size: 0.76rem; color: var(--color-text-secondary); - line-height: 1.4; + font-weight: var(--font-weight-semibold); } - .entry-link-label { + .search-field select, + .lookup-field input { + border: 1px solid var(--color-border-primary); + border-radius: var(--radius-sm); + padding: 0.45rem 0.5rem; + font-size: 0.84rem; + background: var(--color-surface-primary); + color: var(--color-text-primary); + } + + .search-actions { + display: flex; + justify-content: flex-start; + } + + .search-actions button { + border: 1px solid var(--color-brand-primary); + background: var(--color-brand-primary); + color: var(--color-text-inverse, #fff); + border-radius: var(--radius-sm); + padding: 0.45rem 0.8rem; + font-size: 0.82rem; + cursor: pointer; + } + + .quick-views-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); + gap: 0.85rem; + } + + .quick-view-tile { + border: 1px solid var(--color-border-primary); + border-radius: var(--radius-md); + background: var(--color-surface-primary); + padding: 0.85rem; + display: flex; + flex-direction: column; + gap: 0.35rem; + } + + .quick-view-header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.5rem; + } + + .quick-view-title { + margin: 0; + font-size: 0.84rem; + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + } + + .quick-view-window { + font-size: 0.72rem; + color: var(--color-text-secondary); + } + + .quick-view-value { + font-size: 1.3rem; + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); + } + + .quick-view-detail { + margin: 0; + font-size: 0.76rem; + color: var(--color-text-secondary); + line-height: 1.35; + } + + .shortcut-links { + display: flex; + flex-wrap: wrap; + gap: 0.6rem; + } + + .shortcut-link { + border: 1px solid var(--color-border-primary); + border-radius: 999px; + padding: 0.35rem 0.65rem; font-size: 0.8rem; + text-decoration: none; color: var(--color-brand-primary); - margin-top: 0.75rem; - font-weight: var(--font-weight-medium); + background: var(--color-surface-primary); + } + + .shortcut-link:hover { + border-color: var(--color-brand-primary); + background: var(--color-surface-elevated); } /* Stats Section */ @@ -425,68 +512,103 @@ type EvidenceHomeMode = 'normal' | 'degraded' | 'empty'; export class EvidenceAuditOverviewComponent { readonly mode = signal('normal'); - private readonly normalEntryCards: EvidenceEntryCard[] = [ - { - title: 'Evidence Packs', - description: 'Structured evidence collections for releases, bundles, and promotion decisions.', - link: '/evidence-audit/packs', - linkLabel: 'Browse packs', - icon: '📦', - status: 'info', - }, - { - title: 'Proof Chains', - description: 'Cryptographic proof chain traversal from subject digest to attestation.', - link: '/evidence-audit/proofs', - linkLabel: 'View proofs', - icon: '🔒', - status: 'info', - }, - { - title: 'Replay and Verify', - description: 'Replay historical verdict decisions and verify deterministic evidence outcomes.', - link: '/evidence-audit/replay', - linkLabel: 'Open replay', - icon: '↻', - status: 'warning', - }, - { - title: 'Timeline', - description: 'Timeline and checkpoint history for release evidence progression.', - link: '/evidence-audit/timeline', - linkLabel: 'Open timeline', - icon: '⏰', - status: 'info', - }, - { - title: 'Audit Log', - description: 'Comprehensive audit log filtered by actor, action, resource, and domain context.', - link: '/evidence-audit/audit', - linkLabel: 'Open audit log', - icon: '📄', - status: 'ok', - }, - { - title: 'Change Trace', - description: 'Byte-level change tracing between artifact versions with proof annotations.', - link: '/evidence-audit/change-trace', - linkLabel: 'Explore changes', - icon: '📊', - status: 'info', - }, - { - title: 'Evidence Export', - description: 'Export center: bundle exports, replay/verify, and scoped export jobs.', - link: '/evidence-audit/evidence', - linkLabel: 'Export center', - icon: '📢', - status: 'info', - }, - ]; + readonly quickViews = computed((): EvidenceQuickViewTile[] => { + if (this.mode() === 'empty') { + return [ + { + id: 'latest-packs', + title: 'Latest promotion evidence packs', + window: '24h', + count: 0, + detail: 'No packs in the selected window.', + }, + { + id: 'latest-bundles', + title: 'Latest sealed bundles', + window: '7d', + count: 0, + detail: 'No bundles sealed in the selected window.', + }, + { + id: 'failed-replay', + title: 'Failed verification / replay', + window: '7d', + count: 0, + detail: 'No failed replay jobs in the selected window.', + }, + { + id: 'expiring-trust', + title: 'Expiring trust/certs', + window: '30d', + count: 0, + detail: 'No trust certificates nearing expiration.', + }, + ]; + } - readonly entryCards = computed(() => { - if (this.mode() === 'empty') return [] as EvidenceEntryCard[]; - return this.normalEntryCards; + if (this.mode() === 'degraded') { + return [ + { + id: 'latest-packs', + title: 'Latest promotion evidence packs', + window: '24h', + count: 28, + detail: 'One data source is stale; count may lag.', + }, + { + id: 'latest-bundles', + title: 'Latest sealed bundles', + window: '7d', + count: 91, + detail: 'Bundle freshness from last successful index sync.', + }, + { + id: 'failed-replay', + title: 'Failed verification / replay', + window: '7d', + count: 4, + detail: 'Two jobs need replay triage.', + }, + { + id: 'expiring-trust', + title: 'Expiring trust/certs', + window: '30d', + count: 3, + detail: 'Rotate issuer certs before export attestations.', + }, + ]; + } + + return [ + { + id: 'latest-packs', + title: 'Latest promotion evidence packs', + window: '24h', + count: 33, + detail: 'All packs sealed and attached to promotion runs.', + }, + { + id: 'latest-bundles', + title: 'Latest sealed bundles', + window: '7d', + count: 106, + detail: 'Bundles are available for auditor download.', + }, + { + id: 'failed-replay', + title: 'Failed verification / replay', + window: '7d', + count: 1, + detail: 'One replay mismatch pending operator review.', + }, + { + id: 'expiring-trust', + title: 'Expiring trust/certs', + window: '30d', + count: 2, + detail: 'Two certificates need planned rotation.', + }, + ]; }); readonly stats = computed(() => { diff --git a/src/Web/StellaOps.Web/src/app/layout/context-chips/evidence-mode-chip.component.ts b/src/Web/StellaOps.Web/src/app/layout/context-chips/evidence-mode-chip.component.ts index 81e26a12a..f1a839d13 100644 --- a/src/Web/StellaOps.Web/src/app/layout/context-chips/evidence-mode-chip.component.ts +++ b/src/Web/StellaOps.Web/src/app/layout/context-chips/evidence-mode-chip.component.ts @@ -20,7 +20,7 @@ import { AUTH_SERVICE, AuthService, StellaOpsScopes } from '../../core/auth'; class="chip" [class.chip--on]="isEnabled()" [class.chip--off]="!isEnabled()" - routerLink="/settings/trust" + routerLink="/evidence-audit/trust-signing" [attr.title]="tooltip()" >