Playwright-driven audit of the live UI (build 69dcce2, 60+ screenshots)
covering all pages, CRUD lifecycles, design consistency, and interaction
patterns. Spec defines 8 batches of work: critical bugs, layout
consistency, interaction consistency, contrast/readability, data
formatting, chart fixes, admin polish, and nice-to-have items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
355 lines
16 KiB
Markdown
355 lines
16 KiB
Markdown
# Design Consistency Audit — Cameleer3 UI
|
|
|
|
**Audited**: 2026-04-09
|
|
**Scope**: All pages under `ui/src/pages/`
|
|
**Base path**: `C:/Users/Hendrik/Documents/projects/cameleer3-server/ui/src/`
|
|
|
|
|
|
## Shared Layout Infrastructure
|
|
|
|
### LayoutShell (`components/LayoutShell.tsx`)
|
|
All pages render inside `<main className={css.mainContent}>` which applies:
|
|
```css
|
|
.mainContent {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
}
|
|
```
|
|
This is a flex column container with **no padding/margin**. Each page is responsible for its own content spacing.
|
|
|
|
### Shared CSS Modules (`styles/`)
|
|
| Module | Class | Pattern |
|
|
|--------|-------|---------|
|
|
| `section-card.module.css` | `.section` | Card with `padding: 16px 20px`, border, shadow, `margin-bottom: 16px` |
|
|
| `table-section.module.css` | `.tableSection` | Card wrapper for tables, no padding (overflow hidden), with `.tableHeader` (12px 16px padding) |
|
|
| `chart-card.module.css` | `.chartCard` | Card with `padding: 16px` |
|
|
| `log-panel.module.css` | `.logCard` | Card for log viewers, max-height 420px |
|
|
| `refresh-indicator.module.css` | `.refreshIndicator` | Auto-refresh dot indicator |
|
|
| `rate-colors.module.css` | `.rateGood/.rateWarn/.rateBad` | Semantic color helpers |
|
|
|
|
|
|
## Per-Page Findings
|
|
|
|
---
|
|
|
|
### 1. Exchanges Page (`pages/Exchanges/`)
|
|
|
|
**Files**: `ExchangesPage.tsx`, `ExchangesPage.module.css`, `ExchangeHeader.tsx`, `ExchangeHeader.module.css`, `RouteControlBar.tsx`, `RouteControlBar.module.css`
|
|
|
|
**Container pattern**: NO wrapper padding. Uses `height: 100%` split-view layout that fills the entire `mainContent` area.
|
|
|
|
**Content wrapper**:
|
|
```css
|
|
.splitView { display: flex; height: 100%; overflow: hidden; }
|
|
```
|
|
|
|
**Table**: The exchange list is rendered by `Dashboard.tsx` (in `pages/Dashboard/`), which uses:
|
|
```css
|
|
.content { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; background: var(--bg-body); }
|
|
```
|
|
- Custom `.tableHeader` with `padding: 8px 12px` (slightly tighter than shared `tableStyles.tableHeader` which uses `12px 16px`)
|
|
- `DataTable` rendered with `flush` and `fillHeight` props
|
|
- **NO card wrapper** around the table — it's full-bleed against the background
|
|
- **Does NOT import shared `table-section.module.css`** — rolls its own `.tableHeader`, `.tableTitle`, `.tableRight`, `.tableMeta`
|
|
|
|
**Shared modules used**: NONE. All custom.
|
|
|
|
**INCONSISTENCY**: Full-bleed table with no card, no container padding. Custom table header styling duplicates shared module patterns with slightly different padding values (8px 12px vs 12px 16px).
|
|
|
|
---
|
|
|
|
### 2. Dashboard Tab (`pages/DashboardTab/`)
|
|
|
|
**Files**: `DashboardPage.tsx`, `DashboardL1.tsx`, `DashboardL2.tsx`, `DashboardL3.tsx`, `DashboardTab.module.css`
|
|
|
|
**Container pattern**:
|
|
```css
|
|
.content { display: flex; flex-direction: column; gap: 20px; flex: 1; min-height: 0; overflow-y: auto; padding-bottom: 20px; }
|
|
```
|
|
- **No top/left/right padding** — content is full-width inside `mainContent`
|
|
- Only `padding-bottom: 20px` and `gap: 20px` between sections
|
|
|
|
**Tables**: Wrapped in shared `tableStyles.tableSection` (card with border, shadow, border-radius). Imports `table-section.module.css`.
|
|
|
|
**Charts**: Wrapped in design-system `<Card>` component.
|
|
|
|
**Custom sections**: `errorsSection` and `diagramSection` duplicate the card pattern:
|
|
```css
|
|
.errorsSection {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
overflow: hidden;
|
|
}
|
|
```
|
|
This is identical to `tableStyles.tableSection` but defined separately in `DashboardTab.module.css`.
|
|
|
|
**Shared modules used**: `table-section.module.css`, `refresh-indicator.module.css`, `rate-colors.module.css`
|
|
|
|
**INCONSISTENCY**: No container padding means KPI strip and tables sit flush against the sidebar/edge. The `.errorsSection` duplicates `tableStyles.tableSection` exactly — should import the shared module instead of copy-pasting.
|
|
|
|
---
|
|
|
|
### 3. Runtime Tab — Agent Health (`pages/AgentHealth/`)
|
|
|
|
**Files**: `AgentHealth.tsx`, `AgentHealth.module.css`
|
|
|
|
**Container pattern**:
|
|
```css
|
|
.content { flex: 1; overflow-y: auto; padding: 20px 24px 40px; min-width: 0; background: var(--bg-body); }
|
|
```
|
|
- **Has explicit padding**: `20px 24px 40px` (top, sides, bottom)
|
|
|
|
**Tables**: Uses design-system `DataTable` inside a DS `Card` component for agent group cards. The group cards use custom `.groupGrid` grid layout. No `tableStyles.tableSection` wrapper.
|
|
|
|
**Cards/sections**: Custom card patterns like `.configBar`, `.eventCard`:
|
|
```css
|
|
.configBar {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
padding: 12px 16px;
|
|
margin-bottom: 16px;
|
|
}
|
|
```
|
|
|
|
**Shared modules used**: `log-panel.module.css`
|
|
|
|
**INCONSISTENCY**: Uses `padding: 20px 24px 40px` — different from DashboardTab (no padding) and Exchanges (no padding). Custom card patterns duplicate the standard card styling. Does not use `table-section.module.css` or `section-card.module.css`.
|
|
|
|
---
|
|
|
|
### 4. Runtime Tab — Agent Instance (`pages/AgentInstance/`)
|
|
|
|
**Files**: `AgentInstance.tsx`, `AgentInstance.module.css`
|
|
|
|
**Container pattern**:
|
|
```css
|
|
.content { flex: 1; overflow-y: auto; padding: 20px 24px 40px; min-width: 0; background: var(--bg-body); }
|
|
```
|
|
- Matches AgentHealth padding exactly (consistent within Runtime tab)
|
|
|
|
**Cards/sections**: Custom `.processCard`, `.timelineCard` duplicate the card pattern. Uses `chart-card.module.css` for chart wrappers.
|
|
|
|
**Shared modules used**: `log-panel.module.css`, `chart-card.module.css`
|
|
|
|
**INCONSISTENCY**: Consistent with AgentHealth but inconsistent with DashboardTab and Exchanges. Custom card patterns (processCard, timelineCard) duplicate shared module patterns.
|
|
|
|
---
|
|
|
|
### 5. Apps Tab (`pages/AppsTab/`)
|
|
|
|
**Files**: `AppsTab.tsx`, `AppsTab.module.css`
|
|
|
|
**Container pattern**:
|
|
```css
|
|
.container { padding: 16px; overflow-y: auto; flex: 1; }
|
|
```
|
|
- **Has padding**: `16px` all around
|
|
|
|
**Content structure**: Three sub-views (`AppListView`, `AppDetailView`, `CreateAppView`) all wrapped in `.container`.
|
|
|
|
**Tables**: App list uses `DataTable` directly — no `tableStyles.tableSection` wrapper. Deployment table uses custom `.table` with manual `<table>` HTML (not DataTable).
|
|
|
|
**Form controls**: Directly on page background with custom grid layout (`.configGrid`). Uses `SectionHeader` from design-system for visual grouping, but forms are not in cards/sections — they sit flat against the `.container` background.
|
|
|
|
**Custom elements**:
|
|
- `.editBanner` / `.editBannerActive` — custom banner pattern
|
|
- `.configGrid` — 2-column label/input grid
|
|
- `.table` — fully custom `<table>` styling (not DataTable)
|
|
|
|
**Shared modules used**: NONE. All custom.
|
|
|
|
**INCONSISTENCY (user-reported)**: Controls "meshed into background" — correct. Form controls use `SectionHeader` for labels but no `section-card` wrapper. The Tabs component provides visual grouping but the content below tabs is flat. Config grids, toggles, and inputs sit directly on `var(--bg-body)` background via the 16px-padded container. No card/section separation between different config groups. Also uses a manual `<table>` element instead of DataTable for deployments.
|
|
|
|
---
|
|
|
|
### 6. Admin — RBAC Page (`pages/Admin/RbacPage.tsx`, `UsersTab.tsx`, `GroupsTab.tsx`, `RolesTab.tsx`)
|
|
|
|
**Container pattern**: AdminLayout provides `padding: 20px 24px 40px`. RbacPage renders a bare `<div>` (no extra wrapper class).
|
|
|
|
**Content**: Uses `StatCard` strip, `Tabs`, then tab content. Detail views use `SplitPane` (from design-system). User/Group/Role detail sections use `SectionHeader` without card wrappers.
|
|
|
|
**Stat strip**: Custom grid — `grid-template-columns: repeat(3, 1fr)` with `gap: 10px; margin-bottom: 16px`
|
|
|
|
**Shared modules used**: NONE. Uses `UserManagement.module.css` (custom).
|
|
|
|
**INCONSISTENCY**: Detail sections use `SectionHeader` labels but content is flat (no `section-card` wrapper). Similar to AppsTab pattern.
|
|
|
|
---
|
|
|
|
### 7. Admin — Audit Log (`pages/Admin/AuditLogPage.tsx`)
|
|
|
|
**Container pattern**: Inherits AdminLayout padding (`20px 24px 40px`). Renders a bare `<div>`.
|
|
|
|
**Table**: Properly uses shared `tableStyles.tableSection` with `.tableHeader`, `.tableTitle`, `.tableRight`, `.tableMeta`.
|
|
|
|
**Shared modules used**: `table-section.module.css`
|
|
|
|
**STATUS**: CONSISTENT with shared patterns for the table section. Good.
|
|
|
|
---
|
|
|
|
### 8. Admin — OIDC Config (`pages/Admin/OidcConfigPage.tsx`)
|
|
|
|
**Container pattern**: Inherits AdminLayout padding. Adds `.page { max-width: 640px; margin: 0 auto; }` — centered narrow layout.
|
|
|
|
**Sections**: Uses shared `sectionStyles.section` from `section-card.module.css` for every form group. Uses `SectionHeader` inside each section card.
|
|
|
|
**Shared modules used**: `section-card.module.css`
|
|
|
|
**STATUS**: GOOD. This is the correct pattern — form groups wrapped in section cards. Should be the model for other form pages.
|
|
|
|
---
|
|
|
|
### 9. Admin — Database (`pages/Admin/DatabaseAdminPage.tsx`)
|
|
|
|
**Container pattern**: Inherits AdminLayout padding. Renders bare `<div>`.
|
|
|
|
**Tables**: Uses `DataTable` directly with NO `tableStyles.tableSection` wrapper. Tables under custom `.section` divs with `.sectionHeading` text labels.
|
|
|
|
**Cards**: Uses DS `<Card>` for connection pool. Stat strip is a flex layout.
|
|
|
|
**Shared modules used**: NONE. All custom.
|
|
|
|
**INCONSISTENCY**: Tables not wrapped in `tableStyles.tableSection`. Uses custom section headings instead of `SectionHeader`. Missing card wrappers around tables. Stat strip uses `flex` layout while other pages use `grid`.
|
|
|
|
---
|
|
|
|
### 10. Admin — ClickHouse (`pages/Admin/ClickHouseAdminPage.tsx`)
|
|
|
|
**Container pattern**: Inherits AdminLayout padding. Renders bare `<div>`.
|
|
|
|
**Tables**: Uses shared `tableStyles.tableSection` combined with custom `.tableSection` for margin: `className={tableStyles.tableSection} ${styles.tableSection}`.
|
|
|
|
**Custom elements**: `.pipelineCard` duplicates card pattern (bg-surface, border, radius, shadow, padding).
|
|
|
|
**Shared modules used**: `table-section.module.css`
|
|
|
|
**PARTIAL**: Tables correctly use shared module. Pipeline card duplicates shared card pattern.
|
|
|
|
---
|
|
|
|
### 11. Admin — Environments (`pages/Admin/EnvironmentsPage.tsx`)
|
|
|
|
**Container pattern**: Inherits AdminLayout padding. Renders via `SplitPane` (design-system).
|
|
|
|
**Content**: Uses `SectionHeader`, `SplitPane`, custom meta grids from `UserManagement.module.css`.
|
|
|
|
**Shared modules used**: Uses `UserManagement.module.css` (shared with RBAC pages)
|
|
|
|
**INCONSISTENCY**: Does not use `section-card.module.css` for form sections. Config sections use `SectionHeader` without card wrappers. `SplitPane` provides some structure but detail content is flat.
|
|
|
|
---
|
|
|
|
### 12. Admin — App Config Detail (`pages/Admin/AppConfigDetailPage.tsx`)
|
|
|
|
**Container pattern**: Adds `.page { max-width: 720px; margin: 0 auto; }` — centered layout.
|
|
|
|
**Sections**: Uses shared `sectionStyles.section` from `section-card.module.css`. Uses `SectionHeader` inside section cards. Custom header card duplicates the card pattern.
|
|
|
|
**Shared modules used**: `section-card.module.css`
|
|
|
|
**STATUS**: GOOD. Follows same pattern as OIDC page.
|
|
|
|
---
|
|
|
|
### 13. Routes pages (`pages/Routes/`) — NOT ROUTED
|
|
|
|
These pages (`RoutesMetrics.tsx`, `RouteDetail.tsx`) exist but are NOT in `router.tsx`. They may be deprecated or used as sub-components. `RoutesMetrics` correctly uses shared `tableStyles.tableSection`. `RouteDetail` has many custom card patterns (`.headerCard`, `.diagramPane`, `.statsPane`, `.executionsTable`, `.routeFlowSection`) that duplicate the shared card pattern.
|
|
|
|
---
|
|
|
|
|
|
## Summary: Inconsistency Matrix
|
|
|
|
### Container Padding
|
|
|
|
| Page | Padding | Pattern |
|
|
|------|---------|---------|
|
|
| **Exchanges** | NONE (full-bleed) | `height: 100%`, fills container |
|
|
| **Dashboard Tab** | NONE (gap only) | `gap: 20px`, `padding-bottom: 20px` only |
|
|
| **Runtime (AgentHealth)** | `20px 24px 40px` | Explicit padding |
|
|
| **Runtime (AgentInstance)** | `20px 24px 40px` | Explicit padding |
|
|
| **Apps Tab** | `16px` | Uniform padding |
|
|
| **Admin pages** | `20px 24px 40px` | Via AdminLayout |
|
|
|
|
**Finding**: Three different padding strategies. Exchanges and Dashboard have no padding; Runtime and Admin use 20px/24px; Apps uses 16px.
|
|
|
|
|
|
### Table Wrapper Pattern
|
|
|
|
| Page | Uses `tableStyles.tableSection`? | Card wrapper? |
|
|
|------|----------------------------------|---------------|
|
|
| **Exchanges (Dashboard.tsx)** | NO — custom `.tableHeader` | NO — full-bleed |
|
|
| **Dashboard L1/L2/L3** | YES | YES (shared) |
|
|
| **Runtime AgentHealth** | NO | YES (via DS `Card`) |
|
|
| **Apps Tab** | NO | NO — bare `<table>` |
|
|
| **Admin — Audit** | YES | YES (shared) |
|
|
| **Admin — ClickHouse** | YES | YES (shared) |
|
|
| **Admin — Database** | NO | NO |
|
|
|
|
**Finding**: 4 of 7 table-using pages do NOT use the shared `table-section.module.css`. The Exchanges page custom header has padding `8px 12px` vs shared `12px 16px`.
|
|
|
|
|
|
### Form/Control Wrapper Pattern
|
|
|
|
| Page | Form controls in cards? | Uses `section-card`? |
|
|
|------|------------------------|---------------------|
|
|
| **Apps Tab (detail)** | NO — flat against background | NO |
|
|
| **Apps Tab (create)** | NO — flat against background | NO |
|
|
| **Admin — OIDC** | YES | YES |
|
|
| **Admin — App Config** | YES | YES |
|
|
| **Admin — RBAC detail** | NO — flat against background | NO |
|
|
| **Admin — Environments** | NO — flat against background | NO |
|
|
| **Admin — Database** | PARTIAL (Card for pool) | NO |
|
|
| **Runtime — AgentHealth** | YES (custom `.configBar`) | NO (custom) |
|
|
|
|
**Finding**: Only OIDC and AppConfigDetail use `section-card.module.css` for form grouping. Most form pages render controls flat against the page background.
|
|
|
|
|
|
### Duplicated Card Pattern
|
|
|
|
The following CSS pattern appears in 8+ custom locations instead of importing `section-card.module.css` or `table-section.module.css`:
|
|
|
|
```css
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-card);
|
|
```
|
|
|
|
**Duplicated in**:
|
|
- `DashboardTab.module.css` → `.errorsSection`, `.diagramSection`
|
|
- `AgentHealth.module.css` → `.configBar`, `.eventCard`
|
|
- `AgentInstance.module.css` → `.processCard`, `.timelineCard`
|
|
- `ClickHouseAdminPage.module.css` → `.pipelineCard`
|
|
- `AppConfigDetailPage.module.css` → `.header`
|
|
- `RouteDetail.module.css` → `.headerCard`, `.diagramPane`, `.statsPane`, `.executionsTable`, `.routeFlowSection`
|
|
|
|
|
|
## Prioritized Fixes
|
|
|
|
### P0 — User-reported issues
|
|
1. **Exchanges table full-bleed**: `Dashboard.tsx` should wrap its table in `tableStyles.tableSection` and use the shared table header classes instead of custom ones. Custom `.tableHeader` padding (8px 12px) should match shared (12px 16px).
|
|
2. **Apps detail flat controls**: `AppsTab.tsx` config sections should wrap form groups in `sectionStyles.section` (from `section-card.module.css`), matching the OIDC page pattern.
|
|
3. **Apps deployment table**: Replace manual `<table>` with `DataTable` inside `tableStyles.tableSection`.
|
|
|
|
### P1 — Padding normalization
|
|
4. **Standardize container padding**: Choose ONE pattern for scrollable content areas. Recommended: `padding: 20px 24px 40px` (currently used by Runtime + Admin). Apply to DashboardTab's `.content`. Exchanges is an exception due to its split-view height-filling layout.
|
|
5. **DashboardTab.module.css**: Add side padding to `.content`.
|
|
|
|
### P2 — Shared module adoption
|
|
6. **Replace duplicated card patterns**: Import `section-card.module.css` or `table-section.module.css` instead of duplicating the card CSS in:
|
|
- `DashboardTab.module.css` (`.errorsSection` -> use `tableStyles.tableSection`)
|
|
- `AgentHealth.module.css` (`.configBar`, `.eventCard`)
|
|
- `AgentInstance.module.css` (`.processCard`, `.timelineCard`)
|
|
- `ClickHouseAdminPage.module.css` (`.pipelineCard`)
|
|
7. **Database admin**: Wrap tables in `tableStyles.tableSection`.
|
|
8. **Admin detail pages** (RBAC, Environments): Wrap form sections in `sectionStyles.section`.
|