feat(approvals): UI für Freigaben — Review-Queue mit Approve/Reject (Modul 1/16)

Backend:
- Phantom-Permission-Bug gefixt: approvals:read/write/approve fehlten in
  CORE_PERMISSIONS (Rollen konnten sie nie zugewiesen bekommen — gleiche
  Fehlerklasse wie dashboard:read in M2)

Frontend:
- api/approvals.ts: TanStack Hooks (list/detail/approve/reject/expire/create)
- pages/Approvals.tsx: Review-Queue — Status-Tabs (Offen/Alle/Genehmigt/
  Abgelehnt/Abgelaufen), Karten mit Aktion/Entity/Requester/Metadata,
  Approve/Reject mit Kommentar-Modal, Permission-Gating (approvals:approve)
- Route /approvals (PermissionRoute approvals:read), Sidebar-Eintrag
- i18n approvals.* + nav.approvals (de/en)

Verifikation: Vitest 10/10 (Rendering, Tabs, Approve/Reject-Flow,
Kommentar, Permission-Gating, Resolved-Zustände), RBAC-Regression 102/102,
tsc clean, Build OK
This commit is contained in:
Agent Zero
2026-09-08 23:26:56 +02:00
parent 4eb05d96ad
commit ecc7a24c1c
8 changed files with 708 additions and 3 deletions
+2
View File
@@ -60,6 +60,7 @@ const ImportExportPage = React.lazy(() => import('@/pages/ImportExport').then(m
const TagsPage = React.lazy(() => import('@/pages/Tags').then(m => ({ default: m.TagsPage })));
const CustomFieldsPage = React.lazy(() => import('@/pages/CustomFields').then(m => ({ default: m.CustomFieldsPage })));
const ActivityTimelinePage = React.lazy(() => import('@/pages/ActivityTimeline').then(m => ({ default: m.ActivityTimelinePage })));
const ApprovalsPage = React.lazy(() => import('@/pages/Approvals').then(m => ({ default: m.ApprovalsPage })));
const SettingsWebhooksPage = React.lazy(() => import('@/pages/SettingsWebhooks').then(m => ({ default: m.SettingsWebhooksPage })));
const SettingsBackupPage = React.lazy(() => import('@/pages/SettingsBackup').then(m => ({ default: m.SettingsBackupPage })));
const WorkspaceManagerPage = React.lazy(() => import('@/pages/SettingsWorkspaces').then(m => ({ default: m.WorkspaceManagerPage })));
@@ -256,6 +257,7 @@ const router = createBrowserRouter([
{ path: '/workflows', element: <PermissionRoute permission="workflows:read">{withSuspense(<WorkflowsPage />)}</PermissionRoute> },
{ path: '/import-export', element: <PermissionRoute permission="import_export:read">{withSuspense(<ImportExportPage />)}</PermissionRoute> },
{ path: 'tags', element: <PermissionRoute permission="tags:read">{withSuspense(<TagsPage />)}</PermissionRoute> },
{ path: '/approvals', element: <PermissionRoute permission="approvals:read">{withSuspense(<ApprovalsPage />)}</PermissionRoute> },
{ path: '/api-docs', element: <PermissionRoute permission="settings:read">{withSuspense(<ApiDocsPage />)}</PermissionRoute> },
{ path: '/activity', element: <PermissionRoute permission="audit:read">{withSuspense(<ActivityTimelinePage />)}</PermissionRoute> },
{ path: '/wiki', element: <PermissionRoute permission="wiki:read">{withSuspense(<WikiPage />)}</PermissionRoute> },