# Admin Settings Frontend Implementation Plan - Execution Document **Last Updated:** 2025-11-06 **Status:** Ready for Parallel Execution **Priority Order:** Vehicle Catalog → Station Oversight → Admin Users → Audit Logs --- ## Executive Summary This document is the canonical execution plan for implementing the admin settings UI across desktop and mobile. It is designed to enable multiple AI agents to work in parallel on different phases. **Current State:** - Frontend: All infrastructure (API hooks, types, auth gates) exists; pages are routed but use placeholder content - Backend: Phase 0 requires implementation of 5 missing batch operation endpoints **Key Decisions:** - Implement polling-based audit logs (5-second intervals) until SSE backend is ready - Use client-side batch loops until backend batch endpoints exist - All features MUST be implemented on both desktop and mobile - Mobile routing integration is Phase 5 (after UI implementation) --- ## Phase 0 - Backend Coordination (BLOCKING) **Status:** Investigation Complete **Owner:** Backend Team (Coordination) ### Required Backend Implementation The following endpoints are MISSING and must be created before bulk UI can be tested: ``` POST /api/admin/admins/bulk Request: { admins: [{ email: string, role?: 'admin' | 'super_admin' }] } Response: { created: AdminUser[], failed: { email: string, error: string }[] } PATCH /api/admin/admins/bulk-revoke Request: { auth0Subs: string[] } Response: { revoked: AdminUser[], failed: { auth0Sub: string, error: string }[] } PATCH /api/admin/admins/bulk-reinstate Request: { auth0Subs: string[] } Response: { reinstated: AdminUser[], failed: { auth0Sub: string, error: string }[] } DELETE /api/admin/catalog/{entity}/bulk-delete Path: entity = 'makes' | 'models' | 'years' | 'trims' | 'engines' Request: { ids: string[] } Response: { deleted: string[], failed: { id: string, error: string }[] } DELETE /api/admin/stations/bulk-delete Request: { ids: string[], force?: boolean } Response: { deleted: string[], failed: { id: string, error: string }[] } GET /api/admin/audit-logs/stream (SSE) Content-Type: text/event-stream Payload: { id: string, data: AdminAuditLog } ``` ### Interim Strategy Frontend agents can proceed with implementation using **client-side batch loops** against existing single-operation endpoints: - `PATCH /api/admin/admins/:auth0Sub/revoke` (revoke loop) - `DELETE /api/admin/catalog/makes/:makeId` (delete loop) - `DELETE /api/admin/stations/:stationId` (delete loop) When backend batch endpoints are ready, frontend simply swaps the API calls without changing UI logic. ### Polling Fallback for Audit Logs Until `GET /api/admin/audit-logs/stream` exists, use polling: ``` GET /api/admin/audit-logs?limit=50&offset=0&sort=createdAt:desc ``` --- ## Phase 1 - Vehicle Catalog (Priority 1) **Owner:** Any AI Agent **Dependency:** None (uses existing hooks) **Status:** Ready to Start ### Overview Implement the vehicle catalog management interface for both desktop and mobile with hierarchical navigation, multi-select bulk delete, and cascading delete warnings. ### Desktop UI - AdminCatalogPage.tsx **File:** `frontend/src/pages/admin/AdminCatalogPage.tsx` (currently placeholder) **Layout:** ``` ┌─ Header: "Vehicle Catalog" + Stats Cards ──────────────────────┐ │ Makes: 100 | Models: 500 | Years: 20 | Trims: 5000 | Engines: 8000 ├─ Main Content ──────────────────────────────────────────────────┤ │ ┌──────────────────────┐ ┌──────────────────────────────────┐ │ │ │ Left Panel: Tree │ │ Right Panel: DataGrid │ │ │ │ │ │ │ │ │ │ [✓] Makes (100) │ │ □ Name │ Created │ Updated │ │ │ │ ├─ [✓] Model (500) │ │ ─────────────────────────────────│ │ │ │ │ ├─ Year (20) │ │ □ Honda │ 2024 │ 2025 │ │ │ │ │ ├─ Trim (5000) │ │ □ Toyota │ 2024 │ 2025 │ │ │ │ │ └─ Engine (8000) │ │ □ Ford │ 2024 │ 2025 │ │ │ │ └─ [✓] Model 2 │ │ │ │ │ │ │ │ [Add] [Edit] [Delete] [Bulk Del] │ │ │ └──────────────────────┘ └──────────────────────────────────┘ │ │ │ │ ┌─ Audit Log Panel (Collapsible) ──────────────────────────────┐ │ │ Catalog Changes (Last 50) [collapse] │ │ ──────────────────────────────────────────────────────────── │ │ │ 2025-11-06 14:32 | admin@example.com | Created Make | Honda │ │ │ 2025-11-06 14:31 | admin@example.com | Deleted Make | Toyota │ │ └────────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────────┘ ``` **Key Features:** - Left panel: Hierarchical tree with expandable levels (Makes → Models → Years → Trims → Engines) - Right panel: Data grid for selected level with columns: [checkbox], Name, Created, Updated, Actions - Selection: - Individual checkbox for each row - "Select All" checkbox (selects across ALL pages, not just current page) - Toolbar buttons: Add, Edit, Delete (single), Bulk Delete - Breadcrumb: "Catalog > Makes > (Name)" to show current selection context - Timestamps: Show created/updated times with formatting - Audit log sidebar: Filtered to catalog-related actions only **Forms & Dialogs:** - Create/Edit form modal with validation (name required, parent context shown) - Confirm bulk delete dialog: "Delete X items? This will also delete Y dependent items. Continue?" - Loading states and error displays **API Hooks to Use:** ```typescript // From frontend/src/features/admin/hooks/useCatalog.ts useMakes() / useCreateMake() / useUpdateMake() / useDeleteMake() useModels() / useCreateModel() / useUpdateModel() / useDeleteModel() useYears() / useCreateYear() / useDeleteYear() useTrims() / useCreateTrim() / useUpdateTrim() / useDeleteTrim() useEngines() / useCreateEngine() / useUpdateEngine() / useDeleteEngine() ``` **State Management:** - Track selected level in URL or state (e.g., `?level=models&parentId=123`) - Implement `useBulkSelection` hook for multi-select state - Query invalidation strategy: Deleting a Make should invalidate Models, Years, Trims, Engines queries **Implementation Steps:** 1. Create shared admin components (Phase 1A) 2. Implement tree navigation panel 3. Implement data grid with selection 4. Wire forms and dialogs 5. Test all CRUD flows --- ### Mobile UI - AdminCatalogMobileScreen.tsx **File:** `frontend/src/features/admin/mobile/AdminCatalogMobileScreen.tsx` (currently placeholder) **Layout:** ``` ┌─ Header: "Catalog" + level indicator ─────────────────┐ │ Makes (100) [filters] [sort] │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌─ Card ──────────────────────────────────────────┐ │ │ │ □ Honda Civic │ │ │ │ Created: 2024-01-15 | Updated: 2025-11-01 │ │ │ │ Models: 25 │ │ │ │ [Edit] [Delete] [View Models →] │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ ┌─ Card ──────────────────────────────────────────┐ │ │ │ □ Honda CR-V │ │ │ │ Created: 2024-02-10 | Updated: 2025-11-02 │ │ │ │ Models: 18 │ │ │ │ [Edit] [Delete] [View Models →] │ │ │ └─────────────────────────────────────────────────┘ │ │ │ ├─ Sticky Bottom Action Bar (Multi-Select Mode) ────────┤ │ Selected: 3 [Delete] [Cancel] │ └─────────────────────────────────────────────────────────┘ ``` **Key Features:** - Drill-down navigation: List → Select item → View children → Navigate deeper - Card-based list showing entity name, metadata, count of children - Long-press or "Select" button to enter multi-select mode - Checkboxes appear in multi-select mode - Sticky bottom action bar: "Selected: N | [Delete] [Cancel]" - Quick action buttons: Edit, Delete (single), View Children - Breadcrumb navigation at top showing: "Catalog > Makes > Honda > Models" - Bottom sheet for bulk actions confirmation - "Recent Changes" sheet: Tap icon to open audit log filtered to catalog - Touch targets ≥44px **Mobile-Specific Patterns:** - Avoid cramped UI - use bottom sheets for actions, not inline toolbars - Swipe-to-delete option (optional, test in QA) - Collapsible breadcrumb if needed to save space - Loading skeleton cards **Implementation Steps:** 1. Build card component for catalog items 2. Implement drill-down navigation state 3. Add multi-select mode toggle 4. Wire sticky action bar 5. Add breadcrumb navigation 6. Integrate audit log bottom sheet --- ### Shared Components & Hooks (Phase 1A) **Create these components first - they'll be reused in all phases:** #### Hooks **File:** `frontend/src/features/admin/hooks/useBulkSelection.ts` ```typescript export function useBulkSelection(items: T[]) { // Returns: { // selected: Set, // toggleItem(id: string): void, // toggleAll(items: T[]): void, // isSelected(id: string): boolean, // reset(): void, // count: number, // } } ``` **File:** `frontend/src/features/admin/hooks/useAuditLogStream.ts` ```typescript export function useAuditLogStream(filters?: { resourceType?: string; actionType?: string; limit?: number; }) { // Uses polling (GET /api/admin/audit-logs) until SSE available // Returns: { // logs: AdminAuditLog[], // loading: boolean, // error: any, // pagination: { page: number, limit: number, total: number }, // hasMore: boolean, // nextPage(): void, // prevPage(): void, // refetch(): void, // } } ``` #### Components **File:** `frontend/src/features/admin/components/AdminSectionHeader.tsx` - Props: title, stats (array of { label, value }) - Displays: "Vehicle Catalog" with stats cards **File:** `frontend/src/features/admin/components/AdminDataGrid.tsx` - Props: rows, columns, onSelectChange, selectedIds, loading, error, toolbar slot - Features: Checkbox column, sortable headers, pagination controls - Reusable for all data tables **File:** `frontend/src/features/admin/components/SelectionToolbar.tsx` - Props: selectedCount, onSelectAll, onClear, children (action buttons) - Displays: "Selected: N | [Select All] [Clear] + custom actions" **File:** `frontend/src/features/admin/components/BulkActionDialog.tsx` - Props: open, title, message, items, onConfirm, onCancel, loading - For confirming destructive bulk actions **File:** `frontend/src/features/admin/components/AuditLogPanel.tsx` (Desktop) - Props: resourceType, filters - Sidebar panel showing paginated audit logs - Collapse/expand toggle **File:** `frontend/src/features/admin/components/AuditLogDrawer.tsx` (Mobile) - Props: resourceType, filters, open, onClose - Bottom sheet for mobile audit log view **File:** `frontend/src/features/admin/components/EmptyState.tsx` - Props: icon, title, description, action? - Reusable empty state display **File:** `frontend/src/features/admin/components/ErrorState.tsx` - Props: error, onRetry - Reusable error display with retry button **File:** `frontend/src/features/admin/components/AdminSkeleton.tsx` - Loading skeleton cards/rows for all views --- ### Bulk Delete Pattern (Interim - Client-Side Loops) Until backend batch endpoints exist, implement bulk operations like this: ```typescript async function bulkDeleteMakes(makeIds: string[]) { const results = { deleted: [], failed: [] }; for (const makeId of makeIds) { try { await deleteM Make(makeId); results.deleted.push(makeId); } catch (error) { results.failed.push({ makeId, error: error.message }); } } // Invalidate queries to refresh UI queryClient.invalidateQueries({ queryKey: ['makes'] }); queryClient.invalidateQueries({ queryKey: ['models'] }); // Cascade return results; } ``` When backend batch endpoints are ready, swap to: ```typescript const { deleted, failed } = await api.deleteMakesBulk(makeIds); ``` --- ### Testing Checklist (Phase 1) - [ ] Tree navigation works (expand/collapse, selection) - [ ] Data grid displays correctly with sorting - [ ] Select All checkbox selects across all pages - [ ] Bulk delete shows confirmation with cascade warning - [ ] Single delete works with confirmation - [ ] Create/Edit forms validate and submit - [ ] Mobile drill-down navigation works smoothly - [ ] Multi-select mode on mobile toggles correctly - [ ] Audit log panel updates (or shows cached data) - [ ] Loading/error states render properly - [ ] Touch targets ≥44px on mobile - [ ] Desktop ≥1280px and mobile ≤480px viewports tested --- ## Phase 2 - Station Oversight (Priority 2) **Owner:** Any AI Agent **Dependency:** Phase 1A components complete **Status:** Ready to Start After Phase 1A ### Overview Implement station management with soft/hard delete, restore functionality, and search/filter. ### Desktop UI - AdminStationsPage.tsx **File:** `frontend/src/pages/admin/AdminStationsPage.tsx` (currently placeholder) **Layout:** ``` ┌─ Header: "Station Management" + Stats ──────────────────────────┐ │ Total: 500 | Active: 480 | Deleted: 20 | Archived: 0 │ ├─ Search & Filters ─────────────────────────────────────────────┤ │ [Search by name...] [Active] [Deleted] [Archived] [✕ Clear All] │ ├─ DataGrid ─────────────────────────────────────────────────────┤ │ □ Name │ Address │ Status │ Modified │ CreatedBy │ │ ─────────────────────────────────────────────────────────────── │ │ □ Shell Station │ 123 Main St │ Active │ 2025-11-01│ admin1 │ │ □ BP Station │ 456 Oak Ave │ Deleted │ 2025-10-15│ admin2 │ │ □ Chevron │ 789 Pine Rd │ Active │ 2025-11-03│ admin1 │ │ │ │ [Add Station] [Delete] [Restore] [Hard Delete] [Export CSV] │ │ │ ├─ Detail Drawer (Right Side) ───────────────────────────────────┤ │ Shell Station [Close] │ │ ──────────────────────────────────────────────────────────── │ │ Address: 123 Main St, Springfield, IL 62701 │ │ Coordinates: 39.7817, -89.6501 │ │ Status: Active │ │ Created: 2024-01-15 by admin@example.com │ │ Last Modified: 2025-11-01 by admin@example.com │ │ [Edit] [Delete] [Hard Delete] │ │ │ ├─ Audit Log Panel (Collapsible) ────────────────────────────────┤ │ Station Events (Last 50) [collapse] │ │ ────────────────────────────────────────────────────────────── │ │ 2025-11-06 14:32 | admin1 | Hard deleted | Shell Station │ │ 2025-11-06 14:31 | admin2 | Deleted | BP Station │ └────────────────────────────────────────────────────────────────┘ ``` **Key Features:** - Search bar: Filter by station name - Filter chips: Active, Deleted, Archived (soft-deleted showing as "Deleted") - Multi-select with bulk delete (toggle soft/hard via dialog) - Single-item quick actions: Delete, Restore (if deleted), Hard Delete - Detail drawer on right showing full metadata - Status column shows: Active, Deleted (soft), or Archived - Toast notifications for hard deletes (warning) - Audit log sidebar filtered to station events - "Export CSV" button (stub for now) **Forms & Dialogs:** - Create/Edit station form (name, address, coordinates) - Confirm soft delete dialog: "This will soft-delete the station. Data can be restored." - Confirm hard delete dialog: "This will permanently delete the station and cannot be undone. Continue?" - Confirm restore dialog: "Restore this station to active?" **API Hooks:** ```typescript useStationOverview() / useCreateStation() / useUpdateStation() / useDeleteStation() ``` **Status & Delete Logic:** - `deletedAt === null` → "Active" - `deletedAt !== null` → "Deleted" (soft delete) - Hard delete: Pass `?force=true` to delete endpoint **Implementation Steps:** 1. Build data grid with search and filter chips 2. Implement detail drawer 3. Add create/edit form modal 4. Wire soft/hard delete dialogs 5. Add restore functionality 6. Integrate audit log panel 7. Test all flows --- ### Mobile UI - AdminStationsMobileScreen.tsx **File:** `frontend/src/features/admin/mobile/AdminStationsMobileScreen.tsx` (currently placeholder) **Layout:** ``` ┌─ Header: "Stations" + Filters ────────────────────┐ │ Filter: [All] [Active] [Deleted] [sort by ▼] │ ├───────────────────────────────────────────────────┤ │ │ │ ┌─ Card ──────────────────────────────────────┐ │ │ │ Shell Station [status: Active]│ │ │ 123 Main St, Springfield, IL 62701 │ │ │ │ Created: 2024-01-15 | Modified: 2025-11-01 │ │ │ │ [Edit] [Delete] [Restore] [Details →] │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ ┌─ Card ──────────────────────────────────────┐ │ │ │ BP Station [status: Deleted] │ │ │ 456 Oak Ave, Shelbyville, IL 62702 │ │ │ │ Created: 2024-02-20 | Modified: 2025-10-15 │ │ │ │ [Edit] [Delete] [Restore] [Details →] │ │ │ └─────────────────────────────────────────────┘ │ │ │ ├─ Sticky Bottom Action Bar (Multi-Select Mode) ──┤ │ Selected: 2 [Delete] [Cancel] │ └───────────────────────────────────────────────────┘ ``` **Key Features:** - Card-based list with station name, address, status badge - Filter tabs: All / Active / Deleted - Multi-select mode via long-press or "Select" button - Sticky bottom action bar when items selected - Quick action buttons: Edit, Delete, Restore, Details - Details bottom sheet: Full metadata and actions - Audit log bottom sheet: "Recent Changes" showing station events - Touch targets ≥44px **Implementation Steps:** 1. Build card component for stations 2. Add filter tab navigation 3. Implement multi-select mode 4. Wire sticky action bar 5. Create details bottom sheet 6. Add edit form modal 7. Integrate audit log --- ### Testing Checklist (Phase 2) - [ ] Search filters stations correctly - [ ] Filter chips work (Active, Deleted, Archived) - [ ] Multi-select and bulk delete work - [ ] Soft delete works (status changes to "Deleted") - [ ] Restore works (status back to "Active") - [ ] Hard delete confirms and removes permanently - [ ] Detail drawer shows correct metadata - [ ] Create/Edit forms validate - [ ] Audit log filters to station events - [ ] Toast shows for hard deletes - [ ] Mobile filter tabs work - [ ] Mobile quick actions respond correctly - [ ] Desktop and mobile tested at stated breakpoints --- ## Phase 3 - Admin Users (Priority 3) **Owner:** Any AI Agent **Dependency:** Phase 1A components + Phase 2 complete **Status:** Ready to Start After Phase 2 ### Overview Implement admin user management with invite, revoke, reinstate, and deletion with "last admin" protection. ### Desktop UI - AdminUsersPage.tsx **File:** `frontend/src/pages/admin/AdminUsersPage.tsx` (currently placeholder) **Layout:** ``` ┌─ Header: "Admin Users" + Stats ────────────────────────────────┐ │ Total: 5 | Active: 4 | Revoked: 1 │ ├─ Search & Actions ────────────────────────────────────────────┤ │ [Search by email...] [Invite Admin] [Revoke] [Reinstate] [Del] │ ├─ DataGrid ────────────────────────────────────────────────────┤ │ □ Email │ Role │ Status │ Created │ Last │ │ ─────────────────────────────────────────────────────────────── │ │ □ alice@example.com │ super_admin│ Active │ 2024-01-01│ now │ │ □ bob@example.com │ admin │ Active │ 2024-02-15│ 1h │ │ □ charlie@example.com│ admin │ Revoked │ 2024-03-01│ 5d │ │ □ diana@example.com │ admin │ Active │ 2025-01-01│ now │ │ │ ├─ Audit Log Panel (Collapsible) ───────────────────────────────┤ │ Admin Actions (Last 50) [collapse] │ │ ────────────────────────────────────────────────────────────── │ │ 2025-11-06 14:32 | alice | Revoked admin | charlie@example.com │ │ 2025-11-06 14:15 | alice | Invited admin | diana@example.com │ └──────────────────────────────────────────────────────────────────┘ ``` **Key Features:** - Search bar: Filter by email - Toolbar actions: Invite Admin, Revoke (bulk), Reinstate (bulk), Delete (bulk) - Multi-select with confirmation dialogs - Status column: Active or Revoked - Role column: admin or super_admin - Last Activity column: Relative time (e.g., "5m ago", "2d ago") - Last Admin Protection: Show error if trying to revoke/delete the last active admin - Bulk action confirmation: "Revoke N admins? They will lose access immediately." - Audit log sidebar: Filtered to admin actions **Forms & Dialogs:** - Invite Admin modal: - Email field (required, validated) - Role dropdown (admin / super_admin) - Submit button with loading state - Revoke Confirmation dialog: Show who will be revoked, warn if last admin - Reinstate Confirmation dialog: Confirm reinstate - Delete Confirmation dialog: "Delete N admins? This cannot be undone." **API Hooks:** ```typescript useAdmins() / useCreateAdmin() / useRevokeAdmin() / useReinstateAdmin() ``` **Last Admin Logic:** - Before revoke/delete, check if admin is the last active one - If revoke: Show error "Cannot revoke the last active admin" - UI should disable action or show clear message **Implementation Steps:** 1. Build data grid with email search 2. Implement invite form modal 3. Add revoke/reinstate/delete confirmation dialogs 4. Wire last admin protection 5. Add bulk action handling 6. Integrate audit log panel 7. Test all flows including last admin edge case --- ### Mobile UI - AdminUsersMobileScreen.tsx **File:** `frontend/src/features/admin/mobile/AdminUsersMobileScreen.tsx` (currently placeholder) **Layout:** ``` ┌─ Header: "Admins" + Filter ────────────────────────┐ │ Filter: [All] [Active] [Revoked] [Invite + ] │ ├────────────────────────────────────────────────────┤ │ │ │ ┌─ Card ────────────────────────────────────────┐ │ │ │ alice@example.com [super_admin] │ │ │ Status: Active | Created: 2024-01-01 │ │ │ Last Activity: Now │ │ │ [Revoke] [Delete] [Details →] │ │ └────────────────────────────────────────────────┘ │ │ │ │ ┌─ Card ────────────────────────────────────────┐ │ │ │ bob@example.com [admin] │ │ │ Status: Active | Created: 2024-02-15 │ │ │ Last Activity: 1h ago │ │ │ [Revoke] [Delete] [Details →] │ │ └────────────────────────────────────────────────┘ │ │ │ ├─ Sticky Bottom Action Bar (Multi-Select Mode) ───┤ │ Selected: 2 [Revoke] [Delete] [Cancel] │ └────────────────────────────────────────────────────┘ ``` **Key Features:** - Card-based list: Email, role badge, status, created date, last activity - Filter tabs: All / Active / Revoked - "Invite +" button in header for quick access - Multi-select via long-press or "Select" button - Sticky bottom action bar: Revoke, Delete, Cancel - Quick action buttons: Revoke, Delete, Details - Details bottom sheet: Full metadata, role, created by, last activity - Invite modal: Email + role selection - Confirmation for revoke/delete - Last admin protection warning - Touch targets ≥44px **Implementation Steps:** 1. Build card component for admins 2. Add filter tab navigation 3. Wire invite form modal 4. Implement multi-select mode 5. Add sticky action bar with revoke/delete 6. Create details bottom sheet 7. Add confirmation dialogs with last admin check 8. Integrate audit log --- ### Testing Checklist (Phase 3) - [ ] Search filters admins by email - [ ] Invite form validates email and role - [ ] Invite submits successfully - [ ] Revoke shows confirmation and updates status - [ ] Reinstate shows confirmation and updates status - [ ] Delete confirms and removes admin - [ ] Last admin protection prevents revoke/delete (shows error) - [ ] Bulk operations work with confirmation - [ ] Audit log filters to admin actions - [ ] Mobile filter tabs and quick actions work - [ ] Details bottom sheet shows full info - [ ] Desktop and mobile tested at stated breakpoints - [ ] Role badges display correctly (admin vs super_admin) --- ## Phase 4 - Audit Log Streaming (Priority 4) **Owner:** Any AI Agent **Dependency:** Phases 1-3 complete **Status:** Ready to Start After Phase 3 ### Overview Implement real-time (polling-based) audit log display across all admin pages with filtering and search. ### Implementation Strategy **Polling Approach (Until SSE Backend):** - Use `GET /api/admin/audit-logs?limit=50&offset=0&sort=createdAt:desc` - Poll every 5 seconds - Cache results in React Query - Show "Last updated: X seconds ago" **Hook Implementation** **File:** `frontend/src/features/admin/hooks/useAuditLogStream.ts` ```typescript interface AuditLogStreamOptions { resourceType?: 'admin' | 'catalog' | 'station'; actionType?: 'CREATE' | 'UPDATE' | 'DELETE' | 'REVOKE' | 'REINSTATE'; limit?: number; pollIntervalMs?: number; } export function useAuditLogStream(options: AuditLogStreamOptions = {}) { // Returns: { // logs: AdminAuditLog[], // loading: boolean, // error: any, // pagination: { offset: number, limit: number, total: number }, // hasMore: boolean, // nextPage(): void, // prevPage(): void, // refetch(): void, // lastUpdated: Date, // } } ``` **Component Integration** Update `AuditLogPanel.tsx` and `AuditLogDrawer.tsx` to use the hook: ```typescript // Desktop refetch()} logs={logs} loading={loading} pagination={pagination} onNextPage={nextPage} /> // Mobile ``` **Audit Log Table Component** **File:** `frontend/src/features/admin/components/AuditLogTable.tsx` ```typescript interface AuditLogTableProps { logs: AdminAuditLog[]; loading?: boolean; pagination?: PaginationState; onNextPage?: () => void; onPrevPage?: () => void; filters?: { actor?: string; action?: string; resource?: string; }; onFilterChange?: (filters: any) => void; } // Display columns: Timestamp | Actor | Action | Resource | Details // Sortable by timestamp // Paginated with prev/next buttons // Show "Last updated: 5s ago" ``` **Filter Implementation** Add filter controls to audit log UI: - Actor (actor admin email) - Action (CREATE, DELETE, REVOKE, etc.) - Resource Type (admin, make, station, etc.) - Date range picker (optional, start with basic) **Integration Across Pages** Update Phase 1-3 pages to wire audit logs: 1. **AdminCatalogPage:** Filter `resourceType: 'catalog'` 2. **AdminStationsPage:** Filter `resourceType: 'station'` 3. **AdminUsersPage:** Filter `resourceType: 'admin'` --- ### Testing Checklist (Phase 4) - [ ] useAuditLogStream polls correctly every 5s - [ ] Logs display in correct time order (newest first) - [ ] Filters work (resourceType, actionType, actor) - [ ] Pagination controls work (next/prev) - [ ] "Last updated" timestamp shows current time - [ ] Loading state shows skeleton - [ ] Error state shows with retry button - [ ] Mobile drawer opens/closes - [ ] Desktop panel collapses/expands - [ ] Real-time updates visible when actions performed on other tabs - [ ] Audit log accessible only to admins (no leakage) --- ## Phase 5 - Mobile Routing Integration **Owner:** Any AI Agent **Dependency:** Phases 1-4 complete **Status:** Ready to Start After Phase 4 ### Overview Wire mobile admin screens into the mobile navigation system. **Current Mobile Navigation:** - Bottom navigation bar with fixed items: Dashboard, Vehicles, Log Fuel, Stations, Documents, Settings - Admin screens currently exist but are not routable **Changes Required:** 1. **Create Route Wrappers** (Optional but Recommended) **File:** `frontend/src/features/admin/routes/AdminCatalogRoute.tsx` ```typescript export function AdminCatalogRoute() { const { isMobile } = useMediaQuery(); return isMobile ? : ; } ``` Repeat for `AdminStationsRoute` and `AdminUsersRoute`. 2. **Update Mobile Navigation** **File:** `frontend/src/components/MobileBottomNav.tsx` (or similar) Add admin access check and menu expansion: ```typescript const { isAdmin } = useAdminAccess(); // In bottom nav items: { isAdmin && ( }> Catalog Stations Users ) } ``` Alternative (simpler): Add "Admin" to Settings submenu. 3. **Update Mobile Routes** **File:** `frontend/src/routes.tsx` or router configuration ```typescript { path: '/admin/catalog', element: , private: true, requiresAdmin: true, }, { path: '/admin/stations', element: , private: true, requiresAdmin: true, }, { path: '/admin/users', element: , private: true, requiresAdmin: true, }, ``` 4. **Verify Route Protection** - All admin routes should check `useAdminAccess()` and redirect to `/garage/settings` if not admin - Routes should not render until auth check completes **Implementation Steps:** 1. Create route wrapper components 2. Update mobile bottom navigation 3. Add routes to router configuration 4. Test navigation on mobile viewport 5. Verify auth protection --- ### Testing Checklist (Phase 5) - [ ] Admin menu items visible to admins only - [ ] Route navigation works on mobile - [ ] Back button returns to previous screen - [ ] Auth redirects non-admins correctly - [ ] All 3 admin screens accessible from mobile nav - [ ] Returning to mobile nav from admin screen works - [ ] Viewport ≤480px tested --- ## Phase 6 - Testing & QA **Owner:** Any AI Agent (or dedicated QA) **Dependency:** All phases 1-5 complete **Status:** Ready to Start After Phase 5 ### Unit Tests **File:** `frontend/src/features/admin/__tests__/` Test new hooks: ```typescript // useBulkSelection.test.ts - Selecting/deselecting items - Select all / clear all - Toggle item - Reset state // useAuditLogStream.test.ts - Polling interval (mock timer) - Parsing paginated response - Filter by resourceType - Pagination state updates - Error handling and retry ``` **Test Setup:** - Mock API calls using MSW (Mock Service Worker) - Mock React Query cache - Use `renderHook` from testing library - Mock EventSource for SSE upgrade testing ### Component Tests **File:** `frontend/src/features/admin/__tests__/` Desktop components: ```typescript // AdminDataGrid.test.tsx - Render rows and columns - Select/deselect rows - Select all checkbox - Toolbar slot renders - Sorting works - Pagination controls // BulkActionDialog.test.tsx - Confirm button triggers callback - Cancel closes dialog - Loading state disables buttons - Message displays correctly // AdminCatalogPage.test.tsx - Tree navigation expands/collapses - Grid updates when level selected - Create form validates - Delete shows confirmation - Bulk delete loops through items ``` Mobile components: ```typescript // AdminCatalogMobileScreen.test.tsx - Card list renders - Drill-down navigation - Multi-select mode toggle - Bottom sheet opens/closes - Breadcrumb updates // AuditLogDrawer.test.tsx - Opens/closes - Displays logs - Pagination works ``` **Test Coverage Goal:** >80% for admin feature ### Integration Tests **File:** `frontend/src/features/admin/__tests__/integration/` End-to-end flows: ```typescript describe('Admin Catalog E2E', () => { test('Select multiple makes, confirm delete, verify API called', async () => { // 1. Render AdminCatalogPage // 2. Select 3 makes via checkboxes // 3. Click Bulk Delete button // 4. Confirm in dialog // 5. Verify deleteM ake() called 3 times // 6. Verify query invalidated // 7. Verify grid updated }); test('Cascade warning shows when deleting make with models', async () => { // 1. Select make that has 5 models // 2. Click delete // 3. Confirm dialog shows "This will delete 5 models" }); }); describe('Admin Users E2E', () => { test('Last admin protection prevents revoke', async () => { // 1. Try to revoke last active admin // 2. Show error message // 3. Button disabled or error shown }); test('Bulk revoke multiple admins', async () => { // 1. Select 3 non-last admins // 2. Click Bulk Revoke // 3. Confirm dialog // 4. Verify all revoked }); }); describe('Admin Stations E2E', () => { test('Soft delete then restore station', async () => { // 1. Delete station // 2. Verify status is "Deleted" // 3. Click restore // 4. Verify status is "Active" }); test('Hard delete shows warning', async () => { // 1. Click hard delete // 2. Confirm shows warning // 3. After delete, toast shows critical warning }); }); ``` ### Manual QA Matrix **Test Environments:** - Desktop: Chrome, Firefox, Safari at ≥1280px - Mobile: iOS Safari, Android Chrome at ≤480px - Tablet: iPad at 768px (optional) **Critical Flows to Test:** | Flow | Desktop | Mobile | Notes | |------|---------|--------|-------| | Invite admin, verify in list | ✓ | ✓ | Check email sends (stub for now) | | Revoke admin, verify status | ✓ | ✓ | Check audit log shows action | | Bulk revoke 3 admins | ✓ | ✓ | Confirm all revoked | | Last admin protection | ✓ | ✓ | Try to revoke/delete last admin | | Create catalog make | ✓ | ✓ | Check hierarchy updates | | Bulk delete 5 makes | ✓ | ✓ | Check models/years cascade invalidated | | Cascade warning on delete | ✓ | ✓ | Delete make with 10 models, show warning | | Delete station (soft) | ✓ | ✓ | Check status changes to "Deleted" | | Restore station | ✓ | ✓ | Check status back to "Active" | | Hard delete station | ✓ | ✓ | Check permanent removal, toast warning | | Audit log filters | ✓ | ✓ | Filter by resource type, action | | Audit log pagination | ✓ | ✓ | Page through logs | | Mobile navigation | - | ✓ | Admin menu access from settings | | Search/filter on desktop | ✓ | - | Catalog search, station search | **Accessibility Checklist:** - [ ] Touch targets ≥44px on mobile - [ ] Keyboard navigation on desktop (Tab, Enter, Esc) - [ ] Focus indicators visible - [ ] Color contrast WCAG AA compliant - [ ] Screen reader compatible (alt text, labels, ARIA) - [ ] No console errors or warnings ### Browser Compatibility - Chrome 90+ - Firefox 88+ - Safari 14+ - Mobile Safari (iOS 14+) - Android Chrome 90+ --- ## Parallel Execution Strategy This document enables parallel development by multiple AI agents. Here's how: ### Team 1: Shared Components (Phase 1A) - Build all reusable components - Types and hooks - Expected output: Reviewed and merged shared component library ### Team 2: Vehicle Catalog (Phase 1) - Desktop and mobile implementation - Depends on Phase 1A - Parallel with Team 3 after Phase 1A complete ### Team 3: Station Oversight (Phase 2) - Desktop and mobile implementation - Depends on Phase 1A - Can start while Team 2 finishes Phase 1 ### Team 4: Admin Users (Phase 3) - Desktop and mobile implementation - Depends on Phases 1-2 - Starts when Teams 2-3 complete ### Team 5: Audit Logs & Mobile Routing (Phases 4-5) - Integrates polling + streaming - Mobile routing setup - Depends on Phases 1-3 - Can start when Teams 2-4 in progress ### Team 6: Testing & QA (Phase 6) - Unit and integration tests - Manual QA matrix - Runs in parallel with Teams 2-5 after their components merge - Creates final sign-off --- ## Success Criteria All phases complete when: - ✅ All linters pass (ESLint, TypeScript) - Zero issues - ✅ All tests pass (>80% coverage for admin feature) - ✅ Feature works end-to-end on desktop (≥1280px) and mobile (≤480px) - ✅ Old placeholder code deleted - ✅ No console errors or warnings - ✅ Manual QA matrix completed - ✅ Code reviewed and merged to main --- ## Known Risks & Mitigations | Risk | Mitigation | |------|-----------| | Backend batch endpoints delayed | Use client-side loops (already implemented pattern); swap endpoints when ready | | SSE not implemented | Poll audit logs every 5s; upgrade when backend ready; feature flag if needed | | Mobile routing not clear | Implement route wrappers with `useMediaQuery` for viewport detection | | Query invalidation cache issues | Use structured queryKeys; test cascade deletes thoroughly | | Performance with large lists | Implement virtualization if >500 items; use pagination | | Last admin constraint edge case | Disable action and show clear error message; test thoroughly | | Touch targets <44px on mobile | Verify all buttons/interactable elements in QA phase | --- ## Questions / Blockers If any AI agent encounters: - Missing API endpoint → Document in Phase 0 summary; use interim workaround - Component design unclear → Reference existing designs in codebase - Type mismatch → Check `admin.types.ts` for canonical types - Routing issues → Check `src/routes.tsx` for routing pattern --- ## Appendix: File Checklist ### New Files to Create ``` frontend/src/features/admin/components/ ├── AdminSectionHeader.tsx ├── AdminDataGrid.tsx ├── SelectionToolbar.tsx ├── BulkActionDialog.tsx ├── AuditLogPanel.tsx (Desktop) ├── AuditLogDrawer.tsx (Mobile) ├── AuditLogTable.tsx ├── EmptyState.tsx ├── ErrorState.tsx └── AdminSkeleton.tsx frontend/src/features/admin/hooks/ ├── useBulkSelection.ts (NEW) └── useAuditLogStream.ts (NEW - extends existing hooks) frontend/src/features/admin/routes/ (Optional, Phase 5) ├── AdminCatalogRoute.tsx ├── AdminStationsRoute.tsx └── AdminUsersRoute.tsx frontend/src/features/admin/__tests__/ ├── useBulkSelection.test.ts ├── useAuditLogStream.test.ts ├── AdminDataGrid.test.tsx ├── BulkActionDialog.test.tsx ├── AdminCatalogPage.test.tsx ├── AdminCatalogMobileScreen.test.tsx ├── AdminStationsPage.test.tsx ├── AdminStationsMobileScreen.test.tsx ├── AdminUsersPage.test.tsx ├── AdminUsersMobileScreen.test.tsx └── integration/ ├── catalog.e2e.test.ts ├── stations.e2e.test.ts └── users.e2e.test.ts ``` ### Files to Update ``` frontend/src/pages/admin/ ├── AdminCatalogPage.tsx (Replace placeholder) ├── AdminStationsPage.tsx (Replace placeholder) └── AdminUsersPage.tsx (Replace placeholder) frontend/src/features/admin/mobile/ ├── AdminCatalogMobileScreen.tsx (Replace placeholder) ├── AdminStationsMobileScreen.tsx (Replace placeholder) └── AdminUsersMobileScreen.tsx (Replace placeholder) frontend/src/routes.tsx (Add mobile admin routes, Phase 5) frontend/src/components/MobileBottomNav.tsx (Add admin menu, Phase 5) ``` ### Existing Files (No Changes) ``` frontend/src/features/admin/api/admin.api.ts (Ready to use) frontend/src/features/admin/hooks/useAdmins.ts (Ready to use) frontend/src/features/admin/hooks/useCatalog.ts (Ready to use) frontend/src/features/admin/hooks/useStationOverview.ts (Ready to use) frontend/src/features/admin/types/admin.types.ts (Ready to use) frontend/src/core/auth/useAdminAccess.ts (Ready to use) ``` --- ## Document History | Date | Author | Changes | |------|--------|---------| | 2025-11-06 | AI Planning | Initial creation for parallel execution | --- **Next Step:** Assign agents to Phase 1A (Shared Components) to unblock all subsequent phases.