# Admin Pages + New Components Design

**Date:** 2026-03-18
**Scope:** 3 new design system components + 3 admin example pages

## Overview

Transfer the admin section from cameleer3-server UI to the design system project as example pages. Add three new reusable components to the design system that are needed by these pages and useful generally.

## New Design System Components

### 1. MultiSelect (composite)

Dropdown trigger that opens a positioned panel with searchable checkbox list and "Apply" action.

**Props:**
```typescript
interface MultiSelectOption {
  value: string
  label: string
}

interface MultiSelectProps {
  options: MultiSelectOption[]
  value: string[]
  onChange: (value: string[]) => void
  placeholder?: string       // default: "Select..."
  searchable?: boolean       // default: true
  disabled?: boolean
  className?: string
}
```

**Behavior:**
- Click trigger → panel opens below with search input + checkbox list + "Apply (N)" footer
- Search filters options by label (case-insensitive)
- Checkboxes toggle selection; changes are local until "Apply" is clicked
- Apply calls `onChange` with selected values and closes panel
- Click outside or Escape closes without applying (discards pending changes)
- Trigger shows count: "2 selected" or placeholder when empty
- Arrow keys navigate checkbox list, Space toggles focused item, Tab moves between search/list/apply
- Panel has max-height with scroll for long option lists

**Accessibility:**
- Trigger: `role="combobox"`, `aria-expanded`, `aria-haspopup="listbox"`
- Option list: `role="listbox"`, options have `role="option"` with `aria-selected`
- Search input: `aria-label="Filter options"`

**Implementation:**
- New directory: `src/design-system/composites/MultiSelect/`
- Manages its own open/close state and positioning (does NOT wrap Popover — needs controlled close behavior to distinguish apply vs. discard)
- Uses portal for the dropdown panel to avoid overflow clipping
- CSS Modules with design tokens

### 2. ConfirmDialog (composite)

Modal dialog requiring the user to type a confirmation string before a destructive action proceeds.

**Props:**
```typescript
interface ConfirmDialogProps {
  open: boolean
  onClose: () => void
  onConfirm: () => void
  title?: string                            // default: "Confirm Deletion"
  message: string                           // e.g., "Delete user 'alice'? This cannot be undone."
  confirmText: string                       // text the user must type to enable confirm button (must be non-empty)
  confirmLabel?: string                     // default: "Delete"
  cancelLabel?: string                      // default: "Cancel"
  variant?: 'danger' | 'warning' | 'info'  // default: 'danger'
  loading?: boolean                         // default: false — disables buttons, shows pending state
  className?: string
}
```

**Behavior:**
- Built on Modal (size="sm")
- Shows title, message, text input with label "Type '{confirmText}' to confirm"
- Confirm button disabled until input matches `confirmText` exactly
- Input clears on open
- Enter submits when enabled; Escape closes
- Confirm button uses danger/warning/info variant styling (matches AlertDialog pattern)
- When `loading` is true, both buttons are disabled

**Implementation:**
- New directory: `src/design-system/composites/ConfirmDialog/`
- Reuses Modal internally (same pattern as AlertDialog)
- Auto-focuses input on open

### 3. InlineEdit (primitive)

Click-to-edit text field that toggles between display and edit modes.

**Props:**
```typescript
interface InlineEditProps {
  value: string
  onSave: (value: string) => void
  placeholder?: string        // shown when value is empty in display mode
  disabled?: boolean
  className?: string
}
```

**Behavior:**
- **Display mode:** Shows value as text with subtle edit icon (pencil). Clicking text or icon enters edit mode.
- **Edit mode:** Input field with current value. Enter saves. Escape cancels (reverts to original value). Blur cancels (same as Escape — prevents accidental saves when clicking away).
- If saved value is empty and placeholder exists, display mode shows placeholder in muted style.
- No save/cancel buttons — Enter saves, Escape/blur cancels (lightweight inline pattern).

**Implementation:**
- New directory: `src/design-system/primitives/InlineEdit/`
- No forwardRef — the component manages its own input internally (the input only exists in edit mode, so a forwarded ref would be null in display mode)
- Manages internal editing state with useState

## Admin Pages

### Route Structure

```
/admin          → redirects to /admin/rbac
/admin/audit    → AuditLog page
/admin/oidc     → OidcConfig page
/admin/rbac     → UserManagement page (tabs: Users | Groups | Roles)
```

All pages use the standard AppShell + Sidebar + TopBar layout with breadcrumbs.

### Router Integration

Update `App.tsx` to replace the single `/admin` route with nested routes:
```tsx
<Route path="/admin" element={<Navigate to="/admin/rbac" replace />} />
<Route path="/admin/audit" element={<AuditLog />} />
<Route path="/admin/oidc" element={<OidcConfig />} />
<Route path="/admin/rbac" element={<UserManagement />} />
```

### Sidebar Integration

Keep the existing single "Admin" bottom link in the Sidebar. The admin pages handle their own sub-navigation internally via a secondary nav bar at the top of each admin page (links to Audit Log, OIDC, User Management). This avoids cluttering the main sidebar with admin sub-entries.

### Barrel Export Updates

- Add `MultiSelect` and `MultiSelectOption` type to `src/design-system/composites/index.ts`
- Add `ConfirmDialog` and `ConfirmDialogProps` type to `src/design-system/composites/index.ts`
- Add `InlineEdit` and `InlineEditProps` type to `src/design-system/primitives/index.ts`

