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>
21 KiB
UX Polish & Bug Fixes — Design Spec
Date: 2026-04-09 Scope: Bug fixes, design consistency, interaction consistency, contrast/readability, data formatting, chart fixes, admin polish Out of scope: Feature work (onboarding, alerting, shareable links, latency investigation) — tracked in Epic #100
Context
Comprehensive Playwright-driven audit of the live Cameleer UI (build 69dcce2, 60+ screenshots) combined with the existing UI_FINDINGS.md visual audit (2026-03-25) and 16 open Gitea issues. Three code-level audits performed: layout consistency (CSS modules), interaction patterns (dialogs, buttons, toasts), and design system adoption.
Audit artifacts in audit/:
monitoring-pages-findings.md— exchanges, dashboard, runtime, deployments, command palette, dark modeadmin-lifecycle-findings.md— RBAC CRUD, audit log, OIDC, environments, database, ClickHouse, platformdesign-consistency-findings.md— per-page CSS module usage, container padding, card patternsinteraction-patterns-findings.md— confirmation dialogs, button order, edit/save flows, toasts, loading/empty states- 60+ screenshots
Implementation Strategy
8 theme-based batches, ordered by impact. Each batch groups related fixes that share code paths.
Batch 1: Critical Bug Fixes
Effort: 1 day
1.1 SSE Navigation Bug
Problem: Admin pages sporadically redirect to /server/exchanges during form editing. The SSE exchange data stream triggers React state updates that cause route changes, losing unsaved work.
Fix: Guard SSE-driven state updates so they never trigger navigation when the current route is outside the exchanges scope. The exchange list polling/SSE should update data stores without pushing route state. Likely in LayoutShell.tsx or the SSE connection manager — the exchange data subscription must be decoupled from route navigation.
1.2 User Creation in OIDC Mode
Problem: UserAdminController.createUser() returns ResponseEntity.badRequest().build() (empty body) when OIDC is enabled. The UI still shows "+ Add user" and the full creation form. Toast says "Failed to create user" with no explanation.
Fix (backend): Return { "error": "Local user creation is disabled when OIDC is enabled" } as the response body.
Fix (frontend): When OIDC is enabled, show an inline banner replacing the create form: "Local user creation is disabled when OIDC is active. Users are provisioned automatically via SSO." Hide or disable the "+ Add user" button.
Files: UserAdminController.java:92-93, UsersTab.tsx (create form section)
1.3 /server/deployments 404
Problem: Direct URL shows unhandled React Router dev error. The Deployments tab lives at /server/apps.
Fix: Add redirect route in router.tsx: { path: "deployments", element: <Navigate to="/server/apps" replace /> }
Files: ui/src/router.tsx
1.4 GC Pauses Chart X-axis
Problem: Renders ~60 full ISO-8601 timestamps overlapping into an unreadable block. All other agent charts (CPU, Memory, Throughput, Error Rate, Thread Count) use concise time labels.
Fix: Use the same X-axis time formatter as the other 5 agent charts. The GC Pauses chart likely passes raw ISO strings to the chart library instead of using the shared time axis configuration.
Files: AgentInstance.tsx or AgentInstance.module.css (GC Pauses chart config)
Batch 2a: Design/Layout Consistency
Effort: 2 days
Reference: audit/design-consistency-findings.md
2a.1 Exchanges Table Containment
Problem: Only full-bleed table in the app. Dashboard.tsx rolls its own .tableHeader with padding: 8px 12px instead of using table-section.module.css (shared: 12px 16px). No card wrapper.
Fix: Import and use tableStyles.tableSection wrapper. Replace custom .tableHeader, .tableTitle, .tableRight, .tableMeta with the shared module classes. The exchange table should look like every other table (Audit Log, ClickHouse, Dashboard L2/L3).
Files: pages/Dashboard/Dashboard.tsx, pages/Dashboard/Dashboard.module.css
2a.2 App Detail/Create App Flat Controls
Problem: AppsTab.tsx renders all form controls flat against the background. Config tabs (Monitoring, Resources, Variables, Traces & Taps, Route Recording) have labels and controls but no card wrappers. Controls "mesh into background."
Fix: Wrap each configuration group in sectionStyles.section from section-card.module.css, matching the OIDC page pattern (OidcConfigPage.tsx) and AppConfigDetail pattern (AppConfigDetailPage.tsx). Each sub-tab's content gets a section card.
Files: pages/AppsTab/AppsTab.tsx, pages/AppsTab/AppsTab.module.css
2a.3 Apps Deployment Table
Problem: Uses raw HTML <table> instead of DataTable with no tableStyles.tableSection wrapper.
Fix: Replace manual <table> with DataTable inside tableStyles.tableSection, matching the Audit Log pattern.
Files: pages/AppsTab/AppsTab.tsx
2a.4 Container Padding Normalization
Problem: Three different strategies: Exchanges/Dashboard = no padding, Runtime/Admin = 20px 24px 40px, Apps = 16px.
Fix:
- Standardize on
20px 24px 40pxfor all scrollable content pages - Apps: change from
16pxto20px 24px 40px - Dashboard: add
padding: 0 24px 20pxfor side margins (keep gap-based vertical spacing) - Exchanges: exception — split-view height-filling layout needs no padding
Files: pages/AppsTab/AppsTab.module.css (.container), pages/DashboardTab/DashboardTab.module.css (.content)
2a.5 Deduplicate Card CSS
Problem: The bg-surface + border + border-radius + box-shadow card pattern is copy-pasted in 8+ locations instead of importing shared modules.
Fix: Replace custom card declarations with imports from section-card.module.css or table-section.module.css:
DashboardTab.module.css—.errorsSection,.diagramSection->tableStyles.tableSectionAgentHealth.module.css—.configBar,.eventCard->sectionStyles.sectionAgentInstance.module.css—.processCard,.timelineCard->sectionStyles.sectionClickHouseAdminPage.module.css—.pipelineCard->sectionStyles.section
2a.6 Admin Detail Pages Flat Forms
Problem: RBAC detail (Users/Groups) and Environments detail render form controls without section cards.
Fix: Wrap detail sections in sectionStyles.section. For master-detail pages, the detail panel should use section cards to group related fields (Identity, Roles, Group Membership, etc.).
Files: pages/Admin/UsersTab.tsx, pages/Admin/GroupsTab.tsx, pages/Admin/EnvironmentsPage.tsx
2a.7 Database Admin Table
Problem: Uses DataTable without tableStyles.tableSection wrapper.
Fix: Wrap in tableStyles.tableSection like Audit Log and ClickHouse.
Files: pages/Admin/DatabaseAdminPage.tsx
Batch 2b: Interaction Pattern Consistency
Effort: 2 days
Reference: audit/interaction-patterns-findings.md
2b.1 AppConfigDetailPage Button Order Reversed (HIGH)
Problem: Save|Cancel order (lines 311-315), reversed from every other form. Save button has no variant="primary".
Fix: Swap to Cancel (ghost, left) | Save (primary, right).
Files: pages/Admin/AppConfigDetailPage.tsx:311-315
2b.2 Deployment Stop Needs Confirmation (HIGH)
Problem: AppsTab.tsx:672 — stopping a running deployment is immediate. Route Stop/Suspend uses ConfirmDialog.
Fix: Add ConfirmDialog: "Stop deployment for {appName}? This will take the service offline." Type-to-confirm with app slug.
Files: pages/AppsTab/AppsTab.tsx:672
2b.3 Tap Deletion Inconsistency (HIGH)
Problem: TapConfigModal.tsx:117 — delete is immediate. RouteDetail.tsx:992 — shows ConfirmDialog.
Fix: Add ConfirmDialog to TapConfigModal delete, matching RouteDetail.
Files: components/TapConfigModal.tsx:117
2b.4 Kill Query Unguarded (HIGH)
Problem: DatabaseAdminPage.tsx:30 — no confirmation, no toast.
Fix: Add AlertDialog ("Kill query {pid}?") and success/error toast.
Files: pages/Admin/DatabaseAdminPage.tsx:30
2b.5 Role Removal From User Unguarded (MEDIUM)
Problem: UsersTab.tsx:504-528 — role removal is immediate. Group removal shows AlertDialog.
Fix: Add AlertDialog for role removal: "Remove role {name}? This may revoke access. Continue?" matching group removal pattern.
Files: pages/Admin/UsersTab.tsx:504-528
2b.6 Cancel Button Variant Standardization (MEDIUM)
Problem: Create forms use variant="ghost". Modal dialogs (TapConfigModal, RouteDetail) use variant="secondary".
Fix: Standardize Cancel = ghost everywhere.
Files: components/TapConfigModal.tsx:255, pages/Routes/RouteDetail.tsx (tap modal footer)
2b.7 OIDC Always-Editable Deviation (MEDIUM)
Problem: Only admin form without Edit mode toggle or Cancel button. No way to discard changes.
Fix: Add explicit Edit mode with Cancel/Save, matching Environment resource editing and AppConfigDetail patterns. Show read-only view by default, Edit button to enter edit mode.
Files: pages/Admin/OidcConfigPage.tsx
2b.8 ConfirmDialog Missing loading Prop (MEDIUM)
Problem: OidcConfigPage.tsx:258 and RouteDetail.tsx:992 — no loading prop (all others have it).
Fix: Add loading={mutation.isPending} to both.
Files: pages/Admin/OidcConfigPage.tsx:258, pages/Routes/RouteDetail.tsx:992
2b.9 Loading State Standardization (MEDIUM)
Problem: Mix of Spinner size="md", Spinner size="lg", PageLoader, and null.
Fix: Use PageLoader for all full-page loading states. Replace bare <Spinner size="md" /> returns in UsersTab, GroupsTab, RolesTab, EnvironmentsPage, AppListView, AppDetailView with <PageLoader />. Fix OidcConfigPage returning null.
Files: All admin page files, pages/Admin/OidcConfigPage.tsx
2b.10 Error Toast Title Format (MEDIUM)
Problem: RBAC: "Failed to create user" / AppsTab: "Save failed" — two patterns.
Fix: Standardize on "Failed to [verb] [noun]" (more descriptive). Update AppsTab and AppConfigDetailPage error toasts.
Files: pages/AppsTab/AppsTab.tsx, pages/Admin/AppConfigDetailPage.tsx
2b.11 Empty State Standardization (LOW)
Problem: 5 different approaches. DS EmptyState component only used in AgentInstance.
Fix: Replace all <p className={emptyNote}>, <span className={inheritedNote}>, <div className={emptyText}>, and plain text empty states with DS EmptyState component or a consistent shared emptyNote class with centered, muted styling.
Files: pages/AppsTab/AppsTab.tsx, pages/Admin/*.tsx, pages/Routes/RouteDetail.tsx
2b.12 Unsaved Changes Indicator (LOW)
Problem: Only AppsTab ConfigSubTab has the banner pattern.
Fix: Add unsaved-changes indicator to:
AppConfigDetailPage.tsx— reuse banner pattern from ConfigSubTabEnvironmentsPage.tsx— resource editing and JAR retention sectionsOidcConfigPage.tsx— after adding Edit mode (2b.7)
2b.13 Save Button Loading State (LOW)
Problem: AppConfigDetailPage.tsx:313 shows "Saving..." text instead of Button's loading prop.
Fix: Use loading={isPending} on Button (shows spinner, matches all other save buttons).
Files: pages/Admin/AppConfigDetailPage.tsx:313
2b.14 OIDC Dual Error Display (LOW)
Problem: Shows both toast AND inline Alert on error. No other page does this.
Fix: Remove the inline Alert. Use toast only, matching all other pages. Or remove the toast and keep only the inline Alert (useful for form context). Pick one — don't show both.
Files: pages/Admin/OidcConfigPage.tsx:92,139
Batch 3: Contrast & Readability
Effort: 1 day
Reference: UI_FINDINGS.md cross-cutting issues
3.1 WCAG AA Fix for --text-muted
Problem: Fails WCAG AA in both modes. Light: #9C9184 on #FFFFFF = ~3.0:1. Dark: #7A7068 on #242019 = ~2.9:1.
Fix: Change design system token values:
- Light mode:
--text-muted: #766A5E(achieves 4.5:1) - Dark mode:
--text-muted: #9A9088(achieves 4.5:1)
Single highest-impact fix — affects every page.
Files: Design system CSS variables (light and dark theme definitions)
3.2 WCAG AA Fix for --text-faint
Problem: Dark mode #4A4238 on #242019 = 1.4:1 — essentially invisible.
Fix: Restrict --text-faint to decorative use only (borders, dividers), or change dark mode value to #6A6058 (achieves 3:1 minimum). Audit all usages and replace any --text-faint on readable text with --text-muted.
Files: Design system CSS variables, all files using --text-faint
3.3 Font Size Floor
Problem: 10px text used for StatCard labels, overview labels, chain labels, section meta, sidebar tree labels. 11px for table meta, error messages, pagination, toggle buttons, chart titles.
Fix: Establish --font-size-min: 12px. Update all 10px and 11px instances to 12px minimum. Grep for font-size: 10px and font-size: 11px across all CSS modules.
Files: All CSS modules containing sub-12px font sizes
Batch 4: Data Formatting & Terminology
Effort: 2 days
4.1 Exchange ID Truncation
Problem: 33-char hex dominates the table (e.g., 96E395B0088AA6D-000000000001E75C).
Fix:
- Show last 8 chars with ellipsis:
...0001E75C - Full ID on hover tooltip
- Copy-to-clipboard on click (show brief "Copied" indicator)
- Apply to: exchange table, detail breadcrumb, command palette results
4.2 Attributes Column
Problem: Always shows "---" for every row.
Fix: Hide the column when all rows in the current result set have no attributes. Show as colored badges when populated (infrastructure exists in attribute-color.ts).
4.3 Status Terminology
Problem: "OK/ERR" in exchange table vs "COMPLETED/FAILED" in detail panel.
Fix: Standardize on the table convention (OK/WARN/ERR) everywhere. Update the detail panel's status display.
4.4 Agent Name Truncation
Problem: Raw K8s pod names like 8c0affadb860-1 or cameleer-backend-7c778f488c-2c2pc-1.
Fix:
- Use agent
displayNameif set at registration - Otherwise extract a short identifier (strip common prefix, show unique suffix)
- Full name in hover tooltip
- Apply to: exchange table Agent column, Runtime agent list
4.5 Duration Formatting
Problem: P99 "6695ms" should be "6.7s"; raw "321s" should be "5m 21s".
Fix: Create/extend shared duration formatter:
< 1000ms->Xms(e.g., "178ms")1000ms-59999ms->X.Xs(e.g., "6.7s")>= 60000ms->Xm Ys(e.g., "5m 21s")
Apply to: KPI strip, exchange table, exchange detail, dashboard route table, agent detail.
4.6 Number Formatting
Problem: Locale-specific decimals ("1.050" ambiguous), inconsistent unit spacing.
Fix: Use Intl.NumberFormat with explicit locale handling. Always put space before unit: "6.7 s", "1.9 %", "7.1 msg/s". Use K/M suffixes consistently for large numbers.
Batch 5: Chart & Visualization Fixes
Effort: 1 day
5.1 Agent Throughput Y-axis
Problem: 2 msg/s data on 1.2k scale — flat line.
Fix: Auto-scale Y-axis to data range with ~20% headroom. Apply to all 6 agent charts.
Files: pages/AgentInstance/AgentInstance.tsx
5.2 Agent Error Rate Unit Mismatch
Problem: Chart shows "err/h", KPI shows "%".
Fix: Standardize units. If KPI shows %, chart should show %.
5.3 Agent Memory Reference Line
Problem: Y-axis goes to 68 MB but max heap is 124 MB.
Fix: Add a reference/threshold line at max heap so users see how close memory is to the limit.
5.4 Agent State "UNKNOWN"
Problem: Shown alongside "LIVE" — confusing dual state.
Fix: If UNKNOWN is a secondary state field, either hide when primary state is LIVE, or label clearly: "Container: Unknown".
5.5 Dashboard Table Pointer Events
Problem: _tableSection and _chartGrid divs intercept pointer events on Application Health table rows.
Fix: Fix z-index/pointer-events so table rows receive click events correctly.
Files: pages/DashboardTab/DashboardTab.module.css
Batch 6: Admin Polish
Effort: 2 days
6.1 Error Toasts Surface API Details
Problem: All API error handlers show generic "Failed to X" without the response body message.
Fix: Extract response body message in all API error handlers and include in toast description. Fallback to generic only when body is empty.
6.2 Unicode Escape in Roles
Problem: Role descriptions show \u00b7 literally.
Fix: Decode unicode escapes in the role description strings. Either fix at the backend (return actual character) or frontend (decode before render).
Files: pages/Admin/RolesTab.tsx, potentially backend RoleAdminController
6.3 Password Confirmation Field
Problem: User creation form has no password confirmation.
Fix: Add "Confirm Password" field below Password. Inline validation error if mismatch. Show password policy hint: "Min 12 characters, 3 of 4: uppercase, lowercase, number, special".
Files: pages/Admin/UsersTab.tsx
6.4 Platform Label/Value Spacing
Problem: "Slugdefault", "Max Agents3", "Issued8. April 2026" — missing separators.
Fix: Fix the SaaS platform components that render key-value pairs. Add proper layout (colon + space, or definition list).
Files: SaaS platform UI (likely in cameleer-saas repo)
6.5 License Badge Colors
Problem: DISABLED features use red badges — looks like errors.
Fix: Change from --error (red) to neutral gray/muted for "not included in plan". Reserve red for "broken"/"failed".
Files: SaaS platform UI (license page component)
6.6 OIDC Client Secret Masking
Problem: Plain text input for sensitive field.
Fix: Change to type="password" with a show/hide toggle button.
Files: pages/Admin/OidcConfigPage.tsx
6.7 Audit Log Export
Problem: No export functionality for compliance.
Fix: Add export button to audit log header: CSV (current page, client-side) and CSV (all matching, server-side streaming). Filename: cameleer-audit-YYYY-MM-DDTHH-MM.csv.
Files: pages/Admin/AuditLogPage.tsx, new backend endpoint with Accept: text/csv
Batch 7: Nice-to-Have Polish
Effort: 1-2 days
Lower priority, implement time-permitting:
| # | Item | File(s) |
|---|---|---|
| 7.1 | Breadcrumb update when exchange selected | ExchangesPage.tsx |
| 7.2 | Explicit close/back button on exchange detail panel | ExchangesPage.tsx |
| 7.3 | Command palette: update category counts when search filters | Command palette component |
| 7.4 | Command palette: truncate exchange IDs | Command palette component |
| 7.5 | 7-Day Pattern heatmap: show "Insufficient data" when < 2 days | DashboardTab |
| 7.6 | Deployment list: show status badges per row | AppsTab.tsx |
| 7.7 | SplitPane empty state placeholders: add icon + centered styling | DS SplitPane or per-page |
| 7.8 | App deletion: make "Delete App" button more discoverable | AppsTab.tsx |
| 7.9 | Audit log: expandable rows for event detail | AuditLogPage.tsx |
| 7.10 | ConfirmDialog confirmText convention: standardize on display name |
All ConfirmDialog usages |
Implementation Order
| Order | Batch | Items | Effort | Impact |
|---|---|---|---|---|
| 1 | Batch 1: Critical Bugs | 4 | 1 day | Fixes broken functionality |
| 2 | Batch 2a: Layout Consistency | 7 | 2 days | Visual coherence |
| 3 | Batch 2b: Interaction Consistency | 14 | 2 days | Behavioral coherence |
| 4 | Batch 3: Contrast & Readability | 3 | 1 day | WCAG compliance |
| 5 | Batch 4: Data Formatting | 6 | 2 days | Exchange table transformation |
| 6 | Batch 5: Chart Fixes | 5 | 1 day | Agent detail usability |
| 7 | Batch 6: Admin Polish | 7 | 2 days | Form UX and error handling |
| 8 | Batch 7: Nice-to-Have | 10 | 1-2 days | Final polish |
Total: ~52 items across 8 batches, ~12-14 days of work.
Related Issues
| Issue | Relevance |
|---|---|
| #100 | Epic: UX Audit PMF Readiness — this spec covers polish/bugs only, not feature work |
| #105 | Exchange table readability — covered by Batch 4 (4.1-4.4) |
| #110 | Time/locale formatting — covered by Batch 4 (4.5-4.6) |
| #107 | Data export — partially covered by Batch 6 (6.7 audit log export) |
| #82 | Non-admin user experience — not covered (feature work) |
| #90 | Backend gaps — not covered (feature work) |
Appendix: Design System Reference Patterns
Correct table pattern (Audit Log, ClickHouse, Dashboard L2/L3):
import tableStyles from '../../styles/table-section.module.css';
<div className={tableStyles.tableSection}>
<div className={tableStyles.tableHeader}>
<span className={tableStyles.tableTitle}>Title</span>
<span className={tableStyles.tableMeta}>meta</span>
</div>
<DataTable ... />
</div>
Correct form section pattern (OIDC, AppConfigDetail):
import sectionStyles from '../../styles/section-card.module.css';
<div className={sectionStyles.section}>
<SectionHeader>Section Title</SectionHeader>
{/* form controls */}
</div>
Correct button order (all create/edit forms):
<div className={styles.actions}>
<Button variant="ghost" size="sm" onClick={onCancel}>Cancel</Button>
<Button variant="primary" size="sm" onClick={onSubmit}>Save</Button>
</div>
Correct loading state:
import { PageLoader } from '../../components/PageLoader';
if (isLoading) return <PageLoader />;