07 — Component Library (Audit Module)
- A. Entry representations
- B. Filtering
- C. Diff & JSON rendering (the heart of the detail view)
- D. Timeline
(proposed) - E. Status & feedback
- F. Only elements the API supports
- G. Flutter widget map (module)
- H. Golden tests (base 00-shared/03 G)
Module-specific components built from shared
App*primitives (00-shared/03). Props, states, animation, a11y, tokens, Flutter mapping. Reuse over new — only components that don't exist in the shared library are defined here. Data fields are exact fromaudit-log.schema.ts:13-56and the response contract in 12_API_Mapping.md.
A. Entry representations
AuditEntryRow (phone card)
- Purpose: one audit entry in the mobile list.
- Props:
entry(AuditEntrymodel),onTap,highlighted(realtime-append flash). - Rendering:
AppCarde-0: line 1AuditActionChip(action)+bodyMediumlocal time (DateTime.toLocal()); line 2 actor (bodyMediumname if resolved(proposed), elseactorIdmono) +entityType/entityIdmono when present; line 3correlationIdmonobodySmallonSurfaceVariant(truncated, tooltip full). - States: default / highlighted (
primaryContainerflashm-base) / pressed. - a11y: card button semantics "StudentUpdated by
at - Flutter:
Card+InkWell+Column.
AuditTable (desktop ≥ 1200 px)
- Purpose: dense read-only log table (primary desktop surface).
- Props:
rows(AuditEntry),onRowTap(id),loadingMore,total. - Columns: Time (local, UTC tooltip) · Action (
AuditActionChip) · Actor (name/id) · Entity (entityType + id mono) · Context (correlationIdmono, copy icon). - Behaviour: sticky header (
El-e-2); hover row tintm-fast; row tap → detail;Semanticscolumn headers; footer row "Showing X of Y — End of audit trail". - Flutter: custom
DataTable-free implementation —Table/TableRowinside horizontalSingleChildScrollView,ListView.builderfor rows (virtualized); orDataTable2-style if licensed — wrap inAuditTableso it is swappable.
AuditActionChip
- Purpose: compact action token with category color.
- Props:
action(string),outlined?. - Category mapping
(proposed): prefixes/contains —create|created|register→success;delete|deleted|revoke|logout→error;update|updated|change|edit|enable|disable→tertiary;login|login→primary; else neutralsurfaceVariant. Text = rawaction(server vocabulary is free-form PascalCaseeventType,audit.handler.ts:36— never translated by mapping). - a11y:
Semantics(label: action); color never sole signal — text always present. - Flutter:
AppBadge/ChipwithMaterialStatePropertybackground.
B. Filtering
AuditFilterBar
- Purpose: AND-combined filters →
GET /audit-logsparams (audit.controller.ts:22-24). - Props:
actions[],entityTypes[],actorId,onChanged(filters),onClear,busy. - Rendering:
WrapofAppDropdowns (searchable) + actorAppTextField+Clear; active filters mirrored as removableAppChips. - Behaviour: any change →
page=1reload (03 §2);Escclears. - a11y: each control labeled; results count announced (live region).
- Flutter:
Wrap+ shared dropdown widget.
C. Diff & JSON rendering (the heart of the detail view)
AuditDiffView
- Purpose: render
beforevsaftermaps (audit-log.schema.ts:45-49) as changed/added/ removed rows. - Props:
before(Map<String,dynamic>?),after(Map<String,dynamic>?),onExpand(key, value). - Computation (client-side): union of keys →
changed(value differs),added(after-only),removed(before-only). - Rendering per key: key
mono labelMedium· status badge (changed→tertiary,added→success,removed→error) · before (struck through,errortint) → after (successtint). Nested values → "Object (3 keys)" / "Array (5 items)" row → tap expandsJsonTreeView. Empty maps →AppEmptyStatecompact "No snapshot captured". - a11y: each row semantics "field
changed"; live region on expand. - Flutter:
Columnof customDiffRowwidgets;AnimatedSizeexpand.
JsonTreeView
- Purpose: inspect arbitrary payload values (
after= payload fallback,audit.handler.ts:43;metadata,audit-log.schema.ts:54-55). - Props:
value,label?,depth,copyable. - Behaviour: recursive expand/collapse;
SelectableTextleaves; "Copy JSON" action (snackbar "Copied"); collapsed arrays/objects show counts;maxDepthguard with overflow marker. - a11y: tree exposes
Semanticsexpand buttons + value labels; announce "3 keys". - Flutter: recursive
Widgetbuilder;ExpansionTile-free custom (dense).
D. Timeline (proposed)
EntryTimeline
- Purpose: reverse-chronology of related entries (same
entityId/correlationId). - Props:
entries[],onEntryTap. - Rendering: vertical rail + dots per entry (time,
AuditActionChip, actor). - Status:
(proposed)— needs backendentityIdfilter (OQ-1) or client grouping. - Flutter: custom
CustomPaintrail + list.
E. Status & feedback
- Reuses
AppSkeleton,AppEmptyState,AppErrorState,AppOfflineBanner,AppSnackbarfrom 00-shared/03 unchanged. AuditNewEntryBanner: inlineAppBanner info"1 new entry — tap to view" (realtime append), auto-dismiss 6 s; a11y live region.
F. Only elements the API supports
- No create/edit/delete/export actions — server exposes a single GET (
audit.controller.ts:17); anything else is(planned)(AUDITING.md:87) and speced disabled. - No actor-name field in the response — name resolution
(proposed)via users module.
G. Flutter widget map (module)
| Component | Flutter widgets |
|---|---|
| AuditEntryRow | Card + ListTile-style Row |
| AuditTable | Table/custom rows in SingleChildScrollView |
| AuditActionChip | Chip + Badge |
| AuditFilterBar | Wrap + shared AppDropdown |
| AuditDiffView | custom DiffRow column |
| JsonTreeView | recursive ExpansionTile-like builder |
EntryTimeline (proposed) | CustomPaint rail |
H. Golden tests (base 00-shared/03 G)
Every module component: golden at phone/tablet/desktop + dark mode; AuditDiffView with
changed/added/removed/nested/empty permutations; JsonTreeView deep + long-string cases.