### Page: Audit Log (`src/pages/Admin/AuditLog/`)

**Layout:** Full-width content area (no split pane).

**Sections:**
1. **Header** — "Audit Log" title + event count badge
2. **Filter bar** — DateRangePicker (from/to), Input (user), Select (category: INFRA/AUTH/USER_MGMT/CONFIG), Input (search)
3. **Data table** — DataTable with columns: Timestamp (monospace), User, Category (Badge), Action, Target, Result (Badge with success/error color)
4. **Expandable rows** — Clicking a row reveals detail section with IP address, user agent, and JSON detail (CodeBlock)
5. **Pagination** — Pagination component below table

**Mock data:** ~30 audit events with varied categories, actions, results.

### Page: OIDC Config (`src/pages/Admin/OidcConfig/`)

**Layout:** Single-column form layout, max-width constrained.

**Sections:**
1. **Header** — "OIDC Configuration" + Save/Test/Delete buttons
2. **Behavior** — Two Toggle fields (Enabled, Auto Sign-Up) wrapped in FormField
3. **Provider Settings** — FormField-wrapped Inputs for Issuer URI, Client ID, Client Secret (type=password)
4. **Claim Mapping** — FormField-wrapped Inputs for Roles Claim, Display Name Claim with hint text
5. **Default Roles** — Tag list (removable) + Input + Button to add new roles
6. **Delete** — Button (danger) that opens ConfirmDialog

**Mock data:** Pre-filled form state with sample OIDC config.

### Page: User Management (`src/pages/Admin/UserManagement/`)

**Layout:** Tabs component at top (Users | Groups | Roles). Each tab has a CSS grid split-pane (roughly 52/48).

#### Users Tab
- **Left pane:** Input search + "Add user" Button + scrollable user list. Each item: Avatar + name + provider Badge + meta (email, group path) + Tag list (roles: amber, groups: green, inherited: dashed Badge).
- **Inline create form:** Appears at top of list when "Add user" clicked. Input fields for username, display, email, password. Cancel/Create buttons.
- **Right pane (detail):** Large Avatar + InlineEdit (display name) + email + Delete Button. Metadata fields (Status, ID as MonoText, Created). SectionHeader "Group membership" + Tag list (removable) + MultiSelect to add groups. SectionHeader "Effective roles" + Tag list (direct: solid, inherited: dashed with source label) + MultiSelect to add roles.
- **Delete:** ConfirmDialog (type username to confirm).

#### Groups Tab
- **Left pane:** Same pattern — search + "Add group" + group list. Each item: Avatar (square) + name + meta (parent, child count, member count) + role Tags.
- **Inline create form:** Name input + parent Select (top-level or existing group).
- **Right pane:** Avatar + InlineEdit (name) + hierarchy label. Metadata (ID, Parent — editable via Select). SectionHeader "Members" + Tag list. SectionHeader "Child groups" + Tag list. SectionHeader "Assigned roles" + removable Tags + MultiSelect. SectionHeader "Hierarchy" with indented tree display.
- **Delete:** ConfirmDialog.

#### Roles Tab
- **Left pane:** Search + "Add role" + role list. Each item: Avatar (square) + name + lock icon if system + meta (description, assignment count) + Tags.
- **Inline create form:** Name, Description, Scope inputs.
- **Right pane:** Avatar + role name (non-editable for system roles) + description. Metadata (ID, Scope, Type). SectionHeader "Assigned to groups" (read-only list). SectionHeader "Assigned to users (direct)" (read-only). SectionHeader "Effective principals" with inherited entries in dashed style.
- **Delete:** ConfirmDialog (only for non-system roles).

**Mock data:** ~8 users, ~4 groups (with nesting), ~6 roles (including system roles ADMIN, USER). Realistic role/group assignments with inheritance.

### Inventory Updates

Add demos for all three new components:

- **MultiSelect** → CompositesSection: Demo showing multi-select with sample options, displaying selected count
- **InlineEdit** → PrimitivesSection: Demo showing display/edit toggle with a sample name
- **ConfirmDialog** → CompositesSection: Demo with a "Delete item" button that opens the dialog

## File Structure

```
src/design-system/composites/MultiSelect/
  MultiSelect.tsx
  MultiSelect.module.css
  MultiSelect.test.tsx

src/design-system/primitives/InlineEdit/
  InlineEdit.tsx
  InlineEdit.module.css
  InlineEdit.test.tsx

src/design-system/composites/ConfirmDialog/
  ConfirmDialog.tsx
  ConfirmDialog.module.css
  ConfirmDialog.test.tsx

src/pages/Admin/
  AuditLog/
    AuditLog.tsx
    AuditLog.module.css
    auditMocks.ts
  OidcConfig/
    OidcConfig.tsx
    OidcConfig.module.css
  UserManagement/
    UserManagement.tsx
    UserManagement.module.css
    UsersTab.tsx
    GroupsTab.tsx
    RolesTab.tsx
    rbacMocks.ts
```

## Out of Scope

- No backend API integration (static mock data with useState)
- No persistence across page refresh
- No access control / role gating in the example app
- Dashboard tab from RBAC is excluded per user request
- Split pane is page-local CSS, not a design system component
