# Design Document: Admin Page Overhaul ## Overview The Admin Page Overhaul replaces the current inline `UserManagement` modal rendering on the admin page with a full-page, themed admin panel. The new `AdminPage` component follows the same layout conventions as `CompliancePage`, `ExportsPage`, and `KnowledgeBasePage` — a top-level page component rendered in the main content area of `App.js` when `currentPage === 'admin'`. The page consolidates three admin functions into a single tabbed interface: 1. **User Management** — themed table with inline add/edit forms, group badges, and active status toggles 2. **Audit Log** — paginated, filterable log table with action-type badges and date range filters 3. **System Info** — stat cards showing user counts, audit log totals, and recent activity All sections use the dark tactical intelligence theme defined in `DESIGN_SYSTEM.md` and `App.css` — `intel-card` containers, `intel-button` controls, `intel-input` form fields, `status-badge` action labels, and `stat-card` stat displays. ### Design Decisions - **New component, not a wrapper.** The existing `UserManagement.js` and `AuditLog.js` are white-background modals with Tailwind utility classes. Wrapping them would create visual inconsistency. The `AdminPage` component builds themed versions of both panels from scratch, reusing the same backend API endpoints. - **Existing modals preserved.** The `UserMenu` quick-access links ("Manage Users", "Audit Log") continue to open the existing modal components. This keeps the quick-access workflow intact while the admin page provides the full-featured experience. - **No new backend endpoints.** All data comes from existing routes: `GET /api/users`, `POST/PATCH/DELETE /api/users/:id`, `GET /api/audit-logs`, `GET /api/audit-logs/actions`. - **Inline styles + App.css classes.** Follows the project convention of defining style constants in the component file and referencing `App.css` classes (`intel-card`, `intel-button`, `data-row`, etc.) where available. ## Architecture ```mermaid graph TD A[App.js] -->|currentPage === 'admin' && isAdmin| B[AdminPage] B --> C[Tab Navigation] C -->|"User Management"| D[UserManagementPanel] C -->|"Audit Log"| E[AuditLogPanel] C -->|"System Info"| F[SystemInfoPanel] D -->|GET /api/users| G[Backend: users.js] D -->|POST /api/users| G D -->|PATCH /api/users/:id| G D -->|DELETE /api/users/:id| G E -->|GET /api/audit-logs| H[Backend: auditLog.js] E -->|GET /api/audit-logs/actions| H F -->|GET /api/users| G F -->|GET /api/audit-logs?limit=10| H ``` ### Component Hierarchy ``` AdminPage ├── PageHeader (title + accent glow) ├── TabNavigation (User Management | Audit Log | System Info) ├── UserManagementPanel │ ├── AddUserButton / InlineForm │ ├── UserTable │ │ └── UserRow (group badge, status toggle, edit/delete actions) │ ├── ErrorBanner │ └── SuccessToast ├── AuditLogPanel │ ├── FilterBar (username, action, entity type, start date, end date) │ ├── LogTable │ │ └── LogRow (timestamp, user, action badge, entity, details, IP) │ ├── Pagination │ ├── EmptyState │ └── ErrorBanner └── SystemInfoPanel ├── StatCards (total users, active users, audit entries, recent logins) └── RecentActivityList (10 most recent audit entries) ``` ### Data Flow 1. `App.js` renders `` when `currentPage === 'admin'` and `isAdmin()` returns true. Non-admin users are redirected to home. 2. `AdminPage` manages the active tab in local state (default: `'users'`). 3. Each panel fetches its own data on mount using `fetch()` with `credentials: 'include'`. 4. Mutations (create, update, delete user) trigger a re-fetch of the user list. Success/error feedback is shown inline. 5. Audit log panel manages its own pagination and filter state, re-fetching on filter apply or page change. 6. System info panel fetches user list and recent audit logs on mount, computing derived stats client-side. ## Components and Interfaces ### AdminPage (main component) ```javascript // frontend/src/components/pages/AdminPage.js export default function AdminPage() { // Props: none (reads auth context internally) // State: // activeTab: 'users' | 'audit' | 'system' // Renders: PageHeader, TabNavigation, conditional panel } ``` ### TabNavigation ```javascript // Internal to AdminPage // Props: // activeTab: string // onTabChange: (tab: string) => void // Tabs: [ // { id: 'users', label: 'User Management', icon: Shield }, // { id: 'audit', label: 'Audit Log', icon: Clock }, // { id: 'system', label: 'System Info', icon: Activity }, // ] ``` Styling: monospace uppercase text, `--intel-accent` border and background on active tab, transparent with muted text on inactive tabs. Matches the tab pattern used in `CompliancePage` (team tabs). ### UserManagementPanel ```javascript // Internal to AdminPage // State: // users: Array // loading: boolean // error: string | null // showForm: boolean // editingUser: User | null // formData: { username, email, password, group } // formError: string // successMessage: string // // API calls: // GET /api/users → fetch all users // POST /api/users → create user // PATCH /api/users/:id → update user (fields, group, is_active) // DELETE /api/users/:id → delete user // // Group badge colors (themed): // Admin: --intel-danger (#EF4444) // Standard_User: --intel-accent (#0EA5E9) // Leadership: --intel-warning (#F59E0B) // Read_Only: --text-muted (#94A3B8) ``` ### AuditLogPanel ```javascript // Internal to AdminPage // State: // logs: Array // loading: boolean // error: string | null // pagination: { page, limit, total, totalPages } // filters: { user, action, entityType, startDate, endDate } // actions: string[] (populated from /api/audit-logs/actions) // // API calls: // GET /api/audit-logs?page=&limit=25&user=&action=&entityType=&startDate=&endDate= // GET /api/audit-logs/actions // // Action badge colors (themed): // login/logout: --intel-success (#10B981) // *_create: --intel-accent (#0EA5E9) // *_update/*_edit: --intel-warning (#F59E0B) // *_delete: --intel-danger (#EF4444) // default: --text-muted (#94A3B8) ``` ### SystemInfoPanel ```javascript // Internal to AdminPage // State: // users: Array // recentLogs: Array // loading: boolean // errors: { users: string | null, logs: string | null } // // Derived stats: // totalUsers: users.length // activeUsers: users.filter(u => u.is_active).length // recentLogins: users.filter(u => u.last_login && withinLast7Days(u.last_login)).length // totalAuditEntries: fetched from audit-logs pagination.total // // API calls: // GET /api/users // GET /api/audit-logs?limit=10&page=1 ``` ### Integration with App.js ```javascript // In App.js, replace: // {currentPage === 'admin' && isAdmin() && ( //
// setCurrentPage('home')} /> //
// )} // // With: // {currentPage === 'admin' && isAdmin() && } // {currentPage === 'admin' && !isAdmin() && /* redirect to home */} // // Keep existing modal triggers: // {showUserManagement && } // {showAuditLog && } ``` ## Data Models ### User (from GET /api/users) ```javascript { id: number, username: string, email: string, group: 'Admin' | 'Standard_User' | 'Leadership' | 'Read_Only', is_active: 0 | 1, created_at: string, // ISO datetime last_login: string | null // ISO datetime } ``` ### AuditLogEntry (from GET /api/audit-logs) ```javascript { id: number, user_id: number, username: string, action: string, // e.g. 'login', 'user_create', 'cve_delete' entity_type: string, // e.g. 'auth', 'user', 'cve', 'document' entity_id: string | null, details: string | null, // JSON string ip_address: string | null, created_at: string // ISO datetime } ``` ### AuditLogPagination (from GET /api/audit-logs response) ```javascript { logs: AuditLogEntry[], pagination: { page: number, limit: number, total: number, totalPages: number } } ``` ### Tab Configuration ```javascript const TABS = [ { id: 'users', label: 'User Management', icon: Shield }, { id: 'audit', label: 'Audit Log', icon: Clock }, { id: 'system', label: 'System Info', icon: Activity }, ]; ``` ### Group Badge Theme Map ```javascript const GROUP_BADGE_THEMED = { Admin: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' }, Standard_User: { bg: 'rgba(14,165,233,0.15)', border: '#0EA5E9', text: '#7DD3FC' }, Leadership: { bg: 'rgba(245,158,11,0.15)', border: '#F59E0B', text: '#FCD34D' }, Read_Only: { bg: 'rgba(148,163,184,0.15)', border: '#94A3B8', text: '#CBD5E1' }, }; ``` ### Action Badge Theme Map ```javascript const ACTION_BADGE_THEMED = { login: { bg: 'rgba(16,185,129,0.15)', border: '#10B981', text: '#6EE7B7' }, logout: { bg: 'rgba(148,163,184,0.15)', border: '#94A3B8', text: '#CBD5E1' }, login_failed: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' }, user_create: { bg: 'rgba(14,165,233,0.15)', border: '#0EA5E9', text: '#7DD3FC' }, user_update: { bg: 'rgba(245,158,11,0.15)', border: '#F59E0B', text: '#FCD34D' }, user_delete: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' }, cve_create: { bg: 'rgba(14,165,233,0.15)', border: '#0EA5E9', text: '#7DD3FC' }, cve_edit: { bg: 'rgba(245,158,11,0.15)', border: '#F59E0B', text: '#FCD34D' }, cve_delete: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' }, document_upload: { bg: 'rgba(139,92,246,0.15)', border: '#8B5CF6', text: '#C4B5FD' }, document_delete: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' }, }; ``` ## Correctness Properties *A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* ### Property 1: Group badge color mapping is total and correct *For any* valid user group string (`Admin`, `Standard_User`, `Leadership`, `Read_Only`), the group badge styling function SHALL return a non-null object with `bg`, `border`, and `text` fields matching the themed color for that group. *For any* string that is not one of the four valid groups, the function SHALL return the default muted styling. **Validates: Requirements 3.3** ### Property 2: Edit form population preserves user data *For any* user object with arbitrary `username`, `email`, and `group` values, populating the edit form from that user SHALL result in `formData.username === user.username`, `formData.email === user.email`, and `formData.group === user.group`, with `formData.password` set to an empty string. **Validates: Requirements 3.5** ### Property 3: Self-modification prevention *For any* user list that contains the currently authenticated admin user, the admin user's own row SHALL have the group dropdown disabled and the active status toggle disabled. *For any* other user in the list, those controls SHALL be enabled. **Validates: Requirements 3.8** ### Property 4: Action badge color mapping is total and correct *For any* known audit log action string (from the set of defined actions: `login`, `logout`, `login_failed`, `user_create`, `user_update`, `user_delete`, `cve_create`, `cve_edit`, `cve_delete`, `document_upload`, `document_delete`), the action badge styling function SHALL return the correct themed color object. *For any* unknown action string, the function SHALL return the default muted styling. **Validates: Requirements 4.4** ### Property 5: Applying filters resets pagination to page 1 *For any* combination of filter values (username text, action type, entity type, start date, end date) and *for any* current page number, applying the filters SHALL result in a fetch call with `page=1`. **Validates: Requirements 4.7** ### Property 6: Recent login count computation *For any* list of user objects with random `last_login` timestamps (including null values), the computed "recent logins" count SHALL equal the number of users whose `last_login` is non-null and falls within the last 7 days from the current time. **Validates: Requirements 5.1** ### Property 7: Admin-only access control *For any* user object, the admin page content SHALL be rendered if and only if `user.group === 'Admin'`. When `user.group` is any value other than `'Admin'`, the system SHALL redirect to the home page. **Validates: Requirements 6.1, 6.2** ## Error Handling ### User Management Panel | Error Scenario | Handling | |---|---| | `GET /api/users` fails | Display error banner with `--intel-danger` styling. User table is hidden. Retry on next tab switch or manual refresh. | | `POST /api/users` fails (validation) | Display `formError` message below the form in danger color. Form remains open for correction. | | `POST /api/users` fails (409 conflict) | Display "Username or email already exists" in `formError`. | | `PATCH /api/users/:id` fails | Display inline error. Revert optimistic UI changes if any. | | `DELETE /api/users/:id` fails | Display alert with error message. User list unchanged. | | Self-demotion attempt | Group dropdown disabled for current user. Backend returns 400 if bypassed. | | Self-deactivation attempt | Toggle disabled for current user. Backend returns 400 if bypassed. | ### Audit Log Panel | Error Scenario | Handling | |---|---| | `GET /api/audit-logs` fails | Display error banner with `--intel-danger` styling. Table hidden. | | `GET /api/audit-logs/actions` fails | Action filter dropdown shows no options. Non-critical — silently ignored. | | Invalid date range (start > end) | Client-side: no validation needed — backend handles gracefully by returning empty results. | | Empty result set | Display "No audit log entries found" message in `--text-muted` color. | ### System Info Panel | Error Scenario | Handling | |---|---| | `GET /api/users` fails | Affected stat cards (total users, active users, recent logins) show "Unable to load" fallback text. | | `GET /api/audit-logs` fails | Audit entries stat card and recent activity list show "Unable to load" fallback. | | Partial failure (one endpoint fails) | Only the affected cards show fallback. Successfully loaded cards display normally. | ### Success Feedback - Create user: green success toast "User created successfully" auto-dismisses after 2 seconds. - Update user: green success toast "User updated successfully" auto-dismisses after 2 seconds. - Delete user: green success toast "User deleted" auto-dismisses after 2 seconds. - Toggle active status: immediate UI update, no toast (inline visual feedback is sufficient). ## Testing Strategy ### Unit Tests (Example-Based) Unit tests cover specific rendering, interaction, and integration scenarios: **AdminPage structure:** - Renders page header with "Admin Panel" title - Defaults to User Management tab on mount - Switches panels when tabs are clicked - Only renders when user is admin (access control) **UserManagementPanel:** - Renders user table with all required columns - Displays themed group badges for each group type - Shows inline form when "Add User" is clicked - Populates form with user data when edit is clicked - Shows confirmation dialog on delete - Disables self-modification controls for current user - Displays error banner on API failure - Displays success toast on successful operations **AuditLogPanel:** - Renders log table with all required columns - Displays themed action badges - Renders filter controls (username, action, entity type, dates) - Fetches page 1 when filters are applied - Navigates pages with pagination controls - Shows empty state when no results - Shows error banner on API failure **SystemInfoPanel:** - Renders four stat cards with correct labels - Computes derived stats correctly from mock data - Shows recent activity list with up to 10 entries - Shows fallback message when an API call fails ### Property-Based Tests Property-based tests use [fast-check](https://github.com/dubzzz/fast-check) to verify universal properties across generated inputs. Each test runs a minimum of 100 iterations. | Property | Test Description | Tag | |---|---|---| | Property 1 | Generate random group strings (valid + invalid), verify badge function returns correct colors | Feature: admin-page-overhaul, Property 1: Group badge color mapping is total and correct | | Property 2 | Generate random user objects, verify edit form population matches user fields exactly | Feature: admin-page-overhaul, Property 2: Edit form population preserves user data | | Property 3 | Generate random user lists containing the current admin, verify self-edit controls are disabled | Feature: admin-page-overhaul, Property 3: Self-modification prevention | | Property 4 | Generate random action strings (known + unknown), verify badge function returns correct colors | Feature: admin-page-overhaul, Property 4: Action badge color mapping is total and correct | | Property 5 | Generate random filter states and current page numbers, verify fetch is called with page=1 | Feature: admin-page-overhaul, Property 5: Applying filters resets pagination to page 1 | | Property 6 | Generate random user lists with random last_login timestamps, verify recent login count matches manual computation | Feature: admin-page-overhaul, Property 6: Recent login count computation | | Property 7 | Generate random user objects with random groups, verify admin page renders iff group === 'Admin' | Feature: admin-page-overhaul, Property 7: Admin-only access control | ### Test Configuration - **Library:** fast-check (JavaScript property-based testing) - **Runner:** Jest (via react-scripts test) - **Iterations:** Minimum 100 per property test (`fc.assert(property, { numRuns: 100 })`) - **Tag format:** Comment at top of each property test referencing the design property