227 lines
9.0 KiB
Markdown
227 lines
9.0 KiB
Markdown
|
|
# 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 (primitive)
|
||
|
|
|
||
|
|
Dropdown trigger that opens a popover 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 → popover 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 popover
|
||
|
|
- Click outside or Escape closes without applying
|
||
|
|
- Trigger shows count: "2 selected" or placeholder when empty
|
||
|
|
|
||
|
|
**Implementation:**
|
||
|
|
- New directory: `src/design-system/primitives/MultiSelect/`
|
||
|
|
- Uses Popover composite for positioning
|
||
|
|
- CSS Modules with design tokens
|
||
|
|
- No forwardRef needed (not a native form control)
|
||
|
|
|
||
|
|
### 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
|
||
|
|
confirmLabel?: string // default: "Delete"
|
||
|
|
cancelLabel?: string // default: "Cancel"
|
||
|
|
variant?: 'danger' | 'warning' // default: 'danger'
|
||
|
|
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 variant styling
|
||
|
|
|
||
|
|
**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 or blur saves. Escape cancels (reverts to original value).
|
||
|
|
- If saved value is empty and placeholder exists, display mode shows placeholder in muted style.
|
||
|
|
- No save/cancel buttons — Enter saves, Escape cancels (lightweight inline pattern).
|
||
|
|
|
||
|
|
**Implementation:**
|
||
|
|
- New directory: `src/design-system/primitives/InlineEdit/`
|
||
|
|
- Uses `forwardRef` (wraps an input)
|
||
|
|
- Manages internal editing state
|
||
|
|
|
||
|
|
## 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.
|
||
|
|
|
||
|
|
### Sidebar Integration
|
||
|
|
|
||
|
|
Add admin links to the Sidebar bottom links. The existing Sidebar component already supports `bottomLinks` — add entries for Audit Log, OIDC, and User Management.
|
||
|
|
|
||
|
|
Update the mock sidebar data in `src/mocks/sidebar.ts` to include admin navigation, and update the Sidebar usage across all example pages.
|
||
|
|
|
||
|
|
### 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** → PrimitivesSection: 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/primitives/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
|