From 2835d084188182f40b9e71dc8ee2643090abedbe Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:24:48 +0200 Subject: [PATCH] ui(env): explicit switcher button+modal, forced selection, 3px color bar - Replace EnvironmentSelector "All Envs" dropdown with Button+Modal (DS Modal, forced on first-use). - Add 8-swatch preset color picker in the Environment settings "Appearance" section; commits via useUpdateEnvironment. - Render a 3px fixed top bar in the current env's color across every page (z-index 900, below DS modals). - New env-colors tokens (--env-color-*, light + dark) and envColorVar() helper with slate fallback. - Vitest coverage for button, modal, and color helpers (13 new specs). Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/rules/ui.md | 2 + ui/src/api/queries/admin/environments.ts | 2 + .../components/EnvironmentSelector.module.css | 4 - ui/src/components/EnvironmentSelector.tsx | 30 ----- .../EnvironmentSwitcherButton.module.css | 44 +++++++ .../EnvironmentSwitcherButton.test.tsx | 57 +++++++++ .../components/EnvironmentSwitcherButton.tsx | 30 +++++ .../EnvironmentSwitcherModal.module.css | 82 +++++++++++++ .../EnvironmentSwitcherModal.test.tsx | 111 ++++++++++++++++++ .../components/EnvironmentSwitcherModal.tsx | 66 +++++++++++ ui/src/components/LayoutShell.tsx | 53 ++++++++- ui/src/components/env-colors.test.ts | 27 +++++ ui/src/components/env-colors.ts | 29 +++++ ui/src/main.tsx | 1 + ui/src/pages/Admin/EnvironmentsPage.tsx | 59 ++++++++++ ui/src/styles/env-colors.css | 34 ++++++ 16 files changed, 593 insertions(+), 38 deletions(-) delete mode 100644 ui/src/components/EnvironmentSelector.module.css delete mode 100644 ui/src/components/EnvironmentSelector.tsx create mode 100644 ui/src/components/EnvironmentSwitcherButton.module.css create mode 100644 ui/src/components/EnvironmentSwitcherButton.test.tsx create mode 100644 ui/src/components/EnvironmentSwitcherButton.tsx create mode 100644 ui/src/components/EnvironmentSwitcherModal.module.css create mode 100644 ui/src/components/EnvironmentSwitcherModal.test.tsx create mode 100644 ui/src/components/EnvironmentSwitcherModal.tsx create mode 100644 ui/src/components/env-colors.test.ts create mode 100644 ui/src/components/env-colors.ts create mode 100644 ui/src/styles/env-colors.css diff --git a/.claude/rules/ui.md b/.claude/rules/ui.md index 3914dbe3..8b362d79 100644 --- a/.claude/rules/ui.md +++ b/.claude/rules/ui.md @@ -25,6 +25,8 @@ The UI has 4 main tabs: **Exchanges**, **Dashboard**, **Runtime**, **Deployments - `ui/src/auth/auth-store.ts` — Zustand: accessToken, user, roles, login/logout - `ui/src/api/environment-store.ts` — Zustand: selected environment (localStorage) - `ui/src/components/ContentTabs.tsx` — main tab switcher +- `ui/src/components/EnvironmentSwitcherButton.tsx` + `EnvironmentSwitcherModal.tsx` — explicit env picker (button in TopBar; DS `Modal`-based list). Replaces the retired `EnvironmentSelector` (All-Envs dropdown). When `envRecords.length > 0` and the stored `selectedEnv` no longer matches any env, `LayoutShell` opens the modal in `forced` mode (non-dismissible). Switcher pulls env records from `useEnvironments()` (admin endpoint; readable by VIEWER+). +- `ui/src/components/env-colors.ts` + `ui/src/styles/env-colors.css` — 8-swatch preset palette for the per-environment color indicator. Tokens `--env-color-slate/red/amber/green/teal/blue/purple/pink` are defined for both light and dark themes. `envColorVar(name)` falls back to `slate` for unknown values. `LayoutShell` renders a 3px fixed top bar in the current env's color (z-index 900, below DS modals). - `ui/src/components/ExecutionDiagram/` — interactive trace view (canvas) - `ui/src/components/ProcessDiagram/` — ELK-rendered route diagram - `ui/src/hooks/useScope.ts` — TabKey type, scope inference diff --git a/ui/src/api/queries/admin/environments.ts b/ui/src/api/queries/admin/environments.ts index c32888c0..e3c8310e 100644 --- a/ui/src/api/queries/admin/environments.ts +++ b/ui/src/api/queries/admin/environments.ts @@ -9,6 +9,7 @@ export interface Environment { enabled: boolean; defaultContainerConfig: Record; jarRetentionCount: number | null; + color: string; createdAt: string; } @@ -22,6 +23,7 @@ export interface UpdateEnvironmentRequest { displayName: string; production: boolean; enabled: boolean; + color?: string; } export function useEnvironments() { diff --git a/ui/src/components/EnvironmentSelector.module.css b/ui/src/components/EnvironmentSelector.module.css deleted file mode 100644 index 2d3518ac..00000000 --- a/ui/src/components/EnvironmentSelector.module.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Layout wrapper — DS Select handles its own appearance */ -.select { - min-width: 100px; -} diff --git a/ui/src/components/EnvironmentSelector.tsx b/ui/src/components/EnvironmentSelector.tsx deleted file mode 100644 index 2230e654..00000000 --- a/ui/src/components/EnvironmentSelector.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useMemo } from 'react'; -import { Select } from '@cameleer/design-system'; -import styles from './EnvironmentSelector.module.css'; - -interface EnvironmentSelectorProps { - environments: string[]; - value: string | undefined; - onChange: (env: string | undefined) => void; -} - -export function EnvironmentSelector({ environments, value, onChange }: EnvironmentSelectorProps) { - if (environments.length === 0) return null; - - const options = useMemo( - () => [ - { value: '', label: 'All Envs' }, - ...environments.map((env) => ({ value: env, label: env })), - ], - [environments], - ); - - return ( -