updates on the ui design and desing updates
This commit is contained in:
149
CLAUDE.md
149
CLAUDE.md
@@ -15,8 +15,8 @@ For the per-page API map and architectural flow chart, see the project skill **`
|
||||
|
||||
## 2. Stack (pinned — do not upgrade without a deliberate audit)
|
||||
|
||||
- **React 18.2** + **react-app-rewired 2.2** (CRA, not Next.js — no `pages/` file-system routing; routes live in `src/routes/MainRoutes.js`).
|
||||
- **MUI 5.12** (`@mui/material`) is the primary UI library. Ant Design (`antd 5.11`) is used **only** for `<Empty />` placeholders in legacy pages — prefer MUI for new work.
|
||||
- **React 19.2** + **react-app-rewired 2.2** (CRA, not Next.js — no `pages/` file-system routing; routes live in `src/routes/MainRoutes.js`).
|
||||
- **Astryx 0.1.9** (`@astryxdesign/core`) is the UI library — see §6. **MUI 5.12** (`@mui/material`) and Ant Design (`antd 5.11`) are **legacy**, present only on the pages not yet converted, and are being removed. Never write new UI against them.
|
||||
- **Icons**: `@ant-design/icons` (page actions: `EditOutlined`, `EyeOutlined`, `CloseOutlined`, etc.) **and** `react-icons` (page identity: `Md*`, `Tb*`, `Fi*`, `Gi*`, `Lu*`). Both coexist; pick by what's already imported nearby.
|
||||
- **Data layer**: `@tanstack/react-query 5.17` (`useQuery`, `useInfiniteQuery`, `useMutation`). All server fetches go through this. Do **not** introduce `fetch`, `swr`, or `useEffect`-driven fetches for new code.
|
||||
- **HTTP**: `axios 1.3`. Most pages import `axios` directly (raw). `src/utils/axios.js` exists with a 401 → `/login` interceptor but is **not** widely adopted — match the surrounding file rather than introducing it.
|
||||
@@ -115,93 +115,102 @@ src/
|
||||
|
||||
---
|
||||
|
||||
## 6. Design system (the `DT` token block)
|
||||
## 6. Design system (Astryx + the `DT` tokens)
|
||||
|
||||
**The design system lives in two shared modules. Import from them — never redeclare.**
|
||||
The UI is **Astryx** (`@astryxdesign/core@0.1.9`). MUI is legacy and on its way out — see §6.6. Discover components with the CLI, don't guess:
|
||||
|
||||
```js
|
||||
import { DT, a, tint, soft, ring, edge, pillFieldSx, tableScrollSx, tableHeadSx, tableRowSx } from 'themes/dt/tokens';
|
||||
import { SoftPaper, AccentAvatar } from 'themes/dt/primitives';
|
||||
```bash
|
||||
npx -y @astryxdesign/cli component <Name>
|
||||
```
|
||||
|
||||
| Export | From | What it is |
|
||||
|---|---|---|
|
||||
| `DT` | `themes/dt/tokens` | Radii (`radiusPill/Card/Inner/Field`), the three shadows, text/border/surface colours, `DT.brand` (`#C01227`) |
|
||||
| `a` `tint` `soft` `ring` `edge` | `themes/dt/tokens` | Alpha-suffix helpers — append hex transparency to any accent (`08` / `18` / `26` / `55`) |
|
||||
| `pillFieldSx` | `themes/dt/tokens` | Filter `Autocomplete`/`TextField` styling. Takes no arguments — the field is neutral white with a brand focus ring on every page. Legacy call sites pass a colour; it's ignored. |
|
||||
| `tableScrollSx` | `themes/dt/tokens` | Brand-red scrollbar for `<TableContainer>`. Spread it: `sx={{ maxHeight: ..., ...tableScrollSx }}` |
|
||||
| `tableHeadSx` | `themes/dt/tokens` | Uppercase muted header row. Use as `<TableRow sx={tableHeadSx}>` |
|
||||
| `tableRowSx` | `themes/dt/tokens` | Body row hairline divider + hover tint. Use as `<TableRow sx={tableRowSx}>` |
|
||||
| `SoftPaper` | `themes/dt/primitives` | `PaperComponent` for every Autocomplete popup |
|
||||
| `AccentAvatar` | `themes/dt/primitives` | Tinted icon avatar; fills solid when `selected` |
|
||||
(The root README's `yarn dlx` form does not work here — `yarn` is not on PATH.)
|
||||
|
||||
> **Do not paste a local `const DT = {…}` into a page.** Seventeen pages used to
|
||||
> carry their own copy and they drifted — `orders.js` sat on `radiusCard: 16`
|
||||
> and the old heavy shadows while every other page had moved to `14` and the
|
||||
> restrained set, which is exactly why pages stopped matching. They now all
|
||||
> import. If a token needs to change, change it in `themes/dt/tokens.js` so
|
||||
> every page moves together.
|
||||
### 6.1 Where the design system lives
|
||||
|
||||
### Page anatomy (every operator page should follow this order)
|
||||
| Module | What it owns |
|
||||
|---|---|
|
||||
| `themes/astryx.js` | **The theme.** `doormileTheme` — brand accent, card/input radii, component overrides. Mounted by `layout/MainLayout` and `login.js` via `<Theme theme={doormileTheme} mode="light">`. |
|
||||
| `themes/dt/status.js` | **The status registry.** `STATUS_META`, `STATUS_ALIASES`, `getStatusMeta()` — every lifecycle state's label, hex, Astryx Badge variant, StatusDot variant, and icon. |
|
||||
| `themes/dt/tokens.js` | `DT` (radii, shadows, surfaces, `DT.brand`), `STATUS` raw hexes, alpha helpers `a`/`tint`/`soft`/`ring`/`edge`. Its `*Sx` exports are **legacy MUI only**. |
|
||||
| `themes/dt/primitives.js` | `TableScroll` (sticky-header + brand scrollbar wrapper), `AccentAvatar`, `softPopupStyle`. |
|
||||
| `components/nearle_components/` | `PageHeader`, `StatCard`, `StatusBadge`, `StatusTabs`, `DebounceSearchBar`, `TableLoader`, … |
|
||||
|
||||
> **Use brand red `#C01227` (with light variant `#D25463`) for every brand surface below.** Don't use indigo `#6366f1` — that's reserved for the "Accepted" status badge only.
|
||||
**Never redeclare these locally.** Seventeen pages once carried private `DT` copies and drifted (`orders.js` sat on `radiusCard: 16` and the old heavy shadows while everything else had moved to `14`); five pages carried private `STATUS_META` copies that disagreed on labels for the same order. Change the shared module so every page moves together.
|
||||
|
||||
1. **Gradient header `<Paper>`** — `linear-gradient(135deg, ${tint('#C01227')} 0%, ${tint('#D25463')} 100%)`, 48px filled `#C01227` avatar with a page icon, `Typography variant="h3"` title, "Live · {zone}" sub-line with an 8px green pulsing dot, and a pill `LocationAutocomplete` on the right (`pill accentColor="#C01227" paperComponent={SoftPaper}`).
|
||||
2. **KPI tiles row** — `Grid` of 3–4 `Paper` cards, each with a 3px top stripe gradient, uppercase eyebrow label, large bold number, and a soft-tinted avatar holding an icon. The "primary" tile uses brand red `#C01227`; other tiles use semantic status colours.
|
||||
3. **Filter bar `<Paper>`** (optional) — pill-style `Autocomplete`s using `pillFieldSx(color)` + `SoftPaper`, each with an `AccentAvatar` start-adornment.
|
||||
4. **Pill status tabs + pill search** — tabs as clickable `<Box>` pills (inactive = `tint` bg + `edge` border, using each tab's **semantic status colour** — pending → amber, delivered → emerald, etc.). The **selected** tab always fills with **brand red `#C01227`** (border, bg, glow ring, hover), regardless of which status it represents — status colour is a resting-state accent only, not a selection colour. See `deliveries.js`'s `STATUS_TABS.map` (canonical) for the exact `active ? '#C01227' : ...` ternary shape; `orders.js`, `riders.js`, `Tenants.js`, and `reports/ordersDetails.js` all follow the same pattern. Search via `DebounceSearchBar` styled with brand red `#C01227` (tint bg, edge border, focus ring).
|
||||
5. **Table `<Paper>`** with `<TableContainer>` + sticky `<TableHead>` — uppercase muted headers on `DT.surfaceAlt`, rows with `borderBottom: 1px solid ${DT.divider}` and `:hover` row tint. Scrollbar thumb uses brand red `edge('#C01227')`.
|
||||
6. **Status badges in cells** — `Stack` with `AccentAvatar` + label inside a soft pill (tint bg, edge border). Status colours come from a per-page `STATUS_META` map keyed by lowercase status string — these are **semantic**, not brand.
|
||||
7. **Edit / action icon buttons** — soft-pill `IconButton` using brand red `#C01227` (NOT `#8b5cf6` — that overlaps with the "Picked" status badge and confuses operators).
|
||||
8. **Empty state** — centered 64px avatar (soft grey), bold "No X to show" line, and a muted helper sentence. Do not use antd `<Empty />` for new code.
|
||||
### 6.2 Brand colour
|
||||
|
||||
### Universal brand colour
|
||||
**The brand is black (`#000000`)** — `DT.brand`, pinned into the theme as `--color-accent`. The palette is deliberately quiet: chrome is neutral, and colour carries *meaning only* (status). Do not introduce a second brand accent.
|
||||
|
||||
**`#C01227` — Doormile Express brand red.** This is the canonical primary colour for this app, defined in `src/themes/theme/default.js` as `primary.main`. It drives the sidebar, the logo, and is what every new surface (page headers, KPI primary tile, search bars, edit-action buttons, dialog/popup headers, scrollbars) must use as the brand accent.
|
||||
> Astryx's accent generator derives a ramp from the accent's hue and lightness. Black has neither, so feeding it `#000000` made the generator invent a hue and render every primary button magenta. That's why `themes/astryx.js` pins the accent tokens explicitly instead of using `color: { accent }`. Don't "simplify" it back.
|
||||
|
||||
Variants (also from `theme/default.js`):
|
||||
Anything genuinely brand-toned should read `var(--color-accent)` / `DT.brand`, never a literal `#000000`.
|
||||
|
||||
| Token | Hex | Use |
|
||||
|---|---|---|
|
||||
| `primary.lighter` | `#F5DBDE` | Very subtle wash bg |
|
||||
| `primary.light` / `primary.400` | `#D25463` | Gradient pair with main |
|
||||
| `primary.main` | `#C01227` | Brand primary — default for all brand surfaces |
|
||||
| `primary.dark` | `#910E1D` | Hover / pressed states |
|
||||
| `primary.darker` | `#48070F` | Deep contrast text on light bg |
|
||||
### 6.3 Status palette (semantic — the only place colour is allowed to shout)
|
||||
|
||||
**Page header / dialog header gradient:** `linear-gradient(135deg, ${tint('#C01227')} 0%, ${tint('#D25463')} 100%)` (subtle wash) or `linear-gradient(135deg, #C01227 0%, #D25463 100%)` (solid, for dialog titles).
|
||||
Never render a status by picking a colour by hand. Resolve it:
|
||||
|
||||
> **Off-brand accents are gone.** `deliveries.js`'s old indigo `#6366f1` brand
|
||||
> accent, `Tenants.js`'s purple `#662582`, and `multipleOrders.js`'s antd blue
|
||||
> `#1890ff` / purple `#65387a` have all been retired — brand surfaces are
|
||||
> `#C01227` and metric accents come from the status palette below. Indigo
|
||||
> `#6366f1` now appears only as the "Accepted" status colour.
|
||||
```jsx
|
||||
import StatusBadge from 'components/nearle_components/StatusBadge';
|
||||
|
||||
### Status palette (semantic — distinct from brand)
|
||||
<StatusBadge status={row.orderstatus} /> // handles casing, backend enums, unknowns
|
||||
```
|
||||
|
||||
These colour-code lifecycle states. Do **not** swap them for brand red on status **badges** (table-row status pills, `AccentAvatar` dropdown icons, KPI tiles) — operators rely on the colour to identify status at a glance in those read-only contexts.
|
||||
`getStatusMeta()` accepts a canonical key (`pending`), a raw backend enum (`miler_assigned`, `converted_to_consignment`), or any casing, and falls back to a neutral badge showing the raw string so an unmapped state is visible rather than blank.
|
||||
|
||||
**Exception — selected status tabs:** the one deliberate place brand red *does* replace the status colour is a **selected** pill tab (§4 above) — clicking "Pending"/"Active"/etc. fills that tab red, not amber/green, so the clicked state always reads as "this is the active filter" in the one consistent brand colour. The status colour still shows on every *unselected* tab (as a tint) and on the row badges underneath, so at-a-glance status identification in the table itself is unaffected.
|
||||
| Meaning | Hex | Astryx `Badge` | `StatusDot` |
|
||||
|---|---|---|---|
|
||||
| Pending / waiting | `#f59e0b` | `yellow` | `warning` |
|
||||
| Accepted / assigned | `#6366f1` | `blue` | `accent` |
|
||||
| Arrived | `#06b6d4` | `cyan` | `accent` |
|
||||
| Picked up | `#8b5cf6` | `purple` | `accent` |
|
||||
| Active / in-transit | `#14b8a6` | `teal` | `success` |
|
||||
| Delivered / success | `#10b981` | `green` | `success` |
|
||||
| Skipped | `#f97316` | `orange` | `warning` |
|
||||
| Cancelled / error | `#ef4444` | `error` | `error` |
|
||||
| Neutral / unknown | `#94a3b8` | `neutral` | `neutral` |
|
||||
|
||||
| Meaning | Colour |
|
||||
|--------------------------|-----------|
|
||||
| Pending / waiting | `#f59e0b` (amber) |
|
||||
| Accepted / assigned | `#6366f1` (indigo — semantically distinct from brand red) |
|
||||
| Arrived | `#06b6d4` (cyan) |
|
||||
| Picked up | `#8b5cf6` (light purple — distinct from brand) |
|
||||
| Active / in-transit | `#14b8a6` (teal) |
|
||||
| Delivered / success | `#10b981` (emerald) |
|
||||
| Cancelled / error | `#ef4444` (red) |
|
||||
| Skipped | `#f97316` (orange) |
|
||||
| Neutral / muted | `#94a3b8` (slate) |
|
||||
| Sky accent (tenant, info)| `#0ea5e9` |
|
||||
Badges use the **tinted** non-semantic variants on purpose. Astryx reserves the solid `success`/`warning`/`error` variants for states that demand attention, and a table where every row shouts is a table where nothing does — `cancelled` is the one lifecycle state that keeps a loud variant.
|
||||
|
||||
### Don'ts for the design system
|
||||
The hexes in `STATUS` (tokens.js) are the raw layer, for surfaces taking an arbitrary accent (`StatCard color=`, `AccentAvatar`, chart series). Use `status.js` to *render*; use `STATUS` only when you need a bare colour.
|
||||
|
||||
- Don't use raw MUI `<Tabs>` for status/filter switching — they were replaced by the pill `<Box>` pattern on every redesigned page. Pages that still use `<Tabs>` (e.g. for inline collapse views) are tolerated but new top-level navigation should use pills.
|
||||
- Don't reach for `purple lighter` / `e1bee7` or other Mantis theme accents on new pages — those exist in legacy code only.
|
||||
- Don't apply box-shadow to row hover; only tint the background (`DT.surfaceAlt`).
|
||||
- Don't change avatar sizes mid-page — pick from `{18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 56, 64}` and stay consistent.
|
||||
### 6.4 Page anatomy
|
||||
|
||||
Follow this order. **`pages/nearle/hubs/hubs.js` is the reference implementation** — copy its structure.
|
||||
|
||||
1. **`<PageHeader>`** — title, `live` subtitle with pulsing `StatusDot`, and an `action` slot (primary `Button`, zone picker). Plain and un-boxed; the old gradient header Paper is gone.
|
||||
2. **KPI row** — `<Grid columns={{ minWidth: 220 }} gap={3}>` of `<StatCard>`. `color` is a status hex, or `DT.brand` for the primary tile. No top-stripes, no rainbow.
|
||||
3. **`<StatusTabs>`** — the status filter strip. Supply the tab *order* and where each count comes from; labels and icons resolve from the registry.
|
||||
4. **Filter + search bar** — a `<Card padding={3}>` holding an `HStack justify="between"` with the row count on the left and `<DebounceSearchBar>` on the right.
|
||||
5. **Table** — `<Card padding={0}>` → `<TableScroll minWidth={…}>` → `<Table density="balanced" dividers="rows" hasHover>`. `TableScroll` supplies the sticky header and scrollbar that MUI's `TableContainer` used to.
|
||||
6. **Status cells** — `<StatusBadge>`. Never a hand-rolled tinted `Box`.
|
||||
7. **Row actions** — `<IconButton size="sm" variant="ghost">`, `variant="destructive"` for delete. Always pass `label` **and** `tooltip`.
|
||||
8. **Empty state** — `<EmptyState title description icon>` inside a full-width `TableCell colSpan={n}`. Never antd `<Empty/>`.
|
||||
9. **Loading** — `<TableLoader>` for rows, `<Loader>` for a blocking full-page backdrop. These are not interchangeable.
|
||||
|
||||
### 6.5 Astryx rules and known gotchas
|
||||
|
||||
- **No `<div>` for layout.** `HStack` / `VStack` / `Grid` / `Center` / `Stack` do spacing and alignment. No raw `<span>` wrappers either.
|
||||
- **No raw values.** Colours and spacing come from tokens (`var(--color-*)`, `var(--spacing-*)`, `var(--radius-*)`). An inline `style` is the escape hatch *only* for a caller-supplied status hex, which the token system genuinely can't express (see `StatCard`).
|
||||
- **No `sx` prop** — it doesn't exist. Restyle through `themes/astryx.js` so every call site moves at once.
|
||||
- **`Grid` takes `columns={{ minWidth: N }}`**, not `{ base, sm }` breakpoints.
|
||||
- **`Table` children-mode cells have no `align` prop** — use `style`.
|
||||
- **There is no `Tabs` and no `Stepper`** in 0.1.9. Use `TabList`/`Tab` (with `endContent` for counts, `TabMenu` for overflow) and `SegmentedControl`/`ProgressBar` for wizard steps.
|
||||
- **`Spinner` maxes out at 18px** — large loaders are CSS rings.
|
||||
- **`Selector` doesn't forward `ref`**; **`Typeahead` has no freeSolo**, which is why `AddressAutocomplete` is a `TextInput` plus a hand-rolled list.
|
||||
- **Astryx has 153 components** — search before hand-rolling: `npx -y @astryxdesign/cli search "<thing>"`.
|
||||
|
||||
### 6.6 Migration status
|
||||
|
||||
Converted: the app shell, `login.js`, the maintenance pages, the entire shared-component layer, and the pages `hubs`, `vehicles`, `reports/{profitability, RidersRoutes, mapWithRoute}`.
|
||||
|
||||
Still MUI: 25 operator pages and 9 `src/themes/*` files. Four pages still import antd `<Empty/>`.
|
||||
|
||||
Rules while both stacks coexist:
|
||||
|
||||
- A page is converted **whole**, never half. Mixed-stack pages are how the palette drifts.
|
||||
- `ThemeCustomization` must stay mounted in `App.js` until the final page converts; the MUI theme files and the `@mui/*` / `@emotion/*` / `antd` dependencies are removed only after that.
|
||||
- `OrdersTableSkeleton` renders rows *into* a page's table, so it can only convert alongside its last consumer. Converted pages use `TableLoader` instead.
|
||||
- Don't add a call site for anything marked ⛔ LEGACY in `tokens.js`.
|
||||
|
||||
---
|
||||
|
||||
@@ -275,7 +284,7 @@ These colour-code lifecycle states. Do **not** swap them for brand red on status
|
||||
## 13. Cross-references
|
||||
|
||||
- **For the per-page API map and the architectural flow chart** (which endpoint each page calls, what the optimisation pipeline does, FCM flow), invoke the project skill **`nearlexpress-docs`** (`.claude/skills/nearlexpress-docs/SKILL.md`) rather than restating the content here.
|
||||
- **For shared design patterns** between pages, the source of truth is `src/themes/dt/tokens.js` (values, `pillFieldSx`, the three `table*Sx` helpers) and `src/themes/dt/primitives.js` (`SoftPaper`, `AccentAvatar`). Import from them, don't redesign and don't re-declare locally. `src/pages/nearle/deliveries/deliveries.js` remains the reference for how those tokens are *composed* into a page (filter bar → tabs → table), but it is no longer where the values live.
|
||||
- **For shared design patterns** between pages, the sources of truth are `src/themes/astryx.js` (the theme), `src/themes/dt/status.js` (lifecycle rendering), `src/themes/dt/tokens.js` (radii, shadows, raw hexes), and `src/themes/dt/primitives.js` (`TableScroll`, `AccentAvatar`). Import from them; don't redesign and don't re-declare locally. `src/pages/nearle/hubs/hubs.js` is the reference for how they *compose* into a page (header → KPIs → tabs → filter → table).
|
||||
|
||||
<!-- ASTRYX:START -->
|
||||
Astryx v0.1.9 · 153 components
|
||||
|
||||
Reference in New Issue
Block a user