{{ cluster.key }}
-{{ cluster.count }} events · {{ cluster.releases }} release version(s)
-{{ cluster.environments }}
-diff --git a/docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md b/docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md index 4ebf868c3..7cbdee00a 100644 --- a/docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md +++ b/docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md @@ -20,7 +20,7 @@ ## Delivery Tracker ### FE-TLD-001 - Freeze the canonical event model -Status: TODO +Status: DONE Dependency: none Owners: UX, Product Manager Task description: @@ -28,12 +28,12 @@ Task description: - Decide where relative time, absolute time, and grouping should appear so audit and ops surfaces remain truthful and scannable. Completion criteria: -- [ ] A canonical event model exists for mounted timeline surfaces. -- [ ] Rules for relative vs absolute time display are documented. -- [ ] Grouping or expansion expectations are defined before implementation. +- [x] A canonical event model exists for mounted timeline surfaces. +- [x] Rules for relative vs absolute time display are documented. +- [x] Grouping or expansion expectations are defined before implementation. ### FE-TLD-002 - Derive the shared timeline primitive -Status: TODO +Status: DONE Dependency: FE-TLD-001 Owners: Developer (FE) Task description: @@ -41,45 +41,57 @@ Task description: - Avoid keeping a toy timeline component that cannot carry actual operator evidence. Completion criteria: -- [ ] The shared timeline primitive supports the agreed event model. -- [ ] Timestamp rendering is deterministic and appropriate for audit-grade surfaces. -- [ ] The component supports richer detail than the current orphan implementation. +- [x] The shared timeline primitive supports the agreed event model. +- [x] Timestamp rendering is deterministic and appropriate for audit-grade surfaces. +- [x] The component supports richer detail than the current orphan implementation. ### FE-TLD-003 - Adopt the derived timeline on mounted chronology surfaces -Status: TODO +Status: DONE Dependency: FE-TLD-002 Owners: Developer (FE), UX Task description: - Adopt the derived timeline on a small set of mounted chronology surfaces where it improves consistency without flattening domain-specific meaning. - Use the adoption set to validate both compact event streams and denser evidence timelines. +Adoption surfaces: +1. **Incident Timeline** (`features/platform-health/incident-timeline.component.ts`) - replaced bespoke inline timeline with canonical component, preserving domain-specific affected-services chips and correlated-events expandable. +2. **Audit Timeline Search** (`features/audit-log/audit-timeline-search.component.ts`) - replaced bespoke inline timeline with canonical component, preserving module/action badge rendering via content projection. +3. **Releases Activity** (`features/releases/releases-activity.component.ts`) - replaced the timeline view mode (which was rendering a table identical to the table view) with the canonical timeline, preserving lane/environment/outcome chips via content projection. + Completion criteria: -- [ ] A bounded set of mounted chronology surfaces adopt the shared timeline. -- [ ] Timeline UX improves on scanability and event meaning. -- [ ] Domain-specific context is preserved, not lost to over-generalization. +- [x] A bounded set of mounted chronology surfaces adopt the shared timeline. +- [x] Timeline UX improves on scanability and event meaning. +- [x] Domain-specific context is preserved, not lost to over-generalization. ### FE-TLD-004 - Verify and document the derivation -Status: TODO +Status: DONE Dependency: FE-TLD-003 Owners: Test Automation, Documentation author Task description: - Add focused regression coverage for timeline formatting and document the canonical timeline contract and adoption choices. Completion criteria: -- [ ] Tests cover core timeline rendering and timestamp behavior. -- [ ] Docs explain where the shared timeline is appropriate and where bespoke views still make sense. -- [ ] The old orphan classification becomes intentional and documented. +- [x] Tests cover core timeline rendering and timestamp behavior. +- [x] Docs explain where the shared timeline is appropriate and where bespoke views still make sense. +- [x] The old orphan classification becomes intentional and documented. ## Execution Log | Date (UTC) | Update | Owner | | --- | --- | --- | | 2026-03-08 | Sprint created to derive the unused timeline-list into a canonical event-stream pattern for mounted audit and evidence chronologies. | Codex | +| 2026-03-08 | FE-TLD-001 DONE: Frozen canonical event model with TimelineEvent interface (id, timestamp, title, description, actor, eventKind, icon, evidenceLink, metadata, expandable). Time display rules: relative <24h, absolute UTC ISO-8601 >=24h, full ISO on tooltip. Date grouping supported. | Developer | +| 2026-03-08 | FE-TLD-002 DONE: Derived TimelineListComponent with vertical timeline, colored severity markers (info/success/warning/error/critical/neutral), deterministic UTC timestamps, expandable detail sections, actor/source metadata, date grouping, loading skeleton, empty state, accessibility (role="feed", aria-labels), and content projection. | Developer | +| 2026-03-08 | FE-TLD-003 DONE: Adopted on 3 surfaces: incident-timeline, audit-timeline-search, releases-activity (timeline view mode). Domain-specific context preserved via content projection. | Developer | +| 2026-03-08 | FE-TLD-004 DONE: 32 focused tests covering event rendering, severity markers, timestamp formatting (relative vs absolute), expandable toggle, loading/empty states, date grouping, accessibility, and default fallbacks. Build passes. | Developer | ## Decisions & Risks - Risk: oversimplifying audit/evidence timelines could erase domain meaning or precision. - Mitigation: freeze the event model first and adopt only on bounded surfaces where the shared primitive fits cleanly. +- Decision: Excluded witness/evidence hosts (sprint 031 territory), VEX timeline (domain-specific source-consensus visualization), pedigree timeline (horizontal ancestry lineage), observation timeline (SVG bar chart), and explainer timeline (process steps) from adoption because they are fundamentally different visualization patterns, not generic event streams. +- Decision: Used content projection (ng-template #eventContent) to allow adopting surfaces to render domain-specific chips, badges, and links without modifying the shared component. +- Decision: The `eventKind` field uses 'critical' as a distinct severity above 'error' (with visual emphasis via box-shadow ring). ## Next Checkpoints -- Freeze the event model and time-display rules. -- Build the richer shared timeline primitive. -- Adopt it on a bounded set of mounted chronology surfaces. +- Freeze the event model and time-display rules. -- DONE +- Build the richer shared timeline primitive. -- DONE +- Adopt it on a bounded set of mounted chronology surfaces. -- DONE diff --git a/docs/modules/ui/TASKS.md b/docs/modules/ui/TASKS.md index d8794cd92..903109b0f 100644 --- a/docs/modules/ui/TASKS.md +++ b/docs/modules/ui/TASKS.md @@ -24,7 +24,7 @@ - `docs/implplan/SPRINT_20260308_026_FE_settings_information_architecture_rationalization.md` - [DONE] `docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md` - Derived `PageHeaderComponent` into canonical `ContextHeaderComponent` with unified header contract, adopted on 4 target pages, 15 focused tests. - [DONE] `docs/implplan/SPRINT_20260308_028_FE_metric_card_dashboard_card_derivation.md` - Derived MetricCardComponent into canonical KPI card with semantic delta handling, severity accents, and loading/empty/error states. Adopted on 3 dashboards (12 bespoke tiles replaced). 40 tests pass. -- `docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md` +- [DONE] `docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md` - Derived canonical audit-grade timeline-list primitive. Adopted on incident-timeline, audit-timeline-search, and releases-activity. - [DONE] `docs/implplan/SPRINT_20260308_030_FE_split_pane_list_detail_shell_consolidation.md` - Consolidated SplitPaneComponent into ListDetailShellComponent as the canonical master-detail layout primitive. Added collapsible toggle, detail slide-in animation, and accessibility roles. Adopted on signing-key-dashboard. SplitPaneComponent deprecated. - `docs/implplan/SPRINT_20260308_031_FE_witness_viewer_evidence_derivation.md` diff --git a/docs/modules/ui/implementation_plan.md b/docs/modules/ui/implementation_plan.md index 25b38f84d..e609f175a 100644 --- a/docs/modules/ui/implementation_plan.md +++ b/docs/modules/ui/implementation_plan.md @@ -17,6 +17,7 @@ Provide a living plan for UI deliverables, dependencies, and evidence. - The queued orphan batch currently spans `SPRINT_20260308_013` through `SPRINT_20260308_023` and is intentionally not marked active until product review approves staffing. - Newly queued follow-on planning sprints cover Settings information architecture rationalization plus UX derivation tracks for the orphan `PageHeaderComponent`, `MetricCardComponent`, `TimelineListComponent`, `SplitPaneComponent`, and `WitnessViewerComponent` (`SPRINT_20260308_026` through `SPRINT_20260308_031`). - Sprint `028` (MetricCardComponent derivation into canonical KPI card) is DONE. The shared `MetricCardComponent` now supports semantic delta direction (`up-is-good` / `up-is-bad` / `neutral`), severity accents, loading/empty/error states, and ARIA accessibility. Adopted on signals-runtime, search-quality, and delivery-analytics dashboards. See `docs/implplan/SPRINT_20260308_028_FE_metric_card_dashboard_card_derivation.md`. +- Sprint `029` (TimelineListComponent derivation) is DONE. Canonical audit-grade timeline primitive with 6 severity levels, UTC timestamps, expandable detail, date grouping, and content projection. Adopted on incident-timeline, audit-timeline-search, and releases-activity. See `docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md`. - Sprint `030` (SplitPaneComponent consolidation into ListDetailShellComponent) is DONE. The canonical master-detail layout primitive is `ListDetailShellComponent` with collapsible toggle support. `SplitPaneComponent` is deprecated. Adopted on signing-key-dashboard (trust-admin). See sprint file for contract details. - Sprint `014` (CopyToClipboard, InlineCode, TruncatePipe adoption) is DONE. See `docs/features/checked/web/orphan-copy-inline-truncate-adoption.md`. - Sprint `015` (FilterBarComponent adoption) shipped, then was partially rolled back on audit-family pages to restore lost filter semantics. See `docs/features/checked/web/filter-bar-unification.md` and `docs/features/checked/web/orphan-revival-regression-remediation-ui.md`. diff --git a/src/Web/StellaOps.Web/src/app/features/audit-log/audit-timeline-search.component.ts b/src/Web/StellaOps.Web/src/app/features/audit-log/audit-timeline-search.component.ts index ac431dc6a..e877b3db8 100644 --- a/src/Web/StellaOps.Web/src/app/features/audit-log/audit-timeline-search.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/audit-log/audit-timeline-search.component.ts @@ -1,14 +1,23 @@ // Sprint: SPRINT_20251229_028_FE - Unified Audit Log Viewer -import { Component, inject, signal, ChangeDetectionStrategy } from '@angular/core'; - +// Updated: SPRINT_20260308_029_FE - Adopt canonical timeline-list (FE-TLD-003) +import { Component, inject, signal, computed, ChangeDetectionStrategy } from '@angular/core'; import { RouterModule } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { AuditLogClient } from '../../core/api/audit-log.client'; import { AuditTimelineEntry } from '../../core/api/audit-log.models'; +import { TimelineListComponent, TimelineEvent, TimelineEventKind } from '../../shared/ui/timeline-list/timeline-list.component'; + +function mapActionToKind(action: string): TimelineEventKind { + const lower = action.toLowerCase(); + if (lower.includes('create') || lower.includes('approve') || lower.includes('success')) return 'success'; + if (lower.includes('delete') || lower.includes('revoke') || lower.includes('fail')) return 'error'; + if (lower.includes('update') || lower.includes('modify')) return 'warning'; + return 'info'; +} @Component({ selector: 'app-audit-timeline-search', - imports: [RouterModule, FormsModule], + imports: [RouterModule, FormsModule, TimelineListComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
Correlated incidents with root-cause analysis
+Correlated incidents with root-cause analysis
{{ incidents().length }}
+{{ incidents().length }}
{{ activeCount() }}
+{{ activeCount() }}
{{ criticalCount() }}
+{{ criticalCount() }}
{{ resolvedCount() }}
+{{ resolvedCount() }}
{{ incident.description }}
- - -- Suggested Root Cause: - {{ incident.rootCauseSuggestion }} -
-{{ cluster.count }} events · {{ cluster.releases }} release version(s)
-{{ cluster.environments }}
-| Run | -Release Version | -Lane | -Outcome | -Environment | -Needs Approval | -Data Integrity | -When | -
|---|---|---|---|---|---|---|---|
| {{ row.activityId }} | -{{ row.releaseName }} | -{{ deriveLane(row) }} | -{{ deriveOutcome(row) }} | -{{ row.targetRegion || '-' }}/{{ row.targetEnvironment || '-' }} | -{{ deriveNeedsApproval(row) ? 'yes' : 'no' }} | -{{ deriveDataIntegrity(row) }} | -{{ formatDate(row.occurredAt) }} | -
| No runs match the active filters. | |||||||
| Run | +Release Version | +Lane | +Outcome | +Environment | +Needs Approval | +Data Integrity | +When | +
|---|---|---|---|---|---|---|---|
| {{ row.activityId }} | +{{ row.releaseName }} | +{{ deriveLane(row) }} | +{{ deriveOutcome(row) }} | +{{ row.targetRegion || '-' }}/{{ row.targetEnvironment || '-' }} | +{{ deriveNeedsApproval(row) ? 'yes' : 'no' }} | +{{ deriveDataIntegrity(row) }} | +{{ formatDate(row.occurredAt) }} | +
| No runs match the active filters. | |||||||
{{ event.description }}
- } - @if (eventTemplate) { -{{ event.description }}
+ } + + @if (event.evidenceLink) { + View evidence + } + + @if (event.metadata && hasKeys(event.metadata)) { + + } + + @if (event.expandable) { +{{ emptyMessage() }}
+