Files
bellsystems-cp/CLAUDE.md

16 KiB

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/:

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):

.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):

<div className="page-wrapper">

Centered (forms, settings, pages with very few items):

<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:

<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

// 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>:

// 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

// 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.