Files
HiveGrid-Wireframes/Tickets/Interim-Handover/P03-Audit-Notifications.md
AkeshanandClaude Opus 4.8 d8b6772148 Interim-UI-Handover: 55 Vorschau-Screens fuer David (AD-28)
Uebergabe der Dev-Interim-Screens aus HiveGrid-Cloud an den Wireframes-Vault.
INDEX + 7 Domaenen-Seiten (CRM, Leads, HR, Files/Tasks, Audit/Notifications,
Integrations, Auth/RBAC) mit je Screen: D-*-Ziel, Archetyp, Route, VM,
benoetigte ~/ui-Elemente, Actions, States. Layout bleibt Davids freie Hand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 17:35:12 +02:00

9.6 KiB

P03 · Audit & Notifications (AK-P3 → D-P3)

Interim-UI-Referenz (AD-28). Beide Domänen sind AK-P3-Interim-Screens, rein aus ~/ui komponiert, VM als Prop. Layout = Davids freie Hand — Listen nennen nur Elemente/Daten/Aktionen/States. Keine der fünf Dateien importiert @subframe/core/Icons; alle Affordances aus ~/ui-Primitives.

Audit

AuditListPage · Audit-Viewer (A2 + Drawer)

  • Interim-Screen: apps/web/app/components/screens/audit/AuditListPage.tsx
  • Route(n): /admin/audit (routes/admin/audit.tsx) — Admin-Host, nicht app.$tenant. Permission-Gate admin.auditTrails.view.
  • Daten (VM): AuditListVM (Rows/Filter über AuditListFilterState, AuditListRow) — packages/view-models/src/audit/audit-list.vm.ts
  • Benötigte UI-Elemente (KEIN Layout): Alert · Button · Drawer · EmptyState · Select (+ Select.Item) · Table (HeaderRow/HeaderCell/Row/Cell) · Tag_Label · TextInput (+ TextInput.Input) · DateField (lokal ../tasks/DateField). Inhaltselemente: Header (Titel + Subtitle + Export-CTA nur bei canExport), Filter-Bar (from/to Datum, Actor-Type-Select, Outcome-Select, Action-Text, TargetType-Text, Apply-Button), Tabelle (Zeit/Actor+Kind-Tag/Action monospace/Subjekt/Outcome/Summary + Detail-Button bei hasDetail), Server-Pagination (pageInfo + prev/next), Detail-Drawer (Feld-für-Feld-Diff-Tabelle mit „masked"→Tag „Wert geändert", Metadata <pre>, leer→Alert). Icons: keine.
  • Interaktionen (Actions): onApply(query: AuditListQuery) — AuditListQuery = AuditListFilterState & { page: number }; getriggert von Filter-Submit, Select-onValueChange, prev/next. Detail-Drawer öffnet rein lokal (useState openRowId), kein Action-Callback.
  • States: Loading = vm.status !== "ready" → leerer Marker-Div · Empty = rows.length === 0 → EmptyState (emptyHeadlineKey/emptyBodyKey) · Drawer-Empty = kein Diff/Metadata → Alert · Permission-Denied = Route-Level 404 (verifyUserHasPermission), kein In-Component-403.
  • Davids Delta (offen):
    • Überschriebene/geänderte Spec-Punkte: Quick-Filter (Klick auf Actor/Subject/Action) und „Verlauf dieses Objekts"/„Ähnliche Einträge" aus D-P3-Zeile sind im Interim NICHT gebaut — nur Formular-Filter + Diff-Drawer.
    • Neue UI/UX-Entscheidungen: visuelle Unterscheidung der Actor-Kinds (User/Automation/MCP/Agent/Admin) — interim nur neutrales Tag_Label.
    • Subframe-Screen-Link(s):
    • Offene Fragen an Dev: Sollen Quick-Filter denselben onApply nutzen (Patch je Feld)?

