# CLAUDE.md — BellSystems Control Panel v2 # Instructions for Claude Code > Read this file at the start of every session. > This is the v2 project — a clean rebuild. The old v1 code lives in `frontend/src/_archive/` for reference only. > Also read `DESIGN.md` before writing any UI code. --- ## Project Structure ``` C:\development\bellsystems-cp-v2\ │ CLAUDE.md ← you are here │ DESIGN.md ← design rules, component contracts, page layout spec │ docker-compose.yml │ ├── backend/ ← FastAPI backend — DO NOT MODIFY │ └── frontend/ ├── src/ │ ├── _archive/ ← v1 reference code — READ ONLY, never import from here except auth │ ├── assets/ │ │ ├── global-icons/ ← action SVGs (edit, delete, download, etc.) │ │ ├── side-menu-icons/ ← sidebar navigation SVGs │ │ ├── comms/ ← communication type SVGs │ │ ├── other-icons/ ← misc SVGs │ │ └── customer-status/ ← CRM status SVGs │ ├── components/ │ │ ├── ui/ ← design system components (the ONLY place to source UI) │ │ ├── layout/ ← Sidebar, Header, MainLayout │ │ └── shared/ │ ├── hooks/ │ ├── lib/ │ ├── modals/ ← all modal components live here, grouped by domain │ ├── pages/ ← one file per page, grouped by domain │ ├── providers/ │ ├── router/ │ │ └── index.jsx ← all routes defined here │ ├── styles/ │ │ ├── tokens.css ← ALL design tokens (colors, fonts, spacing, shadows) │ │ ├── components.css ← ALL component-level styles │ │ └── global.css ← base resets, typography, scrollbar, .page-wrapper │ └── main.jsx ← app entry point └── vite.config.js ``` --- ## Project Overview Bespoke SaaS Admin Console for BellSystems. Manages Devices, Customers (CRM), Manufacturing, Firmware, MQTT, Melodies, Staff, and more. - **Backend:** FastAPI at `backend/` — never modify - **Archive:** `frontend/src/_archive/` — v1 reference, read-only - **Active code:** `frontend/src/` (everything except `_archive/`) - **Design rules:** `DESIGN.md` at project root — read before writing any UI - **Style Guide:** live at `/dev/styleguide` — shows every component with every variant - **API client:** `frontend/src/lib/api.js` wraps `_archive/api/client.js` --- ## Import Alias `@/` maps to `frontend/src/`: ```js import Button from '@/components/ui/Button' ✅ import Select from '@/components/ui/Select' ✅ import { useAuth } from '@/hooks/useAuth' ✅ import MainLayout from '@/components/layout/MainLayout' ✅ ``` Never use relative `../` paths except inside `providers/` and `hooks/` when referencing `_archive/`. --- ## The Golden Rules - **Never modify `_archive/`** — it is read-only reference material - **All new code goes in `frontend/src/`** — no exceptions - **No `/v2/` prefix anywhere** — routes start from `/`, imports start from `@/` - **Read `DESIGN.md` before writing any UI code** - **Source every UI element from `@/components/ui/`** — no raw HTML elements for styled things - **Use only CSS tokens** — never raw hex, rgb, or pixel values in component or page files - **Use `.masonry-grid` for all content pages with multiple variable-height sections** — never `display: grid` with fixed columns for card layouts. See DESIGN.md §11. --- ## Available UI Components Every component lives in `frontend/src/components/ui/`. These are the ONLY components to use. Check the live Style Guide at `/dev/styleguide` to see all variants and states. | Component | Import path | Purpose | |-----------------|--------------------------------------|----------------------------------------------| | `Button` | `@/components/ui/Button` | All interactive actions | | `StatusBadge` | `@/components/ui/StatusBadge` | Coloured status pills | | `FormField` | `@/components/ui/FormField` | Every text/email/password/textarea input | | `Select` | `@/components/ui/Select` | Custom dropdown (used inside FormField type="select") | | `Modal` | `@/components/ui/Modal` | All overlay dialogs | | `ConfirmDialog` | `@/components/ui/ConfirmDialog` | Destructive / confirmation prompts | | `DataTable` | `@/components/ui/DataTable` | All tabular data with sorting/selection | | `Pagination` | `@/components/ui/Pagination` | Page controls beneath DataTable | | `Spinner` | `@/components/ui/Spinner` | Loading indicators | | `PageHeader` | `@/components/ui/PageHeader` | Page title block — every page starts with this | | `Card` | `@/components/ui/Card` | Contained content sections | | `Tabs` | `@/components/ui/Tabs` | Tabbed navigation within a page | | `Toast` | `@/components/ui/Toast` | Transient notifications (via `useToast`) | | `SearchBar` | `@/components/ui/SearchBar` | Search inputs with debounce | | `Breadcrumbs` | `@/components/ui/Breadcrumbs` | Navigation trail on detail pages | | `Icon` | `@/components/ui/Icon` | Inline SVG icons by name | --- ## Folder Structure — Pages & Modals Folders mirror the sidebar section hierarchy exactly. ``` frontend/src/pages/ ├── auth/ ← unauthenticated routes (login) ├── dashboard/ ← General section ├── bellcloud/ ← Bell Cloud section │ ├── devices/ │ │ └── notes/ │ ├── users/ │ ├── melodies/ │ │ └── archetypes/ │ └── mqtt/ ├── crm/ ← Headquarters section │ ├── comms/ │ │ └── mail/ │ ├── customers/ │ │ └── tabs/ │ ├── orders/ │ ├── quotations/ │ └── products/ ├── engineering/ ← Engineering section │ ├── manufacturing/ │ ├── firmware/ │ └── developer/ ├── public/ ← Public / unauthenticated pages │ ├── cloudflash/ │ └── serial/ ├── settings/ ← Console Settings section │ └── staff/ └── dev/ ← Internal dev tools (StyleGuide) ``` ``` frontend/src/modals/ ├── bellcloud/ │ ├── devices/ │ ├── melodies/ │ └── users/ ├── crm/ │ └── products/ ├── engineering/ │ └── manufacturing/ └── shared/ ``` --- ## Page Layout — How Every Page Is Structured Every authenticated page is rendered inside `MainLayout`, which provides: - **Sidebar** — fixed left, `224px` wide (`--sidebar-width`) - **Header** — fixed top, `56px` tall (`--header-height`) - **Content area** — the remaining viewport space Inside the content area, every page uses the `.page-wrapper` class (defined in `global.css`): ```css .page-wrapper { flex: 1; display: flex; flex-direction: column; padding: var(--space-12); /* 48px on all sides — desktop */ gap: var(--space-6); /* 24px between top-level sections */ min-width: 0; } @media (max-width: 768px) { .page-wrapper { padding: var(--space-8); /* 32px on mobile */ gap: var(--space-4); } } ``` **This is the consistency guarantee**: because every page uses `.page-wrapper`, the `` title on every page starts at exactly the same position — 48px from the top and 48px from the left edge of the content area. Never override these paddings. Never add extra wrappers around `page-wrapper` that introduce additional offset. ### Content width modes Pages fall into two modes — choose based on how much content the page has: **Full-width** (default — lists, tables, dashboards): ```jsx
``` **Centered** (forms, settings, pages with very few items): ```jsx
``` Centered mode caps each direct child at `--content-max-width-sm` (640px) by default and centers it horizontally. Override when needed: ```jsx
``` Available tokens: `--content-max-width-xs` (480px), `--content-max-width-sm` (640px), `--content-max-width-md` (800px), `--content-max-width-lg` (1024px). --- ## Rules for Every Page ### Before writing any code 1. Read `DESIGN.md` — confirm tokens and components to use 2. Check `frontend/src/components/ui/` — use existing components only 3. Check the Style Guide at `/dev/styleguide` for the correct variant/props 4. Check `frontend/src/_archive/` for the equivalent v1 page — copy API calls and data shape only, never styling ### Page template ```jsx // frontend/src/pages/[domain]/PageName.jsx import PageHeader from '@/components/ui/PageHeader' import { useAuth } from '@/hooks/useAuth' // Import ONLY from @/components/ui/ — never raw HTML elements for styled things export default function PageName() { // 1. Auth const { user } = useAuth() // 2. State & data fetching // 3. Event handlers // 4. Render — always handle: loading, error, empty, data states return (
{/* Action buttons — use
) } ``` ### Styling rules - **Tailwind** for layout only: `flex`, `grid`, `items-center`, `min-w-0`, etc. - **CSS token variables** for ALL colors, spacing, typography — `var(--token-name)` - **No** `.module.css` or per-page scoped CSS files - **No** `style={{ }}` inline styles except for genuinely dynamic values (e.g. calculated widths) - **No** raw hex, rgb, or pixel values anywhere ### Toolbar buttons — matching SearchBar height `.btn` has `line-height: 1` while `.searchbar-input` has `line-height: var(--line-height-base)` (1.5). This makes buttons shorter than the search bar by default. Whenever a ` // Option B (preferred when multiple buttons share a toolbar) — scoped CSS block: <>
``` ### Date & time formatting - **Greek/European date style everywhere** — DD/MM/YYYY, never US-style MM/DD/YYYY - **All date/time formatting must use `@/lib/formatters`** — never use raw `toLocaleDateString()`, `Intl.DateTimeFormat`, `toLocaleString()`, or `toISOString().slice()` in pages or modals - **For `datetime-local` input values** — use `toDatetimeLocal(iso)` and `nowLocal()` from formatters. Never use `new Date(x).toISOString().slice(0, 16)` — it converts to UTC and shifts the time (timezone bug) - **Currency** — use `fmtEuro(n)` from formatters (Greek locale: `1.250,00 €`) Available formatters (`import { ... } from '@/lib/formatters'`): | Function | Output example | Use for | |---------------------|------------------------------------|--------------------------------------| | `fmtDate` | `05/03/2026` | Short numeric dates (tables, lists) | | `fmtDateMedium` | `5 Mar 2026` | Medium dates (cards, details) | | `fmtDateLong` | `5 March 2026` | Long dates (headings, summaries) | | `fmtDateFull` | `Wednesday, 5 March 2026` | Dashboard, full context | | `fmtDateTime` | `5 March 2026, 2:30 pm` | Date + 12h time | | `fmtDateTimeMedium` | `5 Mar 2026, 14:30` | Date + 24h time (compact) | | `fmtDateTimeFull` | `Wed, 5 Mar 2026, 2:30 pm` | Emails, comms | | `fmtTime24` | `14:30:05` | Time with seconds | | `fmtRelative` | `5 minutes ago` | Relative timestamps | | `toDatetimeLocal` | `2026-03-05T14:30` | `datetime-local` input values | | `nowLocal` | `2026-03-05T14:30` | Current time for form defaults | | `toDateInput` | `2026-03-05` | `date` input values | | `fmtEuro` | `1.250,00 €` | Euro currency | ### Data fetching - Use `frontend/src/lib/api.js` (wraps `_archive/api/client.js`) - Every data-fetching component must handle **loading**, **error**, and **empty** states ### Modals - Never defined inside page files - Live in `frontend/src/modals/[sidebar-section]/[domain]/ModalName.jsx` — mirror the pages folder structure - Pass data via props, actions via callbacks --- ## Missing Components — Stop and Ask If building a page requires a UI component that does not exist in frontend/src/components/ui/, Claude Code must STOP and say: "I need a [ComponentName] component which doesn't exist yet. Please build it and add it to the StyleGuide before I continue." Do NOT: - Invent an inline one-off component inside a page file - Use raw HTML elements styled with inline CSS as a substitute - Proceed and leave a placeholder The StyleGuide at frontend/src/pages/dev/StyleGuide.jsx is the source of truth for what components exist and how they look. Every component used in a page must have a visible example there first. ## Building a New Page — Checklist - [ ] File in correct `frontend/src/pages/[domain]/` folder - [ ] Root element is `
` — nothing else, nothing wrapping it - [ ] First child inside `page-wrapper` is `` - [ ] Only components from `frontend/src/components/ui/` used - [ ] No raw hex colors or pixel spacing values anywhere - [ ] Loading state implemented - [ ] Error state implemented - [ ] Empty state implemented - [ ] Mobile responsive (375px minimum) - [ ] Modals in `frontend/src/modals/` - [ ] Route added to `frontend/src/router/index.jsx` --- ## API Client ```js // frontend/src/lib/api.js export { default } from '../_archive/api/client' ``` All pages import from `@/lib/api`, never directly from `_archive`. --- ## Auth `frontend/src/hooks/useAuth.js` re-exports from the archive AuthContext. `frontend/src/providers/AuthProvider.jsx` re-exports the AuthProvider. These are the ONLY two files permitted to import from `_archive/auth/`. All other files use `@/hooks/useAuth`.