# Cameleer UI Interaction Patterns Audit Audit date: 2026-04-09 Scope: All `.tsx` files under `ui/src/pages/` and `ui/src/components/` --- ## 1. Delete / Destructive Operations ### 1.1 Delete User - **File**: `ui/src/pages/Admin/UsersTab.tsx` (lines 155-172, 358-365, 580-587) - **Button location**: Detail pane header, top-right, inline with avatar and name - **Button**: `` - **Confirmation**: `ConfirmDialog` (type-to-confirm) - Message: `Delete user "${name}"? This cannot be undone.` - Confirm text: user's `displayName` - Has `loading` prop bound to mutation - **Self-delete guard**: Button is `disabled={isSelf}` (cannot delete yourself) - **Toast on success**: `variant: 'warning'`, title: "User deleted" - **Toast on error**: `variant: 'error'`, `duration: 86_400_000` ### 1.2 Remove User From Group (via User detail) - **File**: `ui/src/pages/Admin/UsersTab.tsx` (lines 588-613) - **Button location**: Tag `onRemove` handler on group tags in detail pane - **Confirmation**: `AlertDialog` (simple confirm, no type-to-confirm) - Title: "Remove group membership" - Description: "Removing this group may also revoke inherited roles. Continue?" - Confirm label: "Remove" - Variant: `warning` - **Toast on success**: `variant: 'success'`, title: "Group removed" ### 1.3 Remove Role From User (via User detail) - **File**: `ui/src/pages/Admin/UsersTab.tsx` (lines 504-528) - **Button location**: Tag `onRemove` handler on role tags in detail pane - **Confirmation**: NONE -- immediate mutation on tag remove click - **Toast on success**: `variant: 'success'`, title: "Role removed" **INCONSISTENCY**: Removing a group shows an AlertDialog confirmation but removing a role does not, even though both can have cascading effects. ### 1.4 Delete Group - **File**: `ui/src/pages/Admin/GroupsTab.tsx` (lines 140-155, 340-347, 434-441) - **Button location**: Detail pane header, top-right - **Button**: `` - **Confirmation**: `ConfirmDialog` (type-to-confirm) - Message: `Delete group "${name}"? This cannot be undone.` - Confirm text: group's `name` - Has `loading` prop - **Built-in guard**: Button is `disabled={isBuiltinAdmins}` - **Toast on success**: `variant: 'warning'`, title: "Group deleted" ### 1.5 Remove Role From Group - **File**: `ui/src/pages/Admin/GroupsTab.tsx` (lines 404-427, 442-455) - **Button location**: Tag `onRemove` handler on role tags in group detail - **Confirmation**: `AlertDialog` shown ONLY when the group has members (conditional) - Title: "Remove role from group" - Description: `Removing this role will affect ${members.length} member(s) who inherit it. Continue?` - Confirm label: "Remove" - Variant: `warning` - **If group has no members**: Immediate mutation, no confirmation - **Toast on success**: `variant: 'success'`, title: "Role removed" ### 1.6 Remove Member From Group - **File**: `ui/src/pages/Admin/GroupsTab.tsx` (lines 366-372) - **Button location**: Tag `onRemove` handler on member tags in group detail - **Confirmation**: NONE -- immediate mutation on tag remove click - **Toast on success**: `variant: 'success'`, title: "Member removed" ### 1.7 Delete Role - **File**: `ui/src/pages/Admin/RolesTab.tsx` (lines 93-110, 261-265, 223-231) - **Button location**: Detail pane header, top-right - **Button**: `` - **Confirmation**: `ConfirmDialog` (type-to-confirm) - Message: `Delete role "${name}"? This cannot be undone.` - Confirm text: role's `name` - Has `loading` prop - **System role guard**: Button hidden for system roles (`!role.system`) - **Toast on success**: `variant: 'warning'`, title: "Role deleted" ### 1.8 Delete Environment - **File**: `ui/src/pages/Admin/EnvironmentsPage.tsx` (lines 101-112, 245-252, 319-327) - **Button location**: Detail pane header, top-right - **Button**: `` - **Confirmation**: `ConfirmDialog` (type-to-confirm) - Message: `Delete environment "${displayName}"? All apps and deployments in this environment will be removed. This cannot be undone.` - Confirm text: environment's `slug` (NOT the display name) - Has `loading` prop - **Default guard**: Button is `disabled={isDefault}` (cannot delete default environment) - **Toast on success**: `variant: 'warning'`, title: "Environment deleted" **NOTE**: The confirm text requires the slug but the message shows the display name. This is intentional (slug is the unique identifier) but differs from Users/Groups/Roles which use the display name. ### 1.9 Delete OIDC Configuration - **File**: `ui/src/pages/Admin/OidcConfigPage.tsx` (lines 113-124, 253-264) - **Button location**: Bottom of page in a "Danger Zone" section - **Button**: `` - **Confirmation**: `ConfirmDialog` (type-to-confirm) - Message: `Delete OIDC configuration? All users signed in via OIDC will lose access.` - Confirm text: `"delete oidc"` (static string) - NO `loading` prop - **Toast on success**: `variant: 'warning'`, title: "Configuration deleted" **INCONSISTENCY**: No `loading` prop on this ConfirmDialog, unlike all other delete confirmations. ### 1.10 Delete App - **File**: `ui/src/pages/AppsTab/AppsTab.tsx` (lines 533-539, 565, 589-596) - **Button location**: App detail header, top-right, in `detailActions` div alongside "Upload JAR" - **Button**: `` - **Confirmation**: `ConfirmDialog` (type-to-confirm) - Message: `Delete app "${displayName}"? All versions and deployments will be removed. This cannot be undone.` - Confirm text: app's `slug` - Has `loading` prop - **Toast on success**: `variant: 'warning'`, title: "App deleted" - **Post-delete**: Navigates to `/apps` ### 1.11 Stop Deployment - **File**: `ui/src/pages/AppsTab/AppsTab.tsx` (lines 526-531, 672) - **Button location**: Inline in deployments table, right-aligned actions column - **Button**: `` - **Confirmation**: NONE -- immediate mutation on click - **Toast on success**: `variant: 'warning'`, title: "Deployment stopped" **INCONSISTENCY**: Stopping a deployment is a destructive operation that affects live services but has NO confirmation dialog. Route stop/suspend in RouteControlBar uses a ConfirmDialog, but deployment stop does not. ### 1.12 Stop/Suspend Route - **File**: `ui/src/pages/Exchanges/RouteControlBar.tsx` (lines 43-154) - **Button location**: Route control bar (segmented button group) - **Button**: Custom segmented `` - **Confirmation**: NONE -- immediate call to `onDelete` then `onClose` - **Toast**: Handled by parent component (ExchangesPage) **INCONSISTENCY**: Deleting a tap from the TapConfigModal has no confirmation, but deleting from the RouteDetail table shows a ConfirmDialog. ### 1.15 Kill Database Query - **File**: `ui/src/pages/Admin/DatabaseAdminPage.tsx` (line 30) - **Button location**: Inline in active queries table - **Button**: `` - **Confirmation**: NONE -- immediate mutation - **Toast**: None visible **INCONSISTENCY**: Killing a database query is a destructive action with no confirmation and no toast feedback. --- ## 2. Button Placement & Order ### 2.1 Create Forms (Users, Groups, Roles, Environments) All four entity create forms use an identical pattern: | Page | File | Line | Left Button | Right Button | |------|------|------|-------------|--------------| | Users | `UsersTab.tsx` | 254-274 | Cancel (ghost) | Create (primary) | | Groups | `GroupsTab.tsx` | 251-268 | Cancel (ghost) | Create (primary) | | Roles | `RolesTab.tsx` | 142-159 | Cancel (ghost) | Create (primary) | | Environments | `EnvironmentsPage.tsx` | 181-194 | Cancel (ghost) | Create (primary) | - **Position**: Bottom of inline create form in the list pane - **Container class**: `styles.createFormActions` - **Order**: Cancel (left) | Create (right) -- **CONSISTENT** - **Variants**: Cancel = `ghost`, Create = `primary` -- **CONSISTENT** - **Size**: Both `sm` -- **CONSISTENT** ### 2.2 App Creation Page - **File**: `ui/src/pages/AppsTab/AppsTab.tsx` (lines 282-287) - **Position**: Top of page in `detailActions` header area - **Order**: Cancel (ghost, left) | Create & Deploy / Create (primary, right) - **Size**: Both `sm` - **CONSISTENT** with the pattern (Cancel left, Submit right) ### 2.3 OIDC Config Page (Toolbar) - **File**: `ui/src/pages/Admin/OidcConfigPage.tsx` (lines 130-137) - **Position**: Top toolbar - **Order**: Test Connection (secondary, left) | Save (primary, right) - **No Cancel button** -- form is always editable **NOTE**: This is the only admin page without a Cancel button or Edit mode toggle. ### 2.4 App Detail Header - **File**: `ui/src/pages/AppsTab/AppsTab.tsx` (lines 560-566) - **Position**: Top-right header area in `detailActions` - **Order**: Upload JAR (primary) | Delete App (danger) **NOTE**: The primary action (Upload) is on the LEFT and the destructive action (Delete) is on the RIGHT. ### 2.5 App Config Detail Page (AppConfigDetailPage) - **File**: `ui/src/pages/Admin/AppConfigDetailPage.tsx` (lines 308-319) - **Position**: Top toolbar - **Read mode**: Back (ghost) ... Edit (secondary) - **Edit mode**: Back (ghost) ... Save (default/no variant specified!) | Cancel (secondary) - **Order when editing**: Save (left) | Cancel (right) **INCONSISTENCY #1**: Save button has NO `variant` prop set -- it renders as default, not `primary`. Every other Save button uses `variant="primary"`. **INCONSISTENCY #2**: Button order is REVERSED from every other form. Here it is Save (left) | Cancel (right). Everywhere else it is Cancel (left) | Save (right). ### 2.6 App Config Sub-Tab (AppsTab ConfigSubTab) - **File**: `ui/src/pages/AppsTab/AppsTab.tsx` (lines 922-936) - **Position**: Top banner bar (editBanner) - **Read mode**: Banner text + Edit (secondary) - **Edit mode**: Banner text + Cancel (ghost) | Save Configuration (primary) - **Order when editing**: Cancel (left) | Save (right) -- **CONSISTENT** ### 2.7 Environment Default Resources / JAR Retention Sections - **File**: `ui/src/pages/Admin/EnvironmentsPage.tsx` (lines 437-446, 505-514) - **Position**: Bottom of section, right-aligned (`justifyContent: 'flex-end'`) - **Read mode**: Edit Defaults / Edit Policy (secondary) - **Edit mode**: Cancel (ghost) | Save (primary) -- **CONSISTENT** - **Size**: Both `sm` ### 2.8 User Password Reset - **File**: `ui/src/pages/Admin/UsersTab.tsx` (lines 407-431) - **Position**: Inline in Security section - **Order**: Cancel (ghost) | Set (primary) - **CONSISTENT** pattern (Cancel left, Submit right) ### 2.9 Tap Modal (TapConfigModal) - **File**: `ui/src/components/TapConfigModal.tsx` (lines 249-257) - **Position**: Modal footer - **Order (edit mode)**: Delete (danger, left, in `footerLeft`) | Cancel (secondary) | Save (primary) - **Order (create mode)**: Cancel (secondary) | Save (primary) - **No `size` prop specified** -- renders at default size **NOTE**: Uses `variant="secondary"` for Cancel, not `variant="ghost"` like create forms. ### 2.10 Tap Modal (RouteDetail inline version) - **File**: `ui/src/pages/Routes/RouteDetail.tsx` (lines 984-986) - **Position**: Modal footer (`tapModalFooter`) - **Order**: Cancel (secondary) | Save (primary) - **No `size` prop specified** - **CONSISTENT** with TapConfigModal ### 2.11 About Me Dialog - **File**: `ui/src/components/AboutMeDialog.tsx` (lines 14, 72) - **Uses `Modal` with built-in close button** (no explicit action buttons) - **Close via**: Modal `onClose` handler (X button and backdrop click) ### 2.12 Login Page - **File**: `ui/src/auth/LoginPage.tsx` (lines 176-184) - **Single button**: Sign in (primary, full width, submit type) - **Optional SSO button above**: Sign in with SSO (secondary) ### Summary of Button Order Patterns | Location | Cancel Side | Submit Side | Consistent? | |----------|------------|-------------|-------------| | User create form | Left (ghost) | Right (primary) | YES | | Group create form | Left (ghost) | Right (primary) | YES | | Role create form | Left (ghost) | Right (primary) | YES | | Env create form | Left (ghost) | Right (primary) | YES | | App create page | Left (ghost) | Right (primary) | YES | | Env Default Resources edit | Left (ghost) | Right (primary) | YES | | Env JAR Retention edit | Left (ghost) | Right (primary) | YES | | AppsTab config sub-tab edit | Left (ghost) | Right (primary) | YES | | User password reset | Left (ghost) | Right (primary) | YES | | TapConfigModal | Left (secondary) | Right (primary) | Variant mismatch | | RouteDetail tap modal | Left (secondary) | Right (primary) | Variant mismatch | | **AppConfigDetailPage** | **Left (NO variant)** | **Right (secondary)** | **REVERSED** | --- ## 3. Edit / Save Patterns ### 3.1 Users (UsersTab) - **Edit mode**: No explicit toggle. Display name uses `InlineEdit` (click-to-edit). Everything else is managed via tag add/remove. - **No Save/Cancel for the detail view** -- all changes are immediate mutations. - **Unsaved changes indicator**: N/A (no batched editing) - **On success**: Toast with `variant: 'success'` - **On error**: Toast with `variant: 'error'`, `duration: 86_400_000` (effectively permanent) ### 3.2 Groups (GroupsTab) - **Edit mode**: Name uses `InlineEdit`. All other changes (members, roles) are immediate mutations. - **Pattern**: Same as Users -- no batched edit mode. ### 3.3 Roles (RolesTab) - **Edit mode**: Read-only detail panel. No editing of role fields. - **Only action**: Delete ### 3.4 Environments (EnvironmentsPage) - **Edit mode (name)**: `InlineEdit` - **Edit mode (production/enabled toggles)**: Immediate mutations per toggle change - **Edit mode (Default Resources)**: Explicit Edit toggle (`setEditing(true)`) - Cancel/Save buttons appear at bottom-right - Resets form on cancel - No unsaved changes indicator - On success: Toast `variant: 'success'` - **Edit mode (JAR Retention)**: Same pattern as Default Resources - **On environment switch**: Both sub-sections auto-reset to read mode ### 3.5 OIDC Config (OidcConfigPage) - **Edit mode**: ALWAYS editable (no toggle) - **Save button**: Always visible in top toolbar - **No Cancel button** -- cannot discard changes - **No unsaved changes indicator** - **On success**: Toast `variant: 'success'` - **On error**: Toast `variant: 'error'` + inline `` both shown **INCONSISTENCY**: Only page that is always editable with no way to discard changes. Also the only page that shows BOTH a toast AND an inline alert on error. ### 3.6 App Config Detail (AppConfigDetailPage) - **Edit mode**: Explicit toggle via `Edit` button (Pencil icon) in toolbar - **Toolbar in edit mode**: Save (unstyled!) | Cancel (secondary) - **Save button text**: Shows "Saving..." while pending - **No unsaved changes indicator** - **On success**: Toast `variant: 'success'`, exits edit mode - **On error**: Toast `variant: 'error'`, stays in edit mode ### 3.7 App Config Sub-Tab (AppsTab ConfigSubTab) - **Edit mode**: Explicit toggle via banner + Edit button - **Banner in read mode**: "Configuration is read-only. Enter edit mode to make changes." - **Banner in edit mode**: "Editing configuration. Changes are not saved until you click Save." (styled differently with `editBannerActive`) - **This IS an unsaved changes indicator** (the banner text changes) - **Cancel/Save in edit banner**: Cancel (ghost) | Save Configuration (primary) - **On success**: Toast `variant: 'success'`, exits edit mode, shows redeploy notice - **On error**: Toast `variant: 'error'`, stays in edit mode ### 3.8 App Create Page - **Edit mode**: N/A (always a creation form) - **Multi-step indicator**: Shows step text like "Creating app...", "Uploading JAR..." during submission - **On success**: Toast `variant: 'success'`, navigates to app detail page - **On error**: Toast `variant: 'error'` with step context ### 3.9 Tap Editing (TapConfigModal + RouteDetail inline) - **Edit mode**: Modal opens for edit or create - **Save/Cancel**: In modal footer - **On success**: Modal closes, parent handles toast - **On error**: Parent handles toast ### Summary of Edit Patterns | Page | Explicit Edit Toggle? | Unsaved Changes Indicator? | Consistent? | |------|----------------------|---------------------------|-------------| | Users | No (inline edits) | N/A | N/A | | Groups | No (inline edits) | N/A | N/A | | Roles | No (read-only) | N/A | N/A | | Environments - name | No (InlineEdit) | N/A | OK | | Environments - resources | YES | No | Missing | | Environments - JAR retention | YES | No | Missing | | OIDC Config | No (always editable) | No | Deviation | | AppConfigDetailPage | YES | No | Missing | | AppsTab ConfigSubTab | YES (banner) | YES (banner text) | Best pattern | **INCONSISTENCY**: The AppsTab ConfigSubTab is the only one with a proper unsaved-changes indicator. AppConfigDetailPage (which edits the same data for a different entry point) has no such indicator. --- ## 4. Toast / Notification Patterns ### 4.1 Toast Provider - **File**: `ui/src/components/LayoutShell.tsx` (line 783) - **Provider**: `` from `@cameleer/design-system` wraps the entire app layout - **Hook**: `useToast()` returns `{ toast }` function ### 4.2 Toast Call Signature All toast calls use the same shape: ```typescript toast({ title: string, description?: string, variant: 'success' | 'error' | 'warning', duration?: number }) ``` ### 4.3 Toast Variants Used | Variant | Used For | Duration | |---------|----------|----------| | `success` | Successful operations | Default (auto-dismiss) | | `error` | Failed operations | `86_400_000` (24 hours = effectively permanent) | | `warning` | Destructive successes (delete, stop) AND partial failures | Mixed (see below) | ### 4.4 Duration Patterns - **Success toasts**: No explicit duration (uses design system default) -- **CONSISTENT** - **Error toasts**: Always `duration: 86_400_000` -- **CONSISTENT** (49 occurrences across 10 files) - **Warning toasts for deletion success** (user/group/role/env/OIDC/app deleted): No explicit duration (auto-dismiss) -- **CONSISTENT** - **Warning toasts for partial push failures**: `duration: 86_400_000` -- **CONSISTENT** ### 4.5 Naming Conventions for Toast Titles **Success pattern**: Action-noun format - "User created", "Group created", "Role created", "Environment created" - "Display name updated", "Password updated", "Group renamed" - "Config saved", "Configuration saved", "Tap configuration saved" **Error pattern**: "Failed to [action]" format - "Failed to create user", "Failed to delete group", "Failed to update password" - "Save failed", "Upload failed", "Deploy failed" (shorter form) **INCONSISTENCY**: Error messages mix two patterns: 1. "Failed to [verb] [noun]" (e.g., "Failed to create user") -- used in RBAC pages 2. "[Noun] failed" (e.g., "Save failed", "Upload failed") -- used in AppsTab, AppConfigDetailPage ### 4.6 Warning Variant for Deletions Successful deletions use `variant: 'warning'` consistently: - "User deleted" (UsersTab:162) - "Group deleted" (GroupsTab:147) - "Role deleted" (RolesTab:100) - "Environment deleted" (EnvironmentsPage:105) - "Configuration deleted" (OidcConfigPage:119) - "App deleted" (AppsTab:536) - "Deployment stopped" (AppsTab:529) **CONSISTENT** -- all destructive-but-successful operations use warning. --- ## 5. Loading / Empty States ### 5.1 Full-Page Loading States | Page | Component | Size | Wrapper | |------|-----------|------|---------| | UsersTab | `` | md | Bare return | | GroupsTab | `` | md | Bare return | | RolesTab | `` | md | Bare return | | EnvironmentsPage | `` | md | Bare return | | AppListView | `` | md | Bare return | | AppDetailView | `` | md | Bare return | | AgentInstance | `` | **lg** | Bare return | | AppConfigDetailPage | `` | **lg** | Wrapped in `div.loading` | | DashboardPage | `` | lg | Centered container | | RuntimePage | `` | lg | Centered container | | OidcConfigPage | `return null` | N/A | Returns nothing | **INCONSISTENCY #1**: Most admin pages use `` as a bare return. AgentInstance and AppConfigDetailPage use `size="lg"`. DashboardPage and RuntimePage use the `` component which wraps `` in a centered container. **INCONSISTENCY #2**: OidcConfigPage returns `null` while loading (shows a blank page), unlike every other page. **INCONSISTENCY #3**: SplitPane detail loading (GroupsTab line 317, RolesTab line 212) uses `` -- consistent within that context. ### 5.2 Section Loading States - **RouteDetail charts**: `` inline in chart containers (lines 713, 804) - **AboutMeDialog**: `` in a `div.loading` wrapper ### 5.3 Empty States | Context | Pattern | Component Used | |---------|---------|----------------| | SplitPane list (no search match) | `emptyMessage="No X match your search"` | EntityList built-in | | SplitPane detail (nothing selected) | `emptyMessage="Select a X to view details"` | SplitPane built-in | | Deployments table (none) | `

