Initial Switch to V2. Completely Overhauled Backend, Frontend and General Structure.
This commit is contained in:
383
CLAUDE.md
Normal file
383
CLAUDE.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# 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 `<PageHeader>` 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
|
||||
<div className="page-wrapper">
|
||||
```
|
||||
|
||||
**Centered** (forms, settings, pages with very few items):
|
||||
```jsx
|
||||
<div className="page-wrapper page-wrapper--centered">
|
||||
```
|
||||
Centered mode caps each direct child at `--content-max-width-sm` (640px) by default and centers it horizontally. Override when needed:
|
||||
```jsx
|
||||
<div className="page-wrapper page-wrapper--centered"
|
||||
style={{ '--page-content-max-width': 'var(--content-max-width-md)' }}>
|
||||
```
|
||||
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 (
|
||||
<div className="page-wrapper">
|
||||
<PageHeader title="Page Title" subtitle="Optional description">
|
||||
{/* Action buttons — use <Button variant="primary"> etc. */}
|
||||
</PageHeader>
|
||||
|
||||
{/* Page content — use Card, DataTable, Tabs, etc. */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 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 `<Button>`, `<SegmentedControl>`, or `<IconButtonGroup>` sits in the same toolbar row as a `<SearchBar>`, all buttons must use `padding-top/bottom: var(--space-3)` and `line-height: var(--line-height-base)`.
|
||||
|
||||
**Already handled automatically (no extra props needed):**
|
||||
- `SegmentedControl` — `.seg-ctrl__btn` in `components.css` enforces `--space-3` padding and `var(--line-height-base)` globally.
|
||||
- `IconButtonGroup` — `.icon-btn-group__btn` in `components.css` enforces `--space-3` padding globally.
|
||||
|
||||
**Must be overridden manually — standalone `<Button>` in the same row as a `<SearchBar>`:**
|
||||
|
||||
```jsx
|
||||
// Option A — inline style prop on the button:
|
||||
<Button
|
||||
size="md"
|
||||
style={{ paddingTop: 'var(--space-3)', paddingBottom: 'var(--space-3)', lineHeight: 'var(--line-height-base)' }}
|
||||
>
|
||||
Label
|
||||
</Button>
|
||||
|
||||
// Option B (preferred when multiple buttons share a toolbar) — scoped CSS block:
|
||||
<>
|
||||
<style>{`
|
||||
.my-toolbar .btn {
|
||||
padding-top: var(--space-3) !important;
|
||||
padding-bottom: var(--space-3) !important;
|
||||
line-height: var(--line-height-base) !important;
|
||||
}
|
||||
`}</style>
|
||||
<div className="my-toolbar" style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)' }}>
|
||||
<IconButtonGroup … />
|
||||
<Button variant="primary" size="md">Compose</Button>
|
||||
</div>
|
||||
</>
|
||||
```
|
||||
|
||||
### 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 `<div className="page-wrapper">` — nothing else, nothing wrapping it
|
||||
- [ ] First child inside `page-wrapper` is `<PageHeader title="...">`
|
||||
- [ ] 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`.
|
||||
Reference in New Issue
Block a user