Files
cve-dashboard/.kiro/specs/admin-page-overhaul/design.md

18 KiB

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.cssintel-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

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

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

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

// Internal to AdminPage
// State:
//   users: Array<User>
//   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

// Internal to AdminPage
// State:
//   logs: Array<AuditLogEntry>
//   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

// Internal to AdminPage
// State:
//   users: Array<User>
//   recentLogs: Array<AuditLogEntry>
//   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

// In App.js, replace:
//   {currentPage === 'admin' && isAdmin() && (
//     <div className="space-y-6">
//       <UserManagement onClose={() => setCurrentPage('home')} />
//     </div>
//   )}
//
// With:
//   {currentPage === 'admin' && isAdmin() && <AdminPage />}
//   {currentPage === 'admin' && !isAdmin() && /* redirect to home */}
//
// Keep existing modal triggers:
//   {showUserManagement && <UserManagement onClose={...} />}
//   {showAuditLog && <AuditLog onClose={...} />}

Data Models

User (from GET /api/users)

{
  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)

{
  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)

{
  logs: AuditLogEntry[],
  pagination: {
    page: number,
    limit: number,
    total: number,
    totalPages: number
  }
}

Tab Configuration

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

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

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