# Admin Section Redesign Spec

**Date:** 2026-03-18
**Scope:** UX/UI consistency overhaul of AuditLog, OidcConfig, UserManagement admin pages

## Overview

Three expert reviews identified critical bugs, consistency gaps, and usability issues in the admin section. This spec covers all changes organized by priority tier.

---

## Tier 1: Critical Bugs

### 1.1 Replace nonexistent `--bg-base` token

`--bg-base` is referenced 3 times but does not exist in `tokens.css`. Dark mode is broken.

**Files:**
- `Admin.module.css` line 6: `.adminNav`
- `UserManagement.module.css` lines 13, 19: `.listPane`, `.detailPane`

**Fix:** Replace all `var(--bg-base)` with `var(--bg-surface)`.

### 1.2 Change AuditEvent `id` to string

`DataTable` requires `T extends { id: string }`. Current `AuditEvent.id` is `number`.

**Files:**
- `auditMocks.ts`: change `id: number` to `id: string`, update all IDs to `'audit-1'`, `'audit-2'`, etc.
- `AuditLog.tsx`: update `expandedId` state from `number | null` to `string | null`

---

## Tier 2: High-Impact Consistency

### 2.1 Replace admin nav with Tabs composite

The hand-rolled admin nav in `Admin.tsx` lacks ARIA roles and has subtle color differences from the Tabs composite.

**Fix:** Replace the custom `<nav>` block with:
```tsx
<Tabs
  tabs={[
    { label: 'User Management', value: '/admin/rbac' },
    { label: 'Audit Log', value: '/admin/audit' },
    { label: 'OIDC', value: '/admin/oidc' },
  ]}
  active={location.pathname}
  onChange={(path) => navigate(path)}
/>
```

Delete `.adminNav`, `.adminTab`, `.adminTabActive` from `Admin.module.css`.

### 2.2 Remove duplicate page titles

Breadcrumb + active tab + h2 heading all show the same label. Remove the h2.

**Files:**
- `AuditLog.tsx`: remove the `.header` div with `<h2>Audit Log</h2>`. Move the event count badge into a toolbar row.
- `OidcConfig.tsx`: remove the `.header` div with `<h2>`. Keep Save/Test buttons in a compact toolbar row below the tabs.

Delete `.header`, `.title` CSS from both `AuditLog.module.css` and `OidcConfig.module.css`.

### 2.3 Migrate AuditLog to DataTable

Replace the hand-built `<table>` with the DataTable composite.

**Column definitions:**
- Timestamp: render with `MonoText`, width `'170px'`
- User: render with bold text
- Category: render with `Badge color="auto"`
- Action: plain text
- Target: render with ellipsis style
- Result: render with `Badge color={row.result === 'SUCCESS' ? 'success' : 'error'}`

**Features to enable:**
- `sortable` on Timestamp, User, Category, Result columns
- `rowAccent={(row) => row.result === 'FAILURE' ? 'error' : undefined}` — red left-border on failures
- `expandedContent={(row) => <detail block with IP, user agent, CodeBlock>}`
- `pageSize={10}`
- `flush` prop (table sits inside a card wrapper)

**Card wrapper:** Wrap DataTable in a section with `background: var(--bg-surface)`, `border: 1px solid var(--border-subtle)`, `border-radius: var(--radius-lg)`, `box-shadow: var(--shadow-card)`. Add a header row with title + event count badge, matching Dashboard's `.tableSection` pattern.

**Delete from AuditLog.module.css:** `.tableWrap`, `.table`, `.th`, `.row`, `.td`, `.userCell`, `.target`, `.empty`, `.detailRow`, `.detailCell`, `.detailGrid`, `.detailField`, `.detailLabel`, `.detailValue`, `.detailJson`. Also remove the separate `Pagination` import — DataTable handles pagination internally.

### 2.4 Fix content padding

`Admin.module.css` `.adminContent`: change `padding: 20px` to `padding: 20px 24px 40px`.

### 2.5 Center OIDC form

`OidcConfig.module.css` `.page`: add `margin: 0 auto` to center the 640px max-width form.

### 2.6 Replace inline style

`UserManagement.tsx` line 20: replace `style={{ marginTop: 16 }}` with a CSS class `.tabContent { margin-top: 16px; }` in `UserManagement.module.css`.

---

## Tier 3: Usability Improvements

