Rename Java packages from com.cameleer3 to com.cameleer, module directories from cameleer3-* to cameleer-*, and all references throughout workflows, Dockerfiles, docs, migrations, and pom.xml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
600 lines
30 KiB
Markdown
600 lines
30 KiB
Markdown
# 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**: `<Button size="sm" variant="danger">Delete</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**: `<Button size="sm" variant="danger">Delete</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**: `<Button size="sm" variant="danger">Delete</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**: `<Button size="sm" variant="danger">Delete</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**: `<Button size="sm" variant="danger">Delete OIDC Configuration</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**: `<Button size="sm" variant="danger">Delete App</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**: `<Button size="sm" variant="danger">Stop</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 `<button>` elements (not design system Button)
|
|
- **Confirmation**: `ConfirmDialog` (type-to-confirm) -- only for `stop` and `suspend` actions
|
|
- Title: `"Stop route?"` or `"Suspend route?"`
|
|
- Message: `This will ${action} route "${routeId}" on ${application}. This affects all live agents.`
|
|
- Confirm text: the action name (e.g., `"stop"` or `"suspend"`)
|
|
- Confirm label: `"Stop Route"` or `"Suspend Route"`
|
|
- Variant: `danger` for stop, `warning` for suspend
|
|
- Has `loading` prop
|
|
- **Start and Resume**: No confirmation (immediate action)
|
|
- **Toast patterns match others**
|
|
|
|
### 1.13 Delete Tap (Route Detail page)
|
|
- **File**: `ui/src/pages/Routes/RouteDetail.tsx` (lines 991-1001)
|
|
- **Button location**: Inline delete icon button in taps table row
|
|
- **Confirmation**: `ConfirmDialog` (type-to-confirm)
|
|
- Title: "Delete Tap"
|
|
- Message: `This will remove the tap "${attributeName}" from the configuration.`
|
|
- Confirm text: tap's `attributeName`
|
|
- Confirm label: "Delete"
|
|
- Variant: `danger`
|
|
- **No `loading` prop on this dialog**
|
|
|
|
**INCONSISTENCY**: No `loading` prop, unlike entity delete confirmations.
|
|
|
|
### 1.14 Delete Tap (TapConfigModal)
|
|
- **File**: `ui/src/components/TapConfigModal.tsx` (lines 117-122, 249-253)
|
|
- **Button location**: Inside the modal footer, left-aligned (only shown when editing)
|
|
- **Button**: `<Button variant="danger">Delete</Button>`
|
|
- **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**: `<Button variant="danger" size="sm">Kill</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 `<Alert variant="error">` 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**: `<ToastProvider>` 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 | `<Spinner size="md" />` | md | Bare return |
|
|
| GroupsTab | `<Spinner size="md" />` | md | Bare return |
|
|
| RolesTab | `<Spinner size="md" />` | md | Bare return |
|
|
| EnvironmentsPage | `<Spinner size="md" />` | md | Bare return |
|
|
| AppListView | `<Spinner size="md" />` | md | Bare return |
|
|
| AppDetailView | `<Spinner size="md" />` | md | Bare return |
|
|
| AgentInstance | `<Spinner size="lg" />` | **lg** | Bare return |
|
|
| AppConfigDetailPage | `<Spinner size="lg" />` | **lg** | Wrapped in `div.loading` |
|
|
| DashboardPage | `<PageLoader />` | lg | Centered container |
|
|
| RuntimePage | `<PageLoader />` | lg | Centered container |
|
|
| OidcConfigPage | `return null` | N/A | Returns nothing |
|
|
|
|
**INCONSISTENCY #1**: Most admin pages use `<Spinner size="md" />` as a bare return. AgentInstance and AppConfigDetailPage use `size="lg"`. DashboardPage and RuntimePage use the `<PageLoader />` component which wraps `<Spinner size="lg" />` 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 `<Spinner size="md" />` -- consistent within that context.
|
|
|
|
### 5.2 Section Loading States
|
|
|
|
- **RouteDetail charts**: `<Spinner size="sm" />` inline in chart containers (lines 713, 804)
|
|
- **AboutMeDialog**: `<Spinner size="md" />` 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) | `<p className={styles.emptyNote}>No deployments yet.</p>` | Plain `<p>` |
|
|
| Versions list (none) | `<p className={styles.emptyNote}>No versions uploaded yet.</p>` | Plain `<p>` |
|
|
| Env vars (none, not editing) | `<p className={styles.emptyNote}>No environment variables configured.</p>` | Plain `<p>` |
|
|
| Traces/Taps (none) | `<p className={styles.emptyNote}>No processor traces or taps configured.</p>` | Plain `<p>` |
|
|
| Route recording (none) | `<p className={styles.emptyNote}>No routes found for this application.</p>` | Plain `<p>` |
|
|
| AgentInstance metrics | `<EmptyState title="No data" description="No X available" />` | EmptyState (DS component) |
|
|
| Log/Event panels | `<div className={logStyles.logEmpty}>No events...</div>` | Styled `<div>` |
|
|
| OIDC default roles | `<span className={styles.noRoles}>No default roles configured</span>` | `<span>` |
|
|
| Group members (none) | `<span className={styles.inheritedNote}>(no members)</span>` | `<span>` |
|
|
| AppConfigDetailPage (not found) | `<div>No configuration found for "{appId}".</div>` | Plain `<div>` |
|
|
| RouteDetail error patterns | `<div className={styles.emptyText}>No error patterns found...</div>` | Styled `<div>` |
|
|
| RouteDetail taps (none) | `<div className={styles.emptyState}>No taps configured...</div>` | Styled `<div>` |
|
|
|
|
**INCONSISTENCY**: Empty states use at least 5 different approaches:
|
|
1. Design system `EmptyState` component (only in AgentInstance)
|
|
2. `<p className={styles.emptyNote}>` (AppsTab)
|
|
3. `<span className={styles.inheritedNote}>` with parenthetical format "(none)" (RBAC pages)
|
|
4. `<div className={styles.emptyText}>` (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`
|