Files
motovaultpro/docs/changes/admin-implementation-plan.md
2025-11-06 16:29:11 -06:00

45 KiB

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:

// 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

export function useBulkSelection<T extends { id: string }>(items: T[]) {
  // Returns: {
  //   selected: Set<string>,
  //   toggleItem(id: string): void,
  //   toggleAll(items: T[]): void,
  //   isSelected(id: string): boolean,
  //   reset(): void,
  //   count: number,
  // }
}

File: frontend/src/features/admin/hooks/useAuditLogStream.ts

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:

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:

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:

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:

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

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:

// Desktop
<AuditLogPanel
  resourceType="catalog"
  onRefresh={() => refetch()}
  logs={logs}
  loading={loading}
  pagination={pagination}
  onNextPage={nextPage}
/>

// Mobile
<AuditLogDrawer
  open={open}
  onClose={onClose}
  resourceType="station"
  logs={logs}
  loading={loading}
/>

Audit Log Table Component

File: frontend/src/features/admin/components/AuditLogTable.tsx

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

export function AdminCatalogRoute() {
  const { isMobile } = useMediaQuery();

  return isMobile ? <AdminCatalogMobileScreen /> : <AdminCatalogPage />;
}

Repeat for AdminStationsRoute and AdminUsersRoute.

  1. Update Mobile Navigation

File: frontend/src/components/MobileBottomNav.tsx (or similar)

Add admin access check and menu expansion:

const { isAdmin } = useAdminAccess();

// In bottom nav items:
{
  isAdmin && (
    <NavItem label="Admin" icon={<AdminIcon />}>
      <NestedMenu>
        <NavLink to="/admin/catalog">Catalog</NavLink>
        <NavLink to="/admin/stations">Stations</NavLink>
        <NavLink to="/admin/users">Users</NavLink>
      </NestedMenu>
    </NavItem>
  )
}

Alternative (simpler): Add "Admin" to Settings submenu.

  1. Update Mobile Routes

File: frontend/src/routes.tsx or router configuration

{
  path: '/admin/catalog',
  element: <AdminCatalogRoute />,
  private: true,
  requiresAdmin: true,
},
{
  path: '/admin/stations',
  element: <AdminStationsRoute />,
  private: true,
  requiresAdmin: true,
},
{
  path: '/admin/users',
  element: <AdminUsersRoute />,
  private: true,
  requiresAdmin: true,
},
  1. 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:

// 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:

// 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:

// 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:

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.