No deployments yet.

` | Plain `

` | | Versions list (none) | `

No versions uploaded yet.

` | Plain `

` | | Env vars (none, not editing) | `

No environment variables configured.

` | Plain `

` | | Traces/Taps (none) | `

No processor traces or taps configured.

` | Plain `

` | | Route recording (none) | `

No routes found for this application.

` | Plain `

` | | AgentInstance metrics | `` | EmptyState (DS component) | | Log/Event panels | `

No events...
` | Styled `
` | | OIDC default roles | `No default roles configured` | `` | | Group members (none) | `(no members)` | `` | | AppConfigDetailPage (not found) | `
No configuration found for "{appId}".
` | Plain `
` | | RouteDetail error patterns | `
No error patterns found...
` | Styled `
` | | RouteDetail taps (none) | `
No taps configured...
` | Styled `
` | **INCONSISTENCY**: Empty states use at least 5 different approaches: 1. Design system `EmptyState` component (only in AgentInstance) 2. `

` (AppsTab) 3. `` with parenthetical format "(none)" (RBAC pages) 4. `

` (RouteDetail) 5. Unstyled inline text (AppConfigDetailPage) The design system provides an `EmptyState` component but it is only used in one place (AgentInstance). --- ## 6. Inconsistency Summary ### HIGH Priority (User-facing confusion) 1. **AppConfigDetailPage button order is reversed** (Save|Cancel instead of Cancel|Save) and Save button has no `variant="primary"`. File: `ui/src/pages/Admin/AppConfigDetailPage.tsx`, lines 311-315. 2. **Deployment Stop has no confirmation dialog**. Stopping a running deployment immediately executes with no confirmation, while stopping/suspending a route shows a ConfirmDialog. File: `ui/src/pages/AppsTab/AppsTab.tsx`, line 672. 3. **Tap deletion is inconsistent**. Deleting from TapConfigModal: no confirmation. Deleting from RouteDetail table: ConfirmDialog. File: `ui/src/components/TapConfigModal.tsx` line 117 vs `ui/src/pages/Routes/RouteDetail.tsx` line 992. 4. **Kill Query has no confirmation and no feedback**. File: `ui/src/pages/Admin/DatabaseAdminPage.tsx`, line 30. ### MEDIUM Priority (Pattern deviations) 5. **Cancel button variant inconsistency**. Create forms use `variant="ghost"` for Cancel. Modal dialogs (TapConfigModal, RouteDetail tap modal) use `variant="secondary"`. File: `ui/src/components/TapConfigModal.tsx` line 255, vs `ui/src/pages/Admin/UsersTab.tsx` line 258. 6. **Removing a role from a user has no confirmation** but removing a group from a user shows an AlertDialog. Both can cascade. File: `ui/src/pages/Admin/UsersTab.tsx`, lines 504-528 vs 588-613. 7. **OIDC Config is always editable with no Cancel/discard**. Every other editable form either has inline-edit (immediate save) or explicit edit mode with Cancel. File: `ui/src/pages/Admin/OidcConfigPage.tsx`. 8. **OIDC Config delete ConfirmDialog missing `loading` prop**. All other delete ConfirmDialogs pass `loading={mutation.isPending}`. File: `ui/src/pages/Admin/OidcConfigPage.tsx`, line 258. 9. **Loading state size inconsistency**. Most pages use `Spinner size="md"`, some use `size="lg"`, some use `PageLoader`, and OidcConfigPage returns `null`. No single standard. 10. **Error toast title format inconsistency**. RBAC pages use "Failed to [verb] [noun]" while AppsTab/AppConfigDetailPage use "[Noun] failed". Should pick one. ### LOW Priority (Minor deviations) 11. **Empty state presentation varies widely**. Five different approaches used. Should standardize on the design system `EmptyState` component or at least a consistent CSS class. 12. **ConfirmDialog confirmText varies between display name and slug**. Users/Groups/Roles use display name; Environments and Apps use slug. This is arguably intentional (slug is the technical identifier) but may confuse users. 13. **OIDC Config shows both toast and inline Alert on error**. No other page shows both simultaneously. File: `ui/src/pages/Admin/OidcConfigPage.tsx`, line 92 (toast) + line 139 (inline Alert). 14. **AppConfigDetailPage Save button text changes to "Saving..."** using string interpolation, while every other page uses the `loading` prop on Button (which shows a spinner). File: `ui/src/pages/Admin/AppConfigDetailPage.tsx`, line 313. 15. **Unsaved changes indicator** only present on AppsTab ConfigSubTab (banner text). AppConfigDetailPage, Environment resource sections, and JAR retention section have no indicator even though they use explicit edit mode. --- ## 7. ConfirmDialog Usage Matrix | Object | File | Line | confirmText Source | Has `loading`? | Has `variant`? | Has `confirmLabel`? | |--------|------|------|-------------------|----------------|----------------|---------------------| | User | UsersTab.tsx | 580 | displayName | YES | No (default) | No (default) | | Group | GroupsTab.tsx | 434 | name | YES | No (default) | No (default) | | Role | RolesTab.tsx | 223 | name | YES | No (default) | No (default) | | Environment | EnvironmentsPage.tsx | 319 | slug | YES | No (default) | No (default) | | OIDC Config | OidcConfigPage.tsx | 258 | "delete oidc" | **NO** | No (default) | No (default) | | App | AppsTab.tsx | 589 | slug | YES | No (default) | No (default) | | Tap (RouteDetail) | RouteDetail.tsx | 992 | attributeName | **NO** | `danger` | `"Delete"` | | Route Stop | RouteControlBar.tsx | 139 | action name | YES | `danger`/`warning` | `"Stop Route"` / `"Suspend Route"` | **NOTE**: RouteControlBar and RouteDetail set explicit `variant` and `confirmLabel` on ConfirmDialog while all RBAC/admin pages use defaults. This creates visual differences in the confirmation dialogs. --- ## 8. AlertDialog Usage Matrix | Context | File | Line | Title | Confirm Label | Variant | |---------|------|------|-------|---------------|---------| | Remove group from user | UsersTab.tsx | 588 | "Remove group membership" | "Remove" | `warning` | | Remove role from group | GroupsTab.tsx | 442 | "Remove role from group" | "Remove" | `warning` | AlertDialog is used consistently where present (both use `warning` variant and "Remove" label). --- ## 9. Files Examined All `.tsx` files under `ui/src/pages/` and `ui/src/components/`: - `ui/src/pages/Admin/UsersTab.tsx` - `ui/src/pages/Admin/GroupsTab.tsx` - `ui/src/pages/Admin/RolesTab.tsx` - `ui/src/pages/Admin/EnvironmentsPage.tsx` - `ui/src/pages/Admin/OidcConfigPage.tsx` - `ui/src/pages/Admin/AppConfigDetailPage.tsx` - `ui/src/pages/Admin/DatabaseAdminPage.tsx` - `ui/src/pages/Admin/ClickHouseAdminPage.tsx` - `ui/src/pages/Admin/AuditLogPage.tsx` - `ui/src/pages/AppsTab/AppsTab.tsx` - `ui/src/pages/Routes/RouteDetail.tsx` - `ui/src/pages/Exchanges/ExchangesPage.tsx` - `ui/src/pages/Exchanges/RouteControlBar.tsx` - `ui/src/pages/AgentHealth/AgentHealth.tsx` - `ui/src/pages/AgentInstance/AgentInstance.tsx` - `ui/src/pages/DashboardTab/DashboardPage.tsx` - `ui/src/pages/RuntimeTab/RuntimePage.tsx` - `ui/src/components/TapConfigModal.tsx` - `ui/src/components/AboutMeDialog.tsx` - `ui/src/components/PageLoader.tsx` - `ui/src/components/LayoutShell.tsx` - `ui/src/auth/LoginPage.tsx`