### 3.1 Add toast notifications to RBAC mutations

Import `useToast` into `UsersTab.tsx`, `GroupsTab.tsx`, `RolesTab.tsx`. Fire toasts on:
- Create user/group/role: `variant: 'success'`
- Delete user/group/role: `variant: 'warning'`
- Role assigned/removed: `variant: 'success'`
- Group added/removed: `variant: 'success'`

### 3.2 Rework user creation form

Replace the flat inline form with a provider-aware two-step form.

**Form structure:**
1. **Provider selection** — RadioGroup with "Local" and "OIDC" options. Default: "Local".
2. **Fields (always shown):** Username (required), Display name (optional), Email (optional)
3. **Fields (Local only):** Password (required)
4. **OIDC info callout:** When OIDC selected, show an InfoCallout: "OIDC users authenticate via the configured identity provider. Pre-register to assign roles/groups before their first login."

**Components used:** RadioGroup + RadioItem (existing primitive), Input, InfoCallout (existing primitive), Button.

**Create handler:** Set `provider` based on RadioGroup selection. Only validate password when provider is 'local'.

The form should use the existing inline pattern (appears at the top of the list pane), but use a proper card-like treatment (the existing `.createForm` background is fine).

### 3.3 Add password management to user detail pane

Add a "Security" section (using `SectionHeader`) in the user detail pane, below the metadata grid.

**For local users:**
- Show "Password: ••••••••" with a "Reset password" Button
- Clicking "Reset password" reveals an inline form: Input (type=password, placeholder "New password") + Cancel/Set buttons
- Setting fires a success toast: "Password updated"

**For OIDC users:**
- Show "Authentication: OIDC ({provider})"
- Show InfoCallout: "Password managed by the identity provider."
- No password reset option

### 3.4 Add ConfirmDialog to cascading removals

When removing a group from a user (which may strip inherited roles), show a confirmation dialog if the group grants roles.

When removing a role from a group (which affects all members), show a confirmation dialog.

Direct role removal from a user does not need confirmation (low risk).

### 3.5 Make entity list items keyboard accessible

Add to each `.entityItem` div:
- `role="option"`
- `tabIndex={0}`
- `aria-selected={selectedId === item.id}`
- `onKeyDown`: Enter/Space to select, ArrowUp/ArrowDown to navigate

Add `role="listbox"` and `aria-label` to each `.entityList` container.

### 3.6 Add expand/collapse affordance to AuditLog

After DataTable migration (2.3), add a first column with a chevron indicator (`>` / `v`) that rotates when the row is expanded. Width: `'40px'`. This makes the expandable row pattern discoverable.

### 3.7 Add duplicate name validation

Before creating, check for existing names:
- Users: `users.some(u => u.username === newUsername.trim())`
- Groups: `groups.some(g => g.name.toLowerCase() === newName.trim().toLowerCase())`
- Roles: `roles.some(r => r.name === newName.trim().toUpperCase())`

Show inline error using state + red text below the name field. Disable Create button.

### 3.8 Partial FilterBar migration for AuditLog

After DataTable migration, use FilterBar for search + category filters:
- Search input maps to FilterBar's built-in search
- Categories (INFRA, AUTH, USER_MGMT, CONFIG) become FilterPill toggles
- Keep DateRangePicker and user filter Input alongside FilterBar in a row

### 3.9 Add empty-search states to entity lists

When search returns no results in Users/Groups/Roles lists, show centered muted text: "No users match your search" (etc.) inside the `.entityList` area.

---

## Tier 4: Polish

### 4.1 Replace lock emoji with Badge

`RolesTab.tsx`: replace `🔒` with `<Badge label="system" color="auto" variant="outlined" />`.

### 4.2 Fix split-pane border radius

`UserManagement.module.css`: change `border-radius: var(--radius-md)` to `var(--radius-lg)` on `.splitPane`, `.listPane`, `.detailPane`.

### 4.3 Add shadow to split-pane

`UserManagement.module.css`: add `box-shadow: var(--shadow-card)` to `.splitPane`.

---

## Out of Scope

- Replacing split-pane with DataTable+DetailPanel (not appropriate for dense editing)
- EventFeed as alternative audit view (future enhancement)
- Tabs inside user detail pane (not needed until more sections are added)
- FilterBar extension to support DateRangePicker slots (separate design system ticket)