AuditExportPage · Audit-Export-Jobs (A2)

  • Interim-Screen: apps/web/app/components/screens/audit/AuditExportPage.tsx
  • Route(n): /admin/audit-export (routes/admin/audit-export.tsx) — Admin-Host. (Tenant-Variante app.$tenant/settings/audit-export.tsx = /app/:tenant/settings/audit-export rendert eigenes Inline-UI, NICHT diesen Interim-Screen.)
  • Daten (VM): AuditExportVM (+ AuditExportResultVM als result-Prop) — packages/view-models/src/audit/audit-export.vm.ts
  • Benötigte UI-Elemente (KEIN Layout): Alert · Button · Select (+ Select.Item, Helper SelectWithAny) · TextInput (+ TextInput.Input) · DateField (lokal ../tasks/DateField). Inhaltselemente: Header (Titel + Beschreibung mit maxRows), ein Formular mit drei Submit-Pfaden (GET csv / GET pdf / POST storage-export), Filterfelder (from/to, tenantId optional, regulatoryScope/actorType/outcome je Select-mit-„Any", purpose Pflicht-Textfeld, format Select), Ergebnis-Panel (Erfolg/Warn-Alert mit Download-Link + Integritäts-Warnung, oder Error-Alert). Icons: keine.
  • Interaktionen (Actions): KEIN …Actions-Typ — reine HTML-Form-Submits gegen die Route (GET streamt CSV/PDF, POST läuft Storage-Export). Einzige Prop neben vm ist result (Aktions-Ergebnis, null = noch nichts abgeschickt).
  • States: Loading = vm.status !== "ready" → leerer Marker-Div · Ergebnis: result.ok===false → Error-Alert (errorKey); result.ok → Success/Warning-Alert (Row-Count, Ablauftage, Download-Link, ggf. Integritäts-Warnung); result===null → kein Panel · Kein Empty-State · Permission-Denied = Route-Level 404.
  • Davids Delta (offen):
    • Überschriebene/geänderte Spec-Punkte: D-P3 sieht Export-Job-Liste + /new + /:id (A2) vor — Interim ist ein Single-Formular ohne Job-Liste/Detail-Route.
    • Neue UI/UX-Entscheidungen: Zweck-Pflichtfeld (purpose) Prominenz; Format-Auswahl CSV/PDF vs. Spec CSV/NDJSON.
    • Subframe-Screen-Link(s):
    • Offene Fragen an Dev: Job-Liste/:id-Status als eigene D-P3-Screens nachziehen oder Single-Form-Ansatz beibehalten?

Notifications

NotificationCenterPage · Notification-Center (A2 + Drawer)

  • Interim-Screen: apps/web/app/components/screens/notifications/NotificationCenterPage.tsx
  • Route(n): /app/:tenant/notifications (routes/app.$tenant/notifications.tsx) — Route hostet SSE-Subscription (useNotificationsStream).
  • Daten (VM): NotificationCenterVM (Items über NotificationCenterItemVM) — packages/view-models/src/notifications/notification-center.vm.ts
  • Benötigte UI-Elemente (KEIN Layout): Alert · BadgeFeedback · Button · EmptyState · Tabs (+ Tabs.Item). Inhaltselemente: Header (Titel + Diagnostics-Subtitle + sr-only Live-Region für connectionStatusKey), Tabs Alle/Ungelesen mit Counts, Bulk-Clear-Button (nur bei unreadCount>0), Notification-Cards (Message als Deeplink-Link oder Text, Kategorie·Channel·Zeit-Metazeile, Status-Badge farbcodiert READ/FAILED/PENDING, Bounce-Alert bei bounceKey mit lastErrorRedacted, Mark-Read-Button bei ungelesen). Icons: keine.
  • Interaktionen (Actions): onMarkRead(deliveryId: string) · onMarkAllRead(). Tab-Umschaltung Alle/Ungelesen ist lokal (useState).
  • States: Loading = vm.status !== "ready" → leerer Marker-Div · Empty = totalCount===0 → EmptyState (emptyHeadlineKey/emptyBodyKey) · Unread-Empty = gefilterte Liste leer → EmptyState (unreadEmptyKey) · Permission-Denied = Route-Level (Auth-Middleware), kein In-Component-403.
  • Davids Delta (offen):
    • Überschriebene/geänderte Spec-Punkte: D-P3 nennt Prioritäts-Marker + Mark-unread — Interim hat nur Mark-read (kein Priority-Marker, kein Unread-Zurücksetzen).
    • Neue UI/UX-Entscheidungen: Bounce-Klartext-Darstellung („Emails an dich kommen aktuell nicht an") als Error-Alert je Card.
    • Subframe-Screen-Link(s):
    • Offene Fragen an Dev: Priority-Marker-Datenfeld im VM ergänzen?

NotificationsDrawer · Notification-Center Topbar-Drawer (A2 + Drawer)

  • Interim-Screen: apps/web/app/components/screens/notifications/NotificationsDrawer.tsx
  • Route(n): n/a — Shell-Level-Komponente (globaler Drawer, gehostet von apps/web/app/components/layouts/NavBar.tsx; NavBar besitzt SSE-Subscription + Open-State).
  • Daten (VM): NotificationCenterItemVM (kein eigener VM; Items als Prop, max 10) — packages/view-models/src/notifications/notification-center.vm.ts
  • Benötigte UI-Elemente (KEIN Layout): BadgeFeedback · Button · Drawer (+ Drawer.Content) · EmptyState. Inhaltselemente: Header (Titel + Close-Button), Item-Liste (max 10; Message als Deeplink oder Text, ungelesen fett, Zeitstempel, „Ungelesen"-Badge bei nicht gelesen), „Alle anzeigen"-Link (allHref). Icons: keine.
  • Interaktionen (Actions): KEIN …Actions-Typ — Props open, items, allHref, onClose(). Deeplink-Klicks folgen validierten deeplinkHref; „Alle anzeigen" navigiert zu allHref.
  • States: Empty = visibleItems.length===0 → EmptyState (drawer.empty + emptyHelper) · kein eigenes Loading/Permission-Denied (Shell steuert Sichtbarkeit).
  • Davids Delta (offen):
    • Überschriebene/geänderte Spec-Punkte: kein Mark-read/Bulk-Clear im Drawer (nur „Alle anzeigen" → Center).
    • Neue UI/UX-Entscheidungen: Verhältnis Bell-Slot (D-D0a) ↔ Drawer-Öffnung; 10-Item-Cap.
    • Subframe-Screen-Link(s):
    • Offene Fragen an Dev: Soll der Drawer Inline-Mark-read bekommen oder bewusst read-only bleiben?

NotificationSettingsPage · Notification-Settings (A6)

  • Interim-Screen: apps/web/app/components/screens/notifications/NotificationSettingsPage.tsx
  • Route(n): /app/:tenant/settings/notifications (routes/app.$tenant/settings/notifications.tsx) — Actions fetcher-backed.
  • Daten (VM): NotificationSettingsVM (Channel-Typ NotificationSettingChannel) — packages/view-models/src/notifications/notification-settings.vm.ts
  • Benötigte UI-Elemente (KEIN Layout): Alert · Switch (+ lokaler ChannelToggle-Wrapper). Inhaltselemente: Header (Heading + Description), Save-State-Alert, Master-Karte „alle Emails aus" (Switch), pro Modul-Gruppe eine Karte mit Modul-Label + je Kategorie In-App/Email-Toggle. Icons: keine.
  • Interaktionen (Actions): onTogglePreference({ category, channel, enabled }) · onSetEmailMaster(disableAllEmails: boolean) (Master-Switch schreibt alle Kategorien). Zusätzliche Prop saveState: { ok; errorText? } | null.
  • States: Loading = vm.status !== "ready" → leerer Marker-Div · Save = saveState.ok → Success-Alert / sonst Error-Alert (errorText) · saveState===null → nichts · kein Empty-State (Registry liefert immer Gruppen) · Permission-Denied = Route-Level.
  • Davids Delta (offen):
    • Überschriebene/geänderte Spec-Punkte: D-P3 nennt Priority-Selektor je Event-Typ — Interim hat nur In-App/Email-Toggles, keinen Priority-Selektor.
    • Neue UI/UX-Entscheidungen: Master-Toggle-Klartext „betrifft auch zukünftige Event-Typen".
    • Subframe-Screen-Link(s):
    • Offene Fragen an Dev: Priority pro Kategorie im VM/Registry ergänzen?