diff --git a/.stitch/DESIGN.md b/.stitch/DESIGN.md new file mode 100644 index 0000000..fb91c36 --- /dev/null +++ b/.stitch/DESIGN.md @@ -0,0 +1,133 @@ +# Design System: BellSystems Console Design +**Project ID:** 18406618574074411899 + +--- + +## 1. Visual Theme & Atmosphere + +**"The Digital Observatory"** — A high-fidelity, immersive enterprise command center aesthetic that rejects the typical boxed-in SaaS feel. The mood is best described as **Atmospheric Depth**: dark, spacious, and precision-focused. Inspired by high-end editorial design and command center interfaces, data is treated as a premium asset surfaced through tonal layering rather than structural lines. + +The base is built on **Midnight Navy** — a deep blue-black that evokes an infinite canvas — with UI elements that appear to float and glow rather than sit flat. Hierarchy is established exclusively through surface tone shifts; hard borders are forbidden. The result feels like peering through a high-resolution window into enterprise data, where clarity comes from contrast in depth rather than weight. + +Overall density is **medium-high** — information-rich layouts with generous vertical whitespace between elements, but no wasted screen real estate. + +--- + +## 2. Color Palette & Roles + +### Core Surfaces (darkest to lightest) +| Name | Hex | Role | +|---|---|---| +| **Abyss** | `#0a0e14` | Deepest well; nested content backgrounds | +| **Midnight** | `#10141a` | Main application viewport / page background | +| **Void Navy** | `#181c22` | Sidebar, header, and secondary navigation surfaces | +| **Deep Slate** | `#1c2026` | Default card and information module background | +| **Elevated Slate** | `#262a31` | High cards, hovered table rows | +| **Island** | `#31353c` | Active states, selected rows, top-layer containers | +| **Frosted Glass** | `#353940` | Floating modals and dropdowns (at 80% opacity with blur) | + +### Accent & Brand Colors +| Name | Hex | Role | +|---|---|---| +| **Indigo Glow** | `#c0c1ff` | Primary accent; CTA text, key metrics, active nav indicator | +| **Lavender Soft** | `#8083ff` | Primary container fills | +| **Violet Pulse** | `#d2bbff` | Secondary accent; gradient pair to Indigo Glow | +| **Deep Indigo** | `#6001d1` | Secondary container fills | +| **Royal Indigo** | `#494bd6` | Inverse primary; used on light-over-dark contexts | +| **Aqua Sky** | `#7bd0ff` | Tertiary accent; data visualization, device status indicators | +| **Ocean** | `#009bd1` | Tertiary container | + +### Semantic / State Colors +| Name | Hex | Role | +|---|---|---| +| **Coral Error** | `#ffb4ab` | Error text and destructive action labels | +| **Crimson** | `#93000a` | Error container backgrounds | +| **Emerald** (Tailwind) | `emerald-*` | Online / active device status badges | +| **Amber** (Tailwind) | `amber-*` | Warning / pending device status badges | + +### Text Colors +| Name | Hex | Role | +|---|---|---| +| **Cloud** | `#dfe2eb` | Primary text; body copy, data values, headings | +| **Mist** | `#c7c4d7` | Secondary / muted text; labels, metadata | +| **Ghost** | `#908fa0` | Placeholder text, disabled states, divider fill | +| **Boundary** | `#464554` | Ghost border fallback for inputs (used at low opacity) | + +--- + +## 3. Typography Rules + +**Single font family throughout: Inter** (geometric sans-serif). Used for headlines, body, and labels alike — unity is achieved through weight and tracking variation rather than font switching. + +| Level | Size | Weight | Tracking | Usage | +|---|---|---|---|---| +| **Display** | 56px / 3.5rem | Bold (700) | Tight (negative) | Hero KPI metrics, total counts | +| **Headline** | 24px / 1.5rem | SemiBold (600) | Normal | Page titles, major section headings | +| **Title** | 16px / 1.0rem | Medium (500) | Normal | Card titles, module headers, tab labels | +| **Body** | 14px / 0.875rem | Regular (400) | Normal | Default text, table rows, descriptions | +| **Label** | 11px / 0.6875rem | SemiBold (600) | Wide (+0.1em) | Sidebar category headers, metadata chips — All Caps | + +**The Editorial Rule:** Sidebar category headers use the Label style in all-caps with wide letter-spacing (+0.1em). This creates a "system-level" authority that contrasts with fluid body text and signals structural navigation. Body text must never be all-caps. + +--- + +## 4. Component Stylings + +### Buttons +- **Primary CTA:** Gradient fill from Indigo Glow (`#c0c1ff`) to Violet Pulse (`#d2bbff`). White text. Softly rounded corners (matching `lg` radius — just barely rounded, not pill). On hover, a high-glow indigo shadow emanates beneath the button. +- **Secondary / Outline:** Island background (`#31353c`) with an extremely faint ghost border — outline-variant (`#464554`) at 20–30% opacity. Mist text (`#c7c4d7`). +- **Tertiary / Ghost:** Fully transparent background. Mist text. On hover: Cloud text with elevated background tint. +- **Destructive:** Coral Error text (`#ffb4ab`) on a Crimson container (`#93000a` at low opacity). Used for delete and irreversible actions. Never bright red. + +### Cards & Containers +- **Default Card:** Deep Slate background (`#1c2026`). No visible border — separation is purely tonal. Features a **subtle top-edge inner glow**: `inset 0px 1px 0px rgba(192, 193, 255, 0.05)` — mimics a ceiling light reflecting off the card's glass surface. +- **Corner Rounding:** Minimal — just enough to soften, not enough to feel playful. Approximately 4px (the `lg` token = 0.25rem). Cards feel rectangular and purposeful, not bubbly. +- **Elevation principle:** No drop shadows on standard cards. Hierarchy comes from the surface color step (Void Navy → Deep Slate → Elevated Slate). +- **Floating Modals / Dropdowns:** Frosted Glass background (`rgba(53, 57, 64, 0.8)`) with `backdrop-filter: blur(12px)`. This "Glassmorphism" effect keeps the user contextually anchored to the underlying page while the modal floats above. Shadow: `0px 8px 24px rgba(13, 17, 23, 0.6)` — navy-tinted, never pure black. + +### Inputs & Forms +- **Default state:** Deep Slate background. Ghost border at very low opacity (near invisible). Text in Cloud color. +- **Focus state:** Indigo Glow (`#c0c1ff`) border at 40% alpha creates a soft halo glow — not a hard ring. The inner glow on the input also subtly strengthens. +- **Placeholder text:** Ghost color (`#908fa0`). +- **Select / Dropdown:** Same surface as inputs; opens as a Frosted Glass panel with blur. +- **No hard outlines at rest** — inputs feel embedded in the surface until interacted with. + +### Status Badges +- **Shape:** Fully pill-shaped (maximum border-radius / `full` = 0.75rem). +- **Style:** Functional color (Emerald, Amber, Coral) at approximately 15% background opacity, with the same color at full 100% opacity for the label text. Creates a "glowing ink" effect — the badge appears illuminated from within. +- **Examples:** Online → soft emerald glow; Warning → soft amber glow; Error/Offline → soft coral glow. + +### Navigation Sidebar (224px wide) +- **Background:** Void Navy (`#181c22`) — one step lighter than the main viewport. +- **Active item indicator:** A `3px` vertical bar on the far-left edge using Indigo Glow (`#c0c1ff`). The text weight also increases slightly. No background highlight on active items — the light bar IS the indicator. +- **Inactive items:** Mist text (`#c7c4d7`) at normal weight. +- **Category headers:** Label-style — all-caps, 11px, SemiBold, wide tracking. Ghost color (`#908fa0`). +- **Item padding:** Comfortable vertical padding (~9.6px / 0.6rem) for breathing room between items. +- **No dividers** between nav sections — spacing does the work. + +### Data Tables +- **Row separation:** No horizontal dividers. Alternating subtle tonal rows (Island `#31353c` on hover) and consistent vertical gap rhythm. +- **Header row:** Mist text (`#c7c4d7`), Label-style capitalization, slightly smaller than body. +- **Hovered row:** Elevated Slate (`#262a31`) or Island (`#31353c`) background. +- **Selected row:** Island background with Indigo Glow left border accent. + +### Scrollbars +- Slim — 4px wide track. +- Thumb: Boundary color (`#464554`), with 2px border-radius. +- Track: Transparent. +- Overall feel: Nearly invisible unless sought out. + +--- + +## 5. Layout Principles + +**Whitespace is structure.** The design never uses lines or dividers to separate sections — space does that job. When content feels disconnected, the solution is always to add vertical breathing room, never to draw a border. + +- **Page content area:** Fills the viewport to the right of the 224px sidebar. Padding inside the content area is generous — approximately 24–32px on all sides. +- **Section spacing:** Major sections within a page are separated by approximately 24px of vertical space. Sub-sections by 16px. +- **Card grid:** Cards sit in fluid grids with 16px gaps. Cards never touch each other. +- **Alignment:** Strong left-edge alignment for all content. Data tables, card headers, and page titles all share the same left origin point. +- **No horizontal rules / `
` elements:** Surface color transitions and whitespace define the visual structure entirely. +- **Modals:** Centered in the viewport, overlaid on a dark scrim. The page content behind is still readable through the frosted glass effect, maintaining spatial context. +- **The "No Raw Border" rule:** Any element requiring a visible boundary for accessibility (e.g., an active input) must use the ghost border approach — Boundary color (`#464554`) at 20% opacity maximum. Full-opacity borders are strictly prohibited. +- **Mobile / responsive:** The sidebar collapses to a drawer on narrow viewports. Cards reflow to single-column. The design's depth relies on background layers, so it translates naturally to smaller screens. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7cd53c0 --- /dev/null +++ b/CLAUDE.md @@ -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 `` 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`. diff --git a/CRM_STATUS_SYSTEM_PLAN.md b/CRM_STATUS_SYSTEM_PLAN.md deleted file mode 100644 index ef9ca3e..0000000 --- a/CRM_STATUS_SYSTEM_PLAN.md +++ /dev/null @@ -1,404 +0,0 @@ -# CRM Customer Status System — Implementation Plan - -## Context - -This project is a Vue/React + FastAPI + Firestore admin console located at `C:\development\bellsystems-cp`. - -The frontend lives in `frontend/src/` and the backend in `backend/`. -The CRM module is at `frontend/src/crm/` and `backend/crm/`. - -Currently, customers have two flat boolean flags on their Firestore document: -- `negotiating: bool` -- `has_problem: bool` - -These need to be replaced with a richer, structured system as described below. - ---- - -## 1. Target Data Model - -### 1A. On the Customer Document (`customers/{id}`) - -Remove `negotiating` and `has_problem`. Add the following: - -``` -relationship_status: string - — one of: "lead" | "prospect" | "active" | "inactive" | "churned" - — default: "lead" - -technical_issues: array of { - active: bool, - opened_date: Firestore Timestamp, - resolved_date: Firestore Timestamp | null, - note: string, - opened_by: string, ← display name or user ID of staff member - resolved_by: string | null -} - -install_support: array of { - active: bool, - opened_date: Firestore Timestamp, - resolved_date: Firestore Timestamp | null, - note: string, - opened_by: string, - resolved_by: string | null -} - -transaction_history: array of { - date: Firestore Timestamp, - flow: string, ← "invoice" | "payment" | "refund" | "credit" - payment_type: string | null, ← "cash" | "bank_transfer" | "card" | "paypal" — null for invoices - category: string, ← "full_payment" | "advance" | "installment" - amount: number, - currency: string, ← default "EUR" - invoice_ref: string | null, - order_ref: string | null, ← references an order document ID, nullable - recorded_by: string, - note: string -} -``` - -### 1B. Orders Subcollection (`customers/{id}/orders/{order_id}`) - -Orders live **exclusively** as a subcollection under each customer. There is no top-level `orders` -collection. The existing top-level `orders` collection in Firestore and its corresponding backend -routes should be **removed entirely** and replaced with subcollection-based routes under -`/crm/customers/{customer_id}/orders/`. - -If cross-customer order querying is ever needed in the future, use Firestore's native -`collectionGroup("orders")` query — no top-level mirror collection is required. - -Each order document carries the following fields: - -``` -order_number: string ← e.g. "ORD-2026-041" (already exists — keep) -title: string ← NEW: human-readable name e.g. "3x Wall Mount Units - Athens Office" -created_by: string ← NEW: staff user ID or display name - -status: string ← REPLACE existing OrderStatus enum with new values: - — "negotiating" | "awaiting_quotation" | "awaiting_customer_confirmation" - | "awaiting_fulfilment" | "awaiting_payment" | "manufacturing" - | "shipped" | "installed" | "declined" | "complete" - -status_updated_date: Firestore Timestamp ← NEW -status_updated_by: string ← NEW - -payment_status: object { ← NEW — replaces the flat PaymentStatus enum - required_amount: number, - received_amount: number, ← computed from transaction_history where order_ref matches - balance_due: number, ← computed: required_amount - received_amount - advance_required: bool, - advance_amount: number | null, - payment_complete: bool -} - -timeline: array of { ← NEW — order event log - date: Firestore Timestamp, - type: string, ← "quote_request" | "quote_sent" | "quote_accepted" | "quote_declined" - | "mfg_started" | "mfg_complete" | "order_shipped" | "installed" - | "payment_received" | "invoice_sent" | "note" - note: string, - updated_by: string -} -``` - ---- - -## 2. Backend Changes - -### 2A. `backend/crm/models.py` - -- **Remove** `negotiating: bool` and `has_problem: bool` from `CustomerCreate` and `CustomerUpdate`. -- **Add** `relationship_status: Optional[str] = "lead"` to `CustomerCreate` and `CustomerUpdate`. -- **Add** `technical_issues: List[dict] = []` to `CustomerCreate` and `CustomerUpdate`. -- **Add** `install_support: List[dict] = []` to `CustomerCreate` and `CustomerUpdate`. -- **Add** `transaction_history: List[dict] = []` to `CustomerCreate` and `CustomerUpdate`. -- **Add** proper Pydantic models for each of the above array item shapes: - - `TechnicalIssue` model - - `InstallSupportEntry` model - - `TransactionEntry` model -- **Update** `OrderStatus` enum with the new values: - `negotiating`, `awaiting_quotation`, `awaiting_customer_confirmation`, - `awaiting_fulfilment`, `awaiting_payment`, `manufacturing`, - `shipped`, `installed`, `declined`, `complete` -- **Replace** the flat `PaymentStatus` enum on `OrderCreate` / `OrderUpdate` with a new `OrderPaymentStatus` Pydantic model matching the structure above. -- **Add** `title: Optional[str]`, `created_by: Optional[str]`, `status_updated_date: Optional[str]`, - `status_updated_by: Optional[str]`, and `timeline: List[dict] = []` to `OrderCreate` and `OrderUpdate`. - -### 2B. `backend/crm/customers_router.py` - -- Update any route that reads/writes `negotiating` or `has_problem` to use the new fields. -- Add new dedicated endpoints: - -``` -POST /crm/customers/{id}/technical-issues - — body: { note: str, opened_by: str } - — appends a new active issue to the array - -PATCH /crm/customers/{id}/technical-issues/{index}/resolve - — body: { resolved_by: str } - — sets active=false and resolved_date=now on the item at that index - -POST /crm/customers/{id}/install-support - — same pattern as technical-issues above - -PATCH /crm/customers/{id}/install-support/{index}/resolve - — same as technical-issues resolve - -POST /crm/customers/{id}/transactions - — body: TransactionEntry (see model above) - — appends to transaction_history - -PATCH /crm/customers/{id}/relationship-status - — body: { status: str } - — updates relationship_status field -``` - -### 2C. `backend/crm/orders_router.py` - -- **Remove** all top-level `/crm/orders/` routes entirely. -- Re-implement all order CRUD under `/crm/customers/{customer_id}/orders/`: - -``` -GET /crm/customers/{customer_id}/orders/ -POST /crm/customers/{customer_id}/orders/ -GET /crm/customers/{customer_id}/orders/{order_id} -PATCH /crm/customers/{customer_id}/orders/{order_id} -DELETE /crm/customers/{customer_id}/orders/{order_id} -``` - -- Add endpoint to append a timeline event: - -``` -POST /crm/customers/{customer_id}/orders/{order_id}/timeline - — body: { type: str, note: str, updated_by: str } - — appends to the timeline array and updates status_updated_date + status_updated_by -``` - -- Add endpoint to update payment status: - -``` -PATCH /crm/customers/{customer_id}/orders/{order_id}/payment-status - — body: OrderPaymentStatus fields (partial update allowed) -``` - -- Add a dedicated "Init Negotiations" endpoint: - -``` -POST /crm/customers/{customer_id}/orders/init-negotiations - — body: { title: str, note: str, date: datetime, created_by: str } - — creates a new order with status="negotiating", auto-fills all other fields - — simultaneously updates the customer's relationship_status to "active" - (only if currently "lead" or "prospect" — do not downgrade an already "active" customer) - — returns the newly created order document -``` - ---- - -## 3. Frontend Changes - -### 3A. `frontend/src/crm/customers/CustomerList.jsx` - -- When Notes: Quick filter is set, replace the `negotiating` and `has_problem` boolean badge display in the Status column with: - - A **relationship status chip** (color-coded pill: lead=grey, prospect=blue, active=green, inactive=amber, churned=soft red) - - A small **red dot / warning icon** if `technical_issues.some(i => i.active)` is true, under a new "Support" column. Add this column to the list of arrangeable and toggleable columns. - - A small **amber dot / support icon** if `install_support.some(i => i.active)` is true, under the same "Support" column. - - These are derived from the arrays — do not store a separate boolean on the document. -- When Notes: Expanded filter is set, replace the `negotiating` and `has_problem` verbose displays with the active order status (if any) in this format: - `""` e.g. `"Negotiating — 24.03.26 — Customer requested a more affordable quotation"` - -### 3B. `frontend/src/crm/customers/CustomerDetail.jsx` - -The customer detail page currently has a tab structure: Overview, Orders, Quotations, Communication, Files & Media, Devices. - -Make the following changes: - -#### Whole page -- On the top of the page where we display the name, organization and full address, change it to: - Line 1: `Full Title + Name + Surname` - Line 2: `Organization · City` (city only, not full address) -- Remove the horizontal separation line after the title and before the tabs. -- On the top right side, there is an Edit Customer button. To its left, add **3 new buttons** in this - order (left → right): **Init Negotiations**, **Record Issue/Support**, **Record Payment**, then - the existing Edit button. All 4 buttons are the same size. Add solid single-color icons to each. - - **"Init Negotiations" button** (blue/indigo accent): - - Opens a mini modal. - - Fields: Date (defaults to NOW), Title (text input, required), Note (textarea, optional). - - Auto-filled server-side: `status = "negotiating"`, `created_by` = current user, - `status_updated_date` = now, `status_updated_by` = current user, - `payment_status` defaults to zeroed object. - - On confirm: calls `POST /crm/customers/{id}/orders/init-negotiations`. - - After success: refreshes customer data and orders list. The customer's `relationship_status` - is set to `"active"` server-side — no separate frontend call needed. - - This is a fast-entry shortcut only. All subsequent edits to this order happen via the Orders tab. - - **"Record Issue/Support" button** (amber/orange accent): - - Opens a mini modal. - - At the top: a **2-button toggle selector** (not a dropdown) to choose: `Technical Issue` | `Install Support`. - - Fields: Date (defaults to NOW), Note (textarea, required). - - On confirm: calls `POST /crm/customers/{id}/technical-issues` or - `POST /crm/customers/{id}/install-support` depending on selection. - - **"Record Payment" button** (green accent): - - Opens a mini modal. - - Fields: Date (defaults to NOW), Payment Type (cash | bank transfer | card | paypal), - Category (full payment | advance | installment), Amount (number), Currency (defaults to EUR), - Invoice Ref (searchable over the customer's invoices, optional), - Order Ref (searchable/selectable from the customer's orders, optional), - Note (textarea, optional). - - On confirm: calls `POST /crm/customers/{id}/transactions`. - -#### Overview Tab -- The main hero section gets a complete overhaul — start fresh: - - **Row 1 — Relationship Status selector**: The 5 statuses (`lead | prospect | active | inactive | churned`) as styled pill/tab buttons in a row. Current status is highlighted with a glow effect. Color-code using global CSS variables (add to `index.css` if not already present). Clicking a status immediately calls `PATCH /crm/customers/{id}/relationship-status`. - - **Row 2 — Customer info**: All fields except Name and Organization (shown in page header). Include language, religion, tags, etc. - - **Row 3 — Contacts**: All contact entries (phone, email, WhatsApp, etc.). - - **Row 4 — Notes**: Responsive column grid. 1 column below 1100px, 2 columns 1100–2000px, 3 columns above 2000px. Masonry/wrap layout with no gaps between note cards. -- Move the Latest Orders section to just below the hero section, before Latest Communications. - Hide this section entirely if no orders exist for this customer. -- For all other sections (Latest Communications, Latest Quotations, Devices): hide each section - entirely if it has no data. Show dynamically when data exists. - -#### New "Support" Tab (add to TABS array, after Overview) -Two full-width section cards: - - **Technical Issues Card** - - Header shows active count badge (e.g. "2 active") - - All issues listed, newest first (active and resolved) - - Each row: colored status dot, opened date, note, opened_by — "Resolve" button if active - - If more than 5 items: list is scrollable (fixed max-height), does not expand the page - - "Report New Issue" button → small inline form with note field + submit - - **Install Support Card** - - Identical structure to Technical Issues card - - Same scrollable behavior if more than 5 items - -#### New "Financials" Tab (add to TABS array, after Support) -Two sections: - - **Active Order Payment Status** (shown only if an active order exists) - - required_amount, received_amount, balance_due - - Advance required indicator + advance amount if applicable - - Payment complete indicator - - **Transaction History** - - Ledger table: Date | Flow | Amount | Currency | Method | Category | Order Ref | Invoice Ref | Note | Recorded By | Actions - - "Add Transaction" button → modal with all TransactionEntry fields - - Totals row: Total Invoiced vs Total Paid vs Outstanding Balance - - Each row: right-aligned **Actions** button (consistent with other tables in the project) - with options: **Edit** (opens edit form) and **Delete** (requires confirmation dialog) - -#### Orders Tab (existing — update in place) -- Each order card/row shows: - - `title` as primary heading - - `status` with human-readable label and color coding (see Section 4) - - `payment_status` summary: required / received / balance due - - **"View Timeline"** toggle: expands a vertical event log below the order card - - **"Add Timeline Event"** button: small inline form with type dropdown + note field -- Update all API calls to use `/crm/customers/{customer_id}/orders/` routes. - -### 3C. `frontend/src/crm/customers/CustomerForm.jsx` - -- Remove `negotiating` and `has_problem` fields. -- Add `relationship_status` dropdown (default: `"lead"`). -- No issue/transaction forms needed here — managed from the detail page. - -### 3D. `frontend/src/crm/orders/OrderForm.jsx` and `OrderDetail.jsx` - -- Update status dropdown with new values and labels: - - `negotiating` → "Negotiating" - - `awaiting_quotation` → "Awaiting Quotation" - - `awaiting_customer_confirmation` → "Awaiting Customer Confirmation" - - `awaiting_fulfilment` → "Awaiting Fulfilment" - - `awaiting_payment` → "Awaiting Payment" - - `manufacturing` → "Manufacturing" - - `shipped` → "Shipped" - - `installed` → "Installed" - - `declined` → "Declined" - - `complete` → "Complete" -- Add `title` input field (required). -- Replace flat `payment_status` enum with the new `payment_status` object fields. -- Add Timeline section to `OrderDetail.jsx`: vertical event log + add-entry inline form. -- Update all API calls to use `/crm/customers/{customer_id}/orders/` routes. - ---- - -## 4. Status Color Coding Reference - -Define all as CSS variables in `index.css` and use consistently across all views: - -### Relationship Status -| Status | Color | -|---|---| -| lead | grey / muted | -| prospect | blue | -| active | green | -| inactive | amber | -| churned | dark or soft red | - -### Order Status -| Status | Color | -|---|---| -| negotiating | blue | -| awaiting_quotation | purple | -| awaiting_customer_confirmation | indigo | -| awaiting_fulfilment | amber | -| awaiting_payment | orange | -| manufacturing | cyan | -| shipped | teal | -| installed | green | -| declined | red | -| complete | muted/grey | - -### Issue / Support Flags -| State | Color | -|---|---| -| active issue | red | -| active support | amber | -| resolved | muted/grey | - ---- - -## 5. Migration Notes - -- The old `negotiating` and `has_problem` fields will remain in Firestore until the migration script is run. The backend should **read both old and new fields** during the transition period, preferring the new structure if present. -- A one-time migration script (`backend/migrate_customer_flags.py`) should: - 1. Read all customer documents - 2. If `negotiating: true` → create an order in the customer's `orders` subcollection with `status = "negotiating"` and set `relationship_status = "active"` on the customer - 3. If `has_problem: true` → append one entry to `technical_issues` with `active: true`, `opened_date: customer.updated_at`, `note: "Migrated from legacy has_problem flag"`, `opened_by: "system"` - 4. Remove `negotiating` and `has_problem` from the customer document -- Do **not** run the migration script until all frontend and backend changes are deployed and tested. - ---- - -## 6. File Summary — What to Touch - -``` -backend/crm/models.py ← model updates (primary changes) -backend/crm/customers_router.py ← new endpoints + field updates -backend/crm/orders_router.py ← remove top-level routes, re-implement as subcollection, - add timeline + payment-status + init-negotiations endpoints -backend/migrate_customer_flags.py ← NEW one-time migration script - -frontend/src/index.css ← add CSS variables for all new status colors -frontend/src/crm/customers/CustomerList.jsx ← relationship status chip + support flag dots column -frontend/src/crm/customers/CustomerDetail.jsx ← page header, 3 new quick-entry buttons + modals, - Overview tab overhaul, new Support tab, - new Financials tab, Orders tab updates -frontend/src/crm/customers/CustomerForm.jsx ← remove old flags, add relationship_status -frontend/src/crm/orders/OrderForm.jsx ← new status values, title field, payment_status, - updated API route paths -frontend/src/crm/orders/OrderDetail.jsx ← timeline section, updated status/payment, - updated API route paths -``` - ---- - -## 7. Do NOT Change (out of scope) - -- Quotations system — leave as-is -- Communications / inbox — leave as-is -- Files & Media tab — leave as-is -- Devices tab — leave as-is -- Any other module outside `crm/` diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..ef90f88 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,627 @@ +# DESIGN SYSTEM — BellSystems Control Panel v2 + +> Single source of truth for all UI/UX decisions. +> Read before writing any page, component, or modal. +> Never override these rules inline. Change the rule here first, then propagate. +> +> Live reference: `/dev/styleguide` — every component, every variant, every state. + +--- + +## 1. Core Philosophy + +- **Consistency over creativity.** Every page must feel like it belongs to the same product. +- **Tokens over hardcoded values.** Never write a raw color, spacing value, or font size. Always `var(--token)`. +- **Components over repetition.** If you write the same pattern twice, it becomes a shared component. +- **Page layout is global.** All pages share the same padding and spacing anchors. Content always starts at the same position. +- **Accessible by default.** ARIA labels, keyboard navigation, visible focus states on all interactive elements. + +--- + +## 2. Page Layout Anatomy + +Every authenticated page lives inside `MainLayout`, which provides: + +``` +┌─────────────┬────────────────────────────────────────────┐ +│ │ HEADER (height: 56px / --header-height) │ +│ │────────────────────────────────────────────┤ +│ │ │ +│ SIDEBAR │ CONTENT AREA │ +│ (224px / │ ┌──────────────────────────────────────┐ │ +│ --sidebar- │ │ .page-wrapper │ │ +│ width) │ │ padding: 48px (--space-12) │ │ +│ │ │ gap: 24px between sections │ │ +│ │ │ │ │ +│ │ │ ← always first │ │ +│ │ │ │ │ +│ │ └──────────────────────────────────────┘ │ +└─────────────┴────────────────────────────────────────────┘ +``` + +### The consistency guarantee + +`.page-wrapper` is defined once in `global.css`. It is the only wrapper used on every page: + +```css +.page-wrapper { + flex: 1; + display: flex; + flex-direction: column; + padding: var(--space-12); /* 48px — desktop */ + gap: var(--space-6); /* 24px between direct children */ + min-width: 0; +} +/* Mobile: padding drops to --space-8 (32px), gap to --space-4 (16px) */ +``` + +**Rules:** +- Every page's root element is `
` — no exceptions +- Never add extra padding, margin, or wrapper divs that shift content relative to `.page-wrapper` +- Never override `.page-wrapper` padding per-page +- The `` is always the first child inside `.page-wrapper` +- This ensures that on every page, the title starts at exactly 48px from the top-left corner of the content area + +### Content width modes + +Every page falls into one of two modes: + +#### 1. Full-width (default) +Content expands to fill the entire available area. Use this for all data-heavy pages: lists, tables, dashboards, detail views. + +```jsx +
+ {/* content fills the full content area */} +
+``` + +#### 2. Centered (narrow content) +For pages with a small number of elements that would look lost spanning the full viewport — e.g. settings forms, auth pages, single-entity configuration screens. + +```jsx +
+ {/* every direct child is capped at --content-max-width-sm (640px) and centered */} +
+``` + +Default max-width is `--content-max-width-sm` (640px). Override per-page only when necessary: + +```jsx +
+``` + +Available width tokens: + +| Token | Value | Use | +|-----------------------------|--------|-----------------------------------------| +| `--content-max-width-xs` | 480px | Tiny forms, login, auth | +| `--content-max-width-sm` | 640px | Small forms, simple settings (default) | +| `--content-max-width-md` | 800px | Medium forms, detail-light pages | +| `--content-max-width-lg` | 1024px | Moderate-width constrained pages | + +**Rules:** +- Never use `page-wrapper--centered` on a list page, data table page, or any page where content should grow with the viewport +- Never hardcode a pixel `max-width` in a page file — always use a token + +--- + +## 3. Color Tokens + +All colors are CSS custom properties defined in `frontend/src/styles/tokens.css`. +The system is **dark-first**: `:root` = dark theme. `[data-theme="light"]` overrides exist as a placeholder. + +### Rule: never write a raw color value in any component or page file. Always `var(--token)`. + +### Background Surfaces (7-step tonal ladder) + +| Token | Value | Use | +|------------------------|--------------|--------------------------------------------------------| +| `--color-bg-abyss` | `#0a0e14` | Deepest well: code blocks, input backgrounds | +| `--color-bg-base` | `#10141a` | Page background (viewport fill) | +| `--color-bg-void` | `#181c22` | Sidebar, header | +| `--color-bg-surface` | `#1c2026` | Default card / panel background | +| `--color-bg-elevated` | `#262a31` | Raised cards, hovered rows, dropdowns | +| `--color-bg-island` | `#31353c` | Active states, selected rows, pressed buttons | +| `--color-bg-float` | `rgba(53,57,64,0.80)` | Glassmorphism: modals, floating panels | + +### Brand / Primary (Indigo Glow) + +| Token | Value | Use | +|----------------------------|------------------------------|--------------------------------------| +| `--color-primary` | `#c0c1ff` | CTAs, active nav, key accent | +| `--color-primary-hover` | `#d2bbff` | Hover, gradient endpoint | +| `--color-primary-container`| `#8083ff` | Container fills | +| `--color-primary-subtle` | `rgba(128,131,255,0.12)` | Hover backgrounds, tinted areas | +| `--gradient-primary` | `linear-gradient(135deg, #c0c1ff, #d2bbff)` | Primary button fill | + +### Semantic / State Colors + +| Token | Value | Use | +|------------------------|---------------------------|------------------------------------------------| +| `--color-success` | `#4ade80` | Online, active, confirmed | +| `--color-success-bg` | `rgba(74,222,128,0.12)` | Success badge / button resting background | +| `--color-warning` | `#fbbf24` | Pending, needs attention | +| `--color-warning-bg` | `rgba(251,191,36,0.12)` | Warning badge / button resting background | +| `--color-danger` | `#ff5c5c` | Error text, destructive actions | +| `--color-danger-bg` | `rgba(255,92,92,0.12)` | Danger badge / button resting background | +| `--color-info` | `#7bd0ff` | Informational, aqua-sky accent | +| `--color-info-bg` | `rgba(123,208,255,0.12)` | Info badge background | + +### Text Colors (4-step hierarchy) + +| Token | Value | Use | +|---------------------------|-------------|---------------------------------------------------| +| `--color-text-primary` | `#dfe2eb` | Body copy, data values, headings | +| `--color-text-secondary` | `#c7c4d7` | Labels, metadata, inactive nav | +| `--color-text-muted` | `#908fa0` | Placeholders, disabled, category headers | +| `--color-text-inverse` | `#10141a` | Text on primary/accent backgrounds (dark on light)| +| `--color-text-accent` | `#c0c1ff` | Active nav items, links | + +### Borders + +| Token | Value | Use | +|-------------------------|----------------------------|--------------------------------------------| +| `--color-border` | `rgba(70,69,84,0.20)` | Resting inputs, card outlines | +| `--color-border-strong` | `rgba(70,69,84,0.45)` | Secondary buttons, stronger dividers | +| `--color-border-focus` | `rgba(192,193,255,0.40)` | Focus ring halo on inputs | + +--- + +## 4. Typography + +Two-font system. Three families total. + +### Font Families + +| Token | Font | Role | +|--------------------------|---------------------|---------------------------------------------------| +| `--font-family-display` | `Barlow Condensed` | H1, H2, page titles, modal titles | +| `--font-family-base` | `Onest` | All UI text, body, labels, buttons, table rows | +| `--font-family-mono` | `JetBrains Mono` | Serial numbers, IDs, code, API keys, terminal | + +**Why this pairing:** +- `Barlow Condensed` has an industrial/engineering quality — feels like instrument panel labelling. Makes page titles immediately distinctive. +- `Onest` is a Ukrainian geometric grotesque with slightly unusual proportions and excellent numerics. Clean at 14px. Not the overused Inter/Space Grotesk. +- `JetBrains Mono` is the standard for developer-facing data. + +### Font Sizes + +| Token | Value | Use | +|--------------------|------------|----------------------------------------------| +| `--font-size-xs` | `0.6875rem` (11px) | Labels, sidebar category headers, chips | +| `--font-size-sm` | `0.75rem` (12px) | Captions, helper text, table headers | +| `--font-size-base` | `0.875rem` (14px) | Body text, table rows (default) | +| `--font-size-md` | `1rem` (16px) | Card titles, module headers | +| `--font-size-lg` | `1.125rem` (18px) | Section subheadings | +| `--font-size-xl` | `1.5rem` (24px) | Page headings (h1/h2) | +| `--font-size-2xl` | `3.5rem` (56px) | Hero KPI numbers, dashboard metrics | + +### Font Weights + +| Token | Value | Use | +|---------------------------|-------|----------------------------------------| +| `--font-weight-normal` | 400 | Body copy | +| `--font-weight-medium` | 500 | Emphasized body, table values | +| `--font-weight-semibold` | 600 | Headings, button labels, field labels | +| `--font-weight-bold` | 700 | Strong emphasis, hero metrics | + +### Typography Usage Rules + +- **Page titles (``):** `Barlow Condensed`, `1.75rem`, weight 600 (handled by `.v2-page-header-title`) +- **Modal titles:** `Barlow Condensed`, `1.125rem`, weight 600 (handled by `.v2-modal-title`) +- **H1, H2 globally:** `Barlow Condensed`, `var(--font-size-xl)`, weight 600 — set in `global.css` +- **H3–H6:** `Onest` (body font), normal heading weights +- **Card titles:** `Onest`, `--font-size-base`, weight 600 +- **Table headers:** `Onest`, `--font-size-sm`, weight 600, uppercase, `--tracking-wide` +- **Body / cell text:** `Onest`, `--font-size-base`, weight 400 +- **Muted / helper text:** `Onest`, `--font-size-sm`, `--color-text-muted` +- **Serials, IDs, codes:** `JetBrains Mono`, `--font-size-sm` + +### Letter Spacing + +| Token | Value | Use | +|---------------------|-----------|--------------------------------------------| +| `--tracking-normal` | `0em` | Default | +| `--tracking-tight` | `-0.01em` | Barlow Condensed headings | +| `--tracking-wide` | `0.08em` | Uppercase labels, sidebar category headers | +| `--tracking-display`| `-0.02em` | Hero KPI numbers at 56px | + +--- + +## 4b. Date, Time & Currency Formatting + +All dates use **Greek/European style** (day-first). Never use US-style MM/DD/YYYY anywhere in the app. + +All formatting is centralized in `frontend/src/lib/formatters.js`. Never use raw `toLocaleDateString()`, `Intl.DateTimeFormat`, `toLocaleString()`, or `toISOString().slice()` in pages or modals — always import from `@/lib/formatters`. + +### Available 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 | +| `fmtRelative` | `5 minutes ago` | Relative timestamps | +| `fmtEuro` | `1.250,00 €` | Euro currency (Greek locale) | + +### Form input helpers + +| Function | Output example | Use for | +|---------------------|-------------------------|--------------------------------------------------| +| `toDatetimeLocal` | `2026-03-05T14:30` | Populating `datetime-local` inputs (local time) | +| `nowLocal` | `2026-03-05T14:30` | Current time for form defaults | +| `toDateInput` | `2026-03-05` | Populating `date` inputs | + +### Critical rule: no `toISOString().slice()` for form inputs + +`toISOString()` converts to **UTC**, which shifts the time by the user's timezone offset (e.g. 3 hours for Greece). Always use `toDatetimeLocal()` or `nowLocal()` instead. + +--- + +## 5. Spacing System + +4px base unit. All spacing must use tokens — no arbitrary pixel values. + +| Token | Value | Common use | +|--------------|--------|--------------------------------------------------| +| `--space-1` | 4px | Tight gaps, icon padding | +| `--space-2` | 8px | Between label and input, inline gaps | +| `--space-3` | 12px | Table cell padding, compact button padding | +| `--space-4` | 16px | Between form fields, mobile page padding | +| `--space-5` | 20px | Tab item spacing | +| `--space-6` | 24px | **Page padding**, card padding, section gap | +| `--space-8` | 32px | Between major sections | +| `--space-10` | 40px | Large section gap | +| `--space-12` | 48px | Extra large spacing | +| `--space-16` | 64px | Maximum spacing, hero sections | + +--- + +## 6. Border Radius & Shadows + +### Border Radius + +| Token | Value | Use | +|----------------|----------|-------------------------------------------| +| `--radius-sm` | 4px | Tags, small chips, select option rows | +| `--radius-md` | 6px | Buttons, inputs, table badges | +| `--radius-lg` | 8px | Cards, panels, dropdown menus | +| `--radius-xl` | 12px | Modals, large containers | +| `--radius-full`| 9999px | Status badge pills, avatars | + +### Shadows + +| Token | Value | Use | +|-------------------------|------------------------------------------|------------------------------------| +| `--shadow-card` | `inset 0 1px 0 rgba(192,193,255,0.05)` | Card top-edge glass reflection | +| `--shadow-sm` | `0 2px 8px rgba(10,14,20,0.40)` | Subtle lift | +| `--shadow-md` | `0 4px 16px rgba(10,14,20,0.50)` | Elevated cards | +| `--shadow-lg` | `0 8px 24px rgba(13,17,23,0.60)` | Modals, dropdowns | +| `--shadow-focus` | `0 0 0 3px rgba(192,193,255,0.20)` | Focus ring glow | +| `--shadow-primary-glow` | `0 4px 16px rgba(192,193,255,0.28)` | Primary button hover halo | +| `--shadow-danger-glow` | `0 4px 16px rgba(255,92,92,0.40)` | Danger button hover halo | +| `--shadow-success-glow` | `0 4px 16px rgba(74,222,128,0.35)` | Success button hover halo | + +--- + +## 7. Component Rules + +### Button + +Import: `@/components/ui/Button` + +**Variants:** + +| Variant | Resting state | Hover state | +|------------------|---------------------------------------|----------------------------------------------------------| +| `primary` | Indigo→violet gradient, dark text | `+brightness(1.06)` + `--shadow-primary-glow` halo | +| `secondary` | Island bg, ghost border | Elevated bg + focus border + subtle indigo glow | +| `ghost` | Transparent | Elevated bg + whisper indigo glow | +| `danger` | Coral tint bg, coral text | **Solid coral fill**, dark text + `--shadow-danger-glow` | +| `success` | Emerald tint bg, emerald text | **Solid emerald fill**, dark text + `--shadow-success-glow` | +| `table-actions` | Fully transparent, muted text | Island bg + strong border (identical to `secondary`) — also activates on `tr:hover` | + +**Sizes:** `sm`, `md` (default), `lg` + +**Rules:** +- Never use a raw ` + +``` + +--- + +### Card + +Import: `@/components/ui/Card` + +Variants: `flat` (default), `elevated`, `outlined`. + +Props: `title`, `subtitle`, `footer`, `padding` (bool, default true), `children`. + +Card header has a faint indigo gradient ceiling (`linear-gradient` from top). + +--- + +### Tabs + +Import: `@/components/ui/Tabs` + +Variants: `line` (default — underline indicator), `pill` (filled background). + +Props: `tabs` (array of `{key, label, icon?, count?}`), `active`, `onChange`, `variant`. + +Line variant uses a sliding indicator measured with `useLayoutEffect`. Pill variant uses filled backgrounds. + +Spacing: line tabs have `gap: --space-5` between items, pill tabs `gap: --space-4`. + +--- + +### Toast + +Import: `@/components/ui/Toast` → `{ ToastProvider, useToast }` + +Setup: wrap the app (or router) with ``. Then in any component: + +```jsx +const toast = useToast() +toast.success('Saved', 'Device updated successfully.') +toast.danger('Error', 'Failed to connect.') +toast.warning('Warning', 'Firmware is outdated.') +toast.info('Info', 'Sync in progress.') +``` + +Toasts auto-dismiss after 4000ms. Hover pauses the timer. Stack appears in the bottom-right corner. + +--- + +### SearchBar + +Import: `@/components/ui/SearchBar` + +Supports controlled (`value` + `onChange`) or uncontrolled mode. +Debounced by default (300ms). Clear button appears when text is present. +Appearance matches the `FormField` cutout treatment. + +--- + +### Breadcrumbs + +Import: `@/components/ui/Breadcrumbs` + +Use on detail pages only (not list pages). Items: array of `{ label, href? }`. Last item has no href — it is the current page. + +--- + +### Spinner + +Import: `@/components/ui/Spinner` + +Props: `size` (`sm`, `md`, `lg`), `color` (defaults to `--color-primary`). + +Use inside loading states. Buttons show their own spinner via `loading` prop — do not add a separate `` inside buttons. + +--- + +### StatusBadge + +Import: `@/components/ui/StatusBadge` + +Never use a raw `` with a background color for status. Always ``. + +Variants: `success`, `warning`, `danger`, `info`, `neutral`. + +--- + +### Icon + +Import: `@/components/ui/Icon` + +Renders an inline SVG by name. 35 named icons available (see Style Guide `/dev/styleguide` → Icon section for the full list). + +```jsx + + +``` + +**Asset SVGs** (from `/assets/` folders) are displayed via `` tags in the Style Guide, not via ``. These are pre-rendered SVG files used for sidebar icons, comms icons, customer status icons, etc. Use them as image sources, not as Icon component names. + +--- + +## 8. Icons + +Three sources: + +| Source | Use case | How to render | +|-------------------------------------|---------------------------------------|-----------------------------| +| `` | Action icons, UI chrome | `@/components/ui/Icon` | +| `assets/side-menu-icons/*.svg` | Sidebar navigation | `` | +| `assets/comms/*.svg` | Communication type indicators | `` | +| `assets/customer-status/*.svg` | CRM status icons | `` | +| `assets/global-icons/*.svg` | Legacy action icons (prefer ``) | `` | +| `assets/other-icons/*.svg` | Misc UI icons | `` | + +Never add a new icon library (e.g. heroicons, lucide). Use the existing sources. + +--- + +## 9. Theming Rules + +- Theme is controlled by `data-theme` attribute on `` +- Default is dark (`:root` = dark theme) +- **Never** use Tailwind's `dark:` prefix — theming is handled entirely via CSS tokens +- `[data-theme="light"]` overrides exist in `tokens.css` as a future placeholder + +--- + +## 10. Responsive Breakpoints + +| Token | Value | Behaviour | +|--------------------|--------|--------------------------------------------------------| +| `--breakpoint-sm` | 640px | | +| `--breakpoint-md` | 768px | Sidebar collapses; page padding drops to `--space-4` | +| `--breakpoint-lg` | 1024px | Full sidebar shown | +| `--breakpoint-xl` | 1280px | | + +Mobile (`< 768px`): single column, sidebar hidden (drawer), tables may become card lists. + +--- + +## 11. Section Layout — Masonry Grid + +**Default layout for ALL content pages with multiple variable-height sections.** + +Sections on a content page must flow like physical objects stacked in columns — the next section always drops into the shortest column. This is CSS column masonry. + +### How it works + +``` +Column 1 | Column 2 | Column 3 +────────────┼─────────────┼──────────── +Section A | Section B | Section C +(300px) | (250px) | (350px) + │ │ +Section E | Section D | +(200px) | (200px) | +``` + +Sections fill left-to-right across the top, then each new section drops into whichever column is currently shortest. This is automatic — the browser handles placement via CSS `columns`. + +### Usage + +```jsx +{/* 2 columns */} +
+ + + {/* auto-drops into shortest column */} +
+ +{/* 3 columns */} +
+ {sections.map(s => )} +
+``` + +Available variants: `masonry-grid--2`, `masonry-grid--3`, `masonry-grid--4` + +Responsive behaviour: +- `--4` collapses to 3 cols at 1024px, 1 col at 768px +- `--3` collapses to 2 cols at 1024px, 1 col at 768px +- `--2` collapses to 1 col at 768px + +### Rules + +- **Use `.masonry-grid` by default** on all content pages with 2+ variable-height sections +- **Do NOT** use `display: grid` with `gridTemplateColumns` for variable-height card layouts — this creates uneven whitespace when cards differ in height +- **Do NOT** use `.masonry-grid` for DataTable pages — tables span full width on their own +- **Do NOT** use `.masonry-grid` when sections must align horizontally (e.g. two fields that are semantically paired side-by-side within a card) — that's an internal card layout, not page-level masonry +- The `Card` component already has `break-inside: avoid` so it will never be split across columns + +--- + +## 12. What Claude Code Must NEVER Do + +- ❌ Write a hex color, `rgb()`, or `hsl()` value directly in any component or page file +- ❌ Write a pixel spacing or size value that isn't a `--space-*` token +- ❌ Use a raw `
+
+
+
+
+
+Sort: + +
+
+Group: + +
+
+
+ + +
+
+ + +
+ +
+ +
+
JPEG
+
+

Enterprise_Structure_01.jpg

+
+ + + +
+
+
+ +
+
+picture_as_pdf +
+
+

Q4_Operational_Brief.pdf

+

2.4 MB • 12 Oct

+
+
+
+ + +
+
+
+ +
+ +
+
+
+play_arrow +
+
+
MP4
+
+

System_Uptime_Monitor.mp4

+
+
+ +
+
+audio_file +
+
+

Client_Meeting_Notes.mp3

+

15.8 MB • 11 Oct

+
+
+ +
+
+ +
+
+description +
+
+
+
+
+
+
+
+

README_Config.txt

+

4 KB • 09 Oct

+
+
+ +
+
+ +
+ +
+

Architecture_Ref.png

+
+
+
+
+inventory_2 +
+
+

Legacy_Archives_2023.zip

+

1.2 GB • 01 Oct

+
+
+
+
+description +
+
+

Invoice_45920.pdf

+

456 KB • 30 Sep

+
+
+
+ +
+
+

Showing 1-20 of 142 files

+
+Per Page: +
+ + + + +
+
+
+ +
+ + \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/CustomerFinanceTab.html b/frontend/src/_archive/stitch-references/CustomerFinanceTab.html new file mode 100644 index 0000000..bf699f3 --- /dev/null +++ b/frontend/src/_archive/stitch-references/CustomerFinanceTab.html @@ -0,0 +1,365 @@ + + + + + +Customer Finance Dashboard | Bell Obsidian + + + + + + + + + +
+ +
+
+ +

Financial Dashboard ID: 8842-X

+
+
+ + +
+
+ + + +
+ +
+
+Total Invoiced +receipt_long +
+
+
€12,450.00
+
+trending_up ++12.5% vs last quarter +
+
+
+ +
+
+Total Received +
+
+
+
€10,200.00
+
Last transaction: 3 days ago
+
+
+ +
+
+Balance Due +priority_high +
+
+
€2,250.00
+
Overdue: €840.00
+
+
+ +
+
+Total Orders +package_2 +
+
+
03
+
2 Complete | 1 Active
+
+
+
+ +
+

+ + Active Financial Progress +

+
+ +
+
+
+
+Pending Payment +ORD-260326-002 +
+

Infrastructure Deployment Phase II

+

Project milestone for regional hub deployment. Finalizing hardware procurement and logistics allocation for Q2 delivery cycle.

+
+
+
+
Payment Progress
+
€7,500 / €9,750 (77%)
+
+
+
+
+
+Initial Deposit +Remaining Balance +
+
+
+
+
+ +
+
+
+

Transaction History

+

Real-time ledger of inbound and outbound financial flows.

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateFlowAmountMethodCategoryOrder RefNoteActions
30 Mar 26 +south_west ++€2,356.00Wire Transfer +Full Payment + + + ORD-260326 + open_in_new + +Regional hub procurement... +
+ + +
+
28 Mar 26 +north_east +-€4,361.00Credit +Invoice + + + INV-992011 + open_in_new + +Annual licensing fee... +
+ + +
+
22 Mar 26 +south_west ++€1,120.00Cash +Full Payment + + + ORD-260105 + open_in_new + +Misc hardware retail... +
+ + +
+
+
+
+
Showing 3 of 42 transactions
+
+ +
+1 +2 +3 +
+ +
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/CustomerOverviewTab.html b/frontend/src/_archive/stitch-references/CustomerOverviewTab.html new file mode 100644 index 0000000..c1f1af7 --- /dev/null +++ b/frontend/src/_archive/stitch-references/CustomerOverviewTab.html @@ -0,0 +1,401 @@ + + + + + +Customer Overview - The Luminous Enterprise + + + + + + + + +
+ +
+
+
+Company Logo +Customer Profile +
+

Fr. Lampros Chripatas

+

+apartment + Aethelgard Global Solutions • Athens, GR +

+
+
+ + +
+
+ +
+
+Lifecycle Status +
+ + + Active + +
+
+
+Open Orders +
+ 12 Pending delivery +
+
+
+Active Issues +
+ 03 Escalated +
+
+
+Support +
+ 01 Open ticket +
+
+
+ + + +
+ +
+ +
+

Account Information

+
+
+Language +Greek (Modern), English +
+
+Billing Address +
+ 42 Panepistimiou Ave,
+ Athens 106 79, Greece +
+
+
+Tags +
+VIP Priority +Enterprise +Annual Plan +
+
+
+
+ +
+

Contact Details

+
+
+
+mail +
+
+l.chripatas@aethelgard.gr +Primary Work +
+
+
+
+mail +
+
+admin@lampros-c.com +Personal +
+
+
+
+call +
+
++30 210 555 0192 +Mobile / WhatsApp +
+
+
+
+ +
+
+

Staff Notes

+ +
+
+ +
+

Client requested expedited processing for the Q4 hardware batch. Approved for VIP handling.

+
+Sarah Jenkins +Oct 24, 14:20 +
+
+ +
+

On-site consultation scheduled for November 12th. Technical lead required.

+
+Marcus Thorne +Oct 22, 09:15 +
+
+ +
+

Payment received for invoice #INV-2024-089. Discrepancy cleared.

+
+Finance Bot +Oct 18, 16:45 +
+
+
+
+ + +
+
+
+ +
+ +
+
+

Latest Orders

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Order IDDateStatusValue
+ORD-9921-X +Oct 26, 2024 +Processing +€14,250.00
+ORD-9844-A +Oct 20, 2024 +Delivered +€3,120.50
+ORD-9801-B +Oct 14, 2024 +Delivered +€28,900.00
+ORD-9755-Z +Oct 05, 2024 +Shipped +€1,840.00
+
+
+ +
+
+

Recent Communications

+
+ +
+
+
+ +
+
+mail +
+
+

Re: Technical Specification Inquiry

+2 hours ago +
+
+
EL
+Elena L. (Sales Engineer) to Fr. Lampros +
+

The requested diagrams for the Obsidian-7 architecture have been updated. Please find the attached PDF with the updated power redundancy specifications.

+
+ +
+
+chat +
+
+

Live Support Session Transcript

+Yesterday, 14:30 +
+
+
BT
+Support Bot to Fr. Lampros +
+

Ticket #SUP-4412: Automated resolution for API endpoint authentication issue. User confirmed system access restored.

+
+ +
+
+mail +
+
+

Urgent: Account Verification Required

+Oct 24, 09:12 +
+
+
SYS
+Security System to Fr. Lampros +
+

Security audit identified a new login location in Berlin. Please verify your credentials to maintain uninterrupted access.

+
+
+ +
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/DatetimePicker.html b/frontend/src/_archive/stitch-references/DatetimePicker.html new file mode 100644 index 0000000..8f80d68 --- /dev/null +++ b/frontend/src/_archive/stitch-references/DatetimePicker.html @@ -0,0 +1,221 @@ + + + + + +Luminous Enterprise Date & Time Picker + + + + + + + + + +
+ +
+ +
+
+
+

October 2024

+

Select date

+
+
+ + +
+
+ +
+ +
Mo
+
Tu
+
We
+
Th
+
Fr
+
Sa
+
Su
+ + +
30
+
1
+
2
+
3
+
4
+
5
+
6
+ +
7
+
8
+
9
+
10
+
11
+
12
+
13
+ +
14
+
15
+
16
+
17
+
18
+
19
+
20
+ +
21
+
22
+
23
+
24
+
25
+
26
+
27
+
+
+ +
+
+

Set Time

+

Scroll to select

+
+
+ +
+ +
+
08
+
09
+
10
+
11
+
12
+
01
+
+ +
:
+ +
+
20
+
25
+
30
+
35
+
40
+
45
+
+ +
+ + +
+
+
+
+ +
+ +
+ + +
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/IssuesTab.html b/frontend/src/_archive/stitch-references/IssuesTab.html new file mode 100644 index 0000000..218d44f --- /dev/null +++ b/frontend/src/_archive/stitch-references/IssuesTab.html @@ -0,0 +1,373 @@ + + + + + + + + + + + + + +
+
+
+System Repository +

Issues

+

Enterprise-wide incident tracking and resolution management for Luminous network nodes.

+
+
+
+search + +
+ + +
+
+
+
+
+Total Active +data_thresholding +
+
1,284
+
+trending_up +12% from last week +
+
+
+
+Researching +science +
+
42
+
Avg. 4.2h per cycle
+
+
+
+High Priority +priority_high +
+
08
+
+warning +Requires attention +
+
+
+
+MTTR +timer +
+
18m
+
+check_circle +Below SLA target +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StatusSubjectCategoryPriorityReporterDate
+
+
+Researching +
+
+
+Latency spikes in Northern Quadrant nodes +#INC-9042 • Network Topology +
+
+Device + +
+signal_cellular_alt +High +
+
+
+
+Reporter +
+Alex Rivera +
+
+Oct 24, 2023 +
+
+
+Active +
+
+
+User authentication failure on mobile portal +#INC-8821 • Identity Service +
+
+User + +
+signal_cellular_alt_2_bar +Medium +
+
+
+
+Reporter +
+Sarah Chen +
+
+Oct 23, 2023 +
+
+
+Complete +
+
+
+Billing mismatch in European Tier-2 accounts +#INC-8750 • Financial Systems +
+
+Customer + +
+signal_cellular_alt_1_bar +Low +
+
+
+
+Reporter +
+Jordan Smyth +
+
+Oct 22, 2023 +
+
+
+Active +
+
+
+API Endpoint 500 error on /v2/telemetry +#INC-8612 • Infrastructure +
+
+Device + +
+signal_cellular_alt +High +
+
+
+
+Reporter +
+Maya Patel +
+
+Oct 22, 2023 +
+
+
+Researching +
+
+
+Intermittent packet loss on satellite uplink +#INC-8599 • Network +
+
+Device + +
+signal_cellular_alt_2_bar +Medium +
+
+
+
+Reporter +
+Marcus Thorne +
+
+Oct 21, 2023 +
+
+
+Showing 5 of 1,284 issues +
+ +
+1 +2 +3 +
+ +
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/SupportTicketsTab.html b/frontend/src/_archive/stitch-references/SupportTicketsTab.html new file mode 100644 index 0000000..adea5b3 --- /dev/null +++ b/frontend/src/_archive/stitch-references/SupportTicketsTab.html @@ -0,0 +1,269 @@ + +```html + + + + + + + +
+ + + +
+ +
+
+
+

Edge node latency in US-East

+

Created by Sarah Connor • 4 total messages

+
+
+
+ + +
+
+ +
+ +
+
+ +
+
+
+Sarah Connor +Today at 10:42 AM +
+
+ Hi Team,

+ We're seeing significant latency spikes on our edge nodes specifically in the US-East region. The P99 is hitting over 1500ms for several customer clusters. Our internal health checks show the nodes are running but the handoff to the gateway seems delayed. +

+ Attached are the current logs for the affected region. +
+
+
+attachment +metrics_dump_us_east.json +
+
+
+
+ +
+
+stars +
+
+
+Today at 11:05 AM +Alex K. (Engineering) +
+
+ Hello Sarah,

+ I've reviewed the logs you sent. It looks like the US-East-1 instance group was hit by a cold-start issue following the patch update last night. I've manually scaled up the warm pool to alleviate the P99 spikes. +

+ Can you confirm if you see improvements on your end? +
+
+
+ +
+
+ +
+
+
+Sarah Connor +Today at 11:12 AM +
+
+ The latency is dropping back to 200ms. Still slightly higher than our baseline of 80ms, but much better than before. +
+
+
+
+ +
+
+ +
+
+ + + + +
+
+ + +
+ +
+ +
+ +
+
+ +
+
+
+ + Set as Resolved on send +
+ +
+
+
+
+
+
+ +``` \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/cardPageCustomers.html b/frontend/src/_archive/stitch-references/cardPageCustomers.html new file mode 100644 index 0000000..f60e744 --- /dev/null +++ b/frontend/src/_archive/stitch-references/cardPageCustomers.html @@ -0,0 +1,92 @@ + + + + + + +Refined Minimalist CRM Card + + + + + + +
+ +

Refined Minimalist CRM Card

+ +
+ +
+
+ +
+
+

+Dominic Vanzetti +

+

Vanzetti & Sons • Milan

+
+ +
+ + +
+
+ + + +
+Awaiting Payment +
+ + +
+VIP +SaaS +Strategic +
+ + +
+ + + + +Email • 2 days ago +
+ +
+
+ +
+ + \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/cardPageUsers.html b/frontend/src/_archive/stitch-references/cardPageUsers.html new file mode 100644 index 0000000..ff89c0a --- /dev/null +++ b/frontend/src/_archive/stitch-references/cardPageUsers.html @@ -0,0 +1,353 @@ + + + + + +Luminous CRM - Profile Variations + + + + + + + + + +
+
+Luminous CRM + +
+
+notifications +help +settings +Administrator Profile +
+
+
+ + + +
+
+
+

User Directory

+

Managing enterprise assets and contact identities.

+
+ +
+ +
+
+
+
+Fr. Panagiotis + +verified + +
+
+
+
+

Fr. Panagiotis

+

Senior Architect

+
+Active +
+
+
+mail +p.architect@luminous.com +
+
+call ++1 (555) 012-3456 +
+
+
+
+

Devices

+

05

+
+
+

Issues

+

02

+
+
+
+
+
+ +
+
+Elena V. +
+

Elena Vasylkov

+

Cloud Infrastructure Lead

+
+
+Pending +

Ref ID: #V-882

+
+
+
+
+mail +e.vasyl@luminous.io +
+
+call ++1 (555) 992-0011 +
+
+Devices Owned +12 +
+
+Active Issues +04 +
+
+ +
+ +
+
+
+
+Marcus Thorne +
+

Marcus Thorne

+

Lead Developer

+
+Inactivity: 3 days +Inactive +
+
+
+

03

+

Devices

+
+
+
+

00

+

Issues

+
+
+
+ +
+Sarah J. +
+
+

Sarah Jenkins

+

Ops Manager

+
+ + +
+ + + Active + +
+
+chevron_right +
+
+ +
+
+
+analytics +

System Health

+
+
+
+
+

78% efficiency across all contact metrics.

+
+
+
+security +

Security Protocol

+
+

All 4 profiles are end-to-end encrypted under Bell Obsidian standards.

+
+
+
+history +

Recent Activity

+
+
+ + + +
+12
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/clocktab.html b/frontend/src/_archive/stitch-references/clocktab.html new file mode 100644 index 0000000..9e1c559 --- /dev/null +++ b/frontend/src/_archive/stitch-references/clocktab.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + +
+

Clock & Alerts Configuration

+

Manage system synchronization and time-indicating bell sequences.

+
+ +
+ +
+ +
+
+ +
+SYSTEM MASTER +
+ +
+
+ +
+
+
+
+
+
08:45
+
+
+ + + +
+NTP NETWORK +
+
+
+ +
+
+
+
+
+
08:45
+
+
+
+ +
+
+Last Sync +2 minutes ago +
+ +
+
+ +
+ +
+
+router +

Device Parameters

+
+
+
+ + +
+
+ + +
+
+
+Auto-DST Adjustment +
+ +
+
+
+
+
+
+ +
+
+notifications_active +

Time-Indicating Chimes

+
+
+ +
+
+

Quarter Hour Strike

+

Westminster Sequence

+
+edit +
+ +
+
+

Hour Strike

+

Deep Bronze Tone

+
+edit +
+ +
+ +
+ +to + +
+

Ensures liturgical chimes do not fire during neighborhood quiet hours.

+
+
+
+ +
+
+light_mode +

Backlight Control

+
+
+
+
+ +85% +
+ +
+
+ +
+ + +
+
+
+
+Auto-Dimming +
+ +
+
+
+
+Proximity Sensor +
+ +
+
+
+
+
+
+
+ +
+ + +
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/deviceMapPage.html b/frontend/src/_archive/stitch-references/deviceMapPage.html new file mode 100644 index 0000000..b00b1c0 --- /dev/null +++ b/frontend/src/_archive/stitch-references/deviceMapPage.html @@ -0,0 +1,395 @@ + + + + + + +Bell Obsidian | Fleet Observatory + + + + + + + + + + + +
+
+Bell Obsidian + +
+
+
+search +
+notifications +settings +
+User profile avatar +
+
+
+ +
+ +
+ +
+ +
+ + +
+
+
+
+ San Francisco Hub ● 12 Active +
+
+ +
+
+
+
+ +
+
+
+
+ Tokyo Edge ▲ Critical +
+
+ +
+
+
+
+ + +
+

Fleet Health

+
+
+ + + + +85% +
+
+
+
+842 Online +
+
+
+148 Offline +
+
+
+
+ +
+ + +
+ +
+ +
+
+

Recent Activity

+Live Feed +
+
+
+
+
+

Gateway #882 Reconnected

+

San Francisco Hub • 2m ago

+
+
+
+
+
+

Voltage Drop Alert

+

Tokyo Edge • 14m ago

+
+
+
+
+
+

Firmware Update Pushed (v2.4.1)

+

London Cluster • 1h ago

+
+
+
+
+ +
+ + + +
+ + +
+ +
+
+
+Healthy +
+
+
+Alert +
+
+
+Offline +
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/general-info.html b/frontend/src/_archive/stitch-references/general-info.html new file mode 100644 index 0000000..68ad154 --- /dev/null +++ b/frontend/src/_archive/stitch-references/general-info.html @@ -0,0 +1,281 @@ + + + + + + + + + + + +
+ +
+
+Device Configuration +

General Info

+
+
+ + +
+
+ +
+ +
+
+location_on +

Location Info

+
+
+
+
+ + +
+
+ + +
+
+
+ +
+
+
Coordinates
+
51.5138° N, 0.0984° W
+
+
+
+
+ +
+
+security +

Safety Settings

+
+
+
+
+
Bell Guard
+
Automated striking lock
+
+ +
+
+
+
Warnings
+
Audible system alerts
+
+ +
+
+
+
Safety
+
Manual override lock
+
+ +
+
+
+ +
+
+settings_input_component +

Mechanisms

+
+
+
+
+notifications +
Has Bells
+
YES
+
+
+schedule +
Has Clock
+
NO
+
+
+
+
Connected Bells
+
+ +12 + +
+
+
+
+ +
+
+terminal +

Log Settings

+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+label +

Tags & Notes

+
+edit +
+
+Critical-Node +Historic-Site ++ Add Tag +
+ +
+ +
+
+
+hub +

Network Info

+
+
+Static IP toggle + +
+
+
+
+ +
bell-stpaul-01
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
E4:5F:01:A2:4B:92
+
+
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/layoutexample.html b/frontend/src/_archive/stitch-references/layoutexample.html new file mode 100644 index 0000000..bf7cf53 --- /dev/null +++ b/frontend/src/_archive/stitch-references/layoutexample.html @@ -0,0 +1,241 @@ + + + + + +Luminous Enterprise - Dashboard Workspace + + + + + + + + + + +
+ +
+ +
+Infrastructure +chevron_right +Cloud +chevron_right +Server Status +
+ +
+ + + +
+ + +
+ +
+ +User Profile Avatar +
+
+
+ +
+ +
+
+
+
+ +
+

placeholder

+
+
+ + +
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/overviewPage.html b/frontend/src/_archive/stitch-references/overviewPage.html new file mode 100644 index 0000000..1e17741 --- /dev/null +++ b/frontend/src/_archive/stitch-references/overviewPage.html @@ -0,0 +1,332 @@ + + + + + +Luminous Enterprise - Church Bell Controller + + + + + + + + + + +
+ +
+
+
Luminous Enterprise
+ +
+
+
+ + +
+
+User profile avatar +
+
+
+ +
+ +
+
+Bell Controller 3D Render +
+
+Operational +
+
+
+
+
+

BellCore V4

+

Primary Site Controller • London East Parish

+
+
+1,248h +Continuous Uptime +
+
+
+
+

Serial Number

+

LX-990-21-BC

+
+
+

Device Family

+

LithoPlex G3

+
+
+

Hardware Revision

+

REV-C (2024)

+
+
+

IP Address

+

192.168.1.104

+
+
+
+
+ +
+ +
+
+

Service Lifecycle

+verified +
+
+
+
+Cloud Subscription +284 Days Remaining +
+
+
+
+
+
+
+Hardware Warranty +Expires Aug 2026 +
+
+
+
+
+
+
+ +
+
+

Admin Quick Notes

+ +
+
+
+

"Annual mechanical inspection scheduled for June 12. Motor tensioners might require adjustment."

+— Site Manager Mark S. +
+
+

"Emergency bypass key stored in South Vestry locker 4B."

+— Security Protocol +
+
+
+
+ +
+ +
+
+
+

Live Stream Logs

+
+
+

[14:02:11] SYSTEM_READY: HEARTBEAT_OK

+

[14:15:00] TRIGGER_SEQUENCE: NOON_ANGELUS_INIT

+

[14:15:02] RELAY_01: ACTIVATE (380V_MOT)

+

[14:16:30] RELAY_01: DEACTIVATE

+

[14:18:45] WARN: VOLTAGE_FLUCTUATION_OFFSET (0.2v)

+

[14:22:00] TELEMETRY_SYNC: SUCCESSFUL

+

[14:25:01] IDLE_STATE: MONITORING

+

[14:30:00] SYSTEM_SCAN: NO_ERRORS

+
+
+ +
+

Issue & Note History

+
+
+
+

Motor Overheat Fault

+

Automatic shutdown triggered on Relay 2. Cleared manually by admin.

+2 DAYS AGO +
+
+
+

Firmware Update: v4.2.1

+

Optimization for heavy swing patterns and GPS time sync stability.

+1 WEEK AGO +
+
+
+

Site Installation Complete

+

Commissioning report uploaded by field engineer.

+3 WEEKS AGO +
+
+
+ +
+
+

Auth Access

+ +
+
+
+User 1 +

S. Jensen

+Chief Op +
+
+User 2 +

M. Chen

+System Eng +
+
+
+church +
+

Local Admin

+Restricted +
+
+add +Invite +
+
+
+shield +

Biometric override is currently ENABLED for this node cluster.

+
+
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/overviewtab.html b/frontend/src/_archive/stitch-references/overviewtab.html new file mode 100644 index 0000000..2d65ad8 --- /dev/null +++ b/frontend/src/_archive/stitch-references/overviewtab.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + +
+ +
+
+System Node Overview +

Device Analytics Hub

+
+
+ + +
+
+ +
+ +
+
+ +
+
+
+
+memory +Industrial IoT Gateway +
+

LX-2994-OBSIDIAN

+
+
+

Revision

+

v4.2.1-Beta

+
+
+

Location

+

Oslo Hub, Sector 7

+
+
+

Deployment

+

Oct 12, 2023

+
+
+

Last Sync

+

4 minutes ago

+
+
+
+
+ +
+
+

+ Health Monitor + check_circle +

+
+
+System Uptime +142 Days +
+
+
+
+
+
+All Systems Nominal +
+
+
+
+

Diagnostics running in background

+
+
+ +
+

Service Metrics

+
+
+

Subscription Tier

+

Enterprise Plus

+
+
+

Warranty Status

+
+Active until Jan 2026 +verified +
+
+
+ + +
+
+
+ +
+

+edit_note + Quick Admin Notes +

+ +
+ +
+
+ +
+
+
+
+
+
+

+terminal + Live MQTT Stream +

+
+

[14:22:01] PUBLISH core/telemetry/temp: 24.2°C

+

[14:22:05] SUBSCRIBE device/control/reset

+

[14:22:08] PUBLISH core/telemetry/humidity: 42%

+

[14:22:10] PING successful (32ms)

+

[14:22:15] PUBLISH core/state/health: nominal

+

[14:22:20] PUBLISH core/telemetry/load: 0.12

+
+
+ +
+
+

Issue Tracker & Staff Logs

+
+ 2 Active Tickets + 48 Resolved +
+
+
+ +
+
+
+warning +
+
+

Cooling Unit Degraded

+

Ticket #TK-882 · Logged 3h ago by Erik N.

+
+
+High Priority +
+ +
+
+
+history +
+
+

Firmware Update Routine

+

Ticket #TK-879 · Completed by System

+
+
+Resolved +
+
+
+ +
+

Assigned Personnel

+
+
+
+ +
+

Lars Jensen

+

Primary Engineer

+
+
+ +
+
+
+ +
+

Siri Ødegård

+

Fleet Manager

+
+
+ +
+ +
+
+
+ +
+

Obsidian Core v4.2.1-Beta · Build Hash: 7fb02a1c

+ +
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/userdetail.html b/frontend/src/_archive/stitch-references/userdetail.html new file mode 100644 index 0000000..35e34fc --- /dev/null +++ b/frontend/src/_archive/stitch-references/userdetail.html @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + +
+
+
+Profile +
+
+
+
+

Adrian Thorne

+Active +
+

Senior Systems Architect • ID: 8829-QX

+
+
+
+ + + +
+
+ +
+ +
+
+
+person +

Account Information

+
+
+
+
+ +

+1 (555) 902-4412

+
+
+ +

a.thorne@luminous-ent.com

+
+
+
+
+ +USR-8829-QX-ALPHA +
+
+ +

Overseeing node-level distribution and enterprise security protocols for the West Coast cluster.

+
+
+
+
+
+ +
+
+security +

Security Access

+
+
+
+ +
+•••••• +visibility +
+
+
+ +
+•••• +visibility +
+
+
+
+ +
+
+analytics +

System Activity

+
+
+
+
+
+

Created Time

+

Oct 12, 2023 • 08:42 AM

+
+
+
+
+
+

Last Active

+

Just now • 14:12 PM

+
+
+
+
+
+

Session Duration

+

4h 12m

+
+
+
+
+
+
+ +
+
+
+group +

Network

+
+24 +
+
+
+User +User +User +
+21
+
+
+
+Invited Members +8 / 10 +
+
+
+
+
+
+
+ +
+
+devices +

Linked Devices

+
+
+
+
+laptop_mac +
+

MacBook Pro 16

+

San Francisco, CA

+
+
+ +
+
+
+smartphone +
+

iPhone 15 Pro

+

San Francisco, CA

+
+
+ +
+
+
+tablet_mac +
+

iPad Air

+

Offline

+
+
+ +
+
+
+ +
+
+ + +
+
+
+
+sticky_note_2 +
+
+System Upgrade Scheduled +2 days ago +
+

User requested access to the Level 4 terminal for the upcoming migration. Verified credentials and approved temporary elevation.

+
+
+
+history +
+
+Policy Review +1 week ago +
+

Standard quarterly compliance check completed. No anomalies detected in cross-node communication logs.

+
+
+
+
+ +
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/_archive/stitch-references/warrantyTab.html b/frontend/src/_archive/stitch-references/warrantyTab.html new file mode 100644 index 0000000..1ce7cb0 --- /dev/null +++ b/frontend/src/_archive/stitch-references/warrantyTab.html @@ -0,0 +1,296 @@ + + + +Warranty & Subscription | The Luminous Enterprise + + + + + + + + +
+ +
+

Warranty & Subscription

+

Enterprise hardware status and service entitlements for Asset ID: LX-8802

+
+ +
+
+Subscription Status +
+
+
+
+workspace_premium +
+
+
Basic Tier
+
Active Enterprise License
+
+
+ +
+
+
2
+
Users
+
+
+
6
+
Outputs
+
+ +
+
+
+ +
+
+Warranty Information +
+
+
+
+
+warning +
+
+

Warranty Void / Expired

+

Coverage ended on January 14, 2024.

+
+
+
+
+
Purchase Date
+
Oct 12, 2022
+
+
+
Void Event
+
Jan 14, 2024
+
+
+
Support Level
+
Tier 3 (None)
+
+
+
Serial Num
+
LMX-009-FF
+
+
+
+ + +
+
+ +
+
+
+
+ +
+
+Maintenance History + +
+
+
+ +
+ +
+
+
+
+Thermal Paste Re-application +MAR 02, 2024 +
+

Routine core cooling service performed by System Node 4. Operating temp decreased by 4.2°C.

+
+
+ +
+
+
+
+Chassis Integrity Alert +JAN 14, 2024 +
+

System detected unscheduled casing penetration at primary latch. Warranty flagged for review.

+
+
+ +
+
+
+
+Firmware Patch v4.2.1 +DEC 12, 2023 +
+

Automated security handshake and peripheral driver optimizations completed successfully.

+
+
+ +
+
+
+
+Initial Calibration +OCT 14, 2022 +
+

Factory benchmarking and identity assignment. All systems nominal for first deployment.

+
+
+
+
+
+ +
+
+Device Performance Metrics +
+
+
+
+play_circle ++12% +
+
+
1.2M
+
Total Playbacks
+
+
+
+
+bolt +Active +
+
+
14
+
Voltage Strikes
+
+
+
+
+crisis_alert +Critical +
+
+
02
+
System Warnings
+
+
+
+
+query_stats +Optimized +
+
+
99.8%
+
Uptime Reliability
+
+
+
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/users/UserDetail.jsx b/frontend/src/_archive/users/UserDetail.jsx similarity index 100% rename from frontend/src/users/UserDetail.jsx rename to frontend/src/_archive/users/UserDetail.jsx diff --git a/frontend/src/users/UserForm.jsx b/frontend/src/_archive/users/UserForm.jsx similarity index 100% rename from frontend/src/users/UserForm.jsx rename to frontend/src/_archive/users/UserForm.jsx diff --git a/frontend/src/users/UserList.jsx b/frontend/src/_archive/users/UserList.jsx similarity index 100% rename from frontend/src/users/UserList.jsx rename to frontend/src/_archive/users/UserList.jsx diff --git a/frontend/src/assets/customer-status/.gitkeep b/frontend/src/assets/customer-status/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/assets/global-icons/.gitkeep b/frontend/src/assets/global-icons/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/assets/global-icons/balance-due.svg b/frontend/src/assets/global-icons/balance-due.svg new file mode 100644 index 0000000..5180bca --- /dev/null +++ b/frontend/src/assets/global-icons/balance-due.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/global-icons/invoice.svg b/frontend/src/assets/global-icons/invoice.svg new file mode 100644 index 0000000..14f48df --- /dev/null +++ b/frontend/src/assets/global-icons/invoice.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/global-icons/invoice2.svg b/frontend/src/assets/global-icons/invoice2.svg new file mode 100644 index 0000000..438eb9b --- /dev/null +++ b/frontend/src/assets/global-icons/invoice2.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/src/assets/global-icons/money.svg b/frontend/src/assets/global-icons/money.svg new file mode 100644 index 0000000..3613e63 --- /dev/null +++ b/frontend/src/assets/global-icons/money.svg @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/frontend/src/assets/global-icons/money2.svg b/frontend/src/assets/global-icons/money2.svg new file mode 100644 index 0000000..2652232 --- /dev/null +++ b/frontend/src/assets/global-icons/money2.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/global-icons/payment.svg b/frontend/src/assets/global-icons/payment.svg new file mode 100644 index 0000000..7d60943 --- /dev/null +++ b/frontend/src/assets/global-icons/payment.svg @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/frontend/src/assets/global-icons/speed.svg b/frontend/src/assets/global-icons/speed.svg new file mode 100644 index 0000000..b73dd88 --- /dev/null +++ b/frontend/src/assets/global-icons/speed.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/assets/global-icons/status.svg b/frontend/src/assets/global-icons/status.svg new file mode 100644 index 0000000..232bfd4 --- /dev/null +++ b/frontend/src/assets/global-icons/status.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/src/assets/global-icons/total-orders.svg b/frontend/src/assets/global-icons/total-orders.svg new file mode 100644 index 0000000..42a37b8 --- /dev/null +++ b/frontend/src/assets/global-icons/total-orders.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/images/background1.jpg b/frontend/src/assets/images/background1.jpg new file mode 100644 index 0000000..c827705 Binary files /dev/null and b/frontend/src/assets/images/background1.jpg differ diff --git a/frontend/src/assets/images/background2.jpg b/frontend/src/assets/images/background2.jpg new file mode 100644 index 0000000..a34bcab Binary files /dev/null and b/frontend/src/assets/images/background2.jpg differ diff --git a/frontend/src/assets/images/carbon-fiber.jpg b/frontend/src/assets/images/carbon-fiber.jpg new file mode 100644 index 0000000..25e1846 Binary files /dev/null and b/frontend/src/assets/images/carbon-fiber.jpg differ diff --git a/frontend/src/assets/images/hex.jpg b/frontend/src/assets/images/hex.jpg new file mode 100644 index 0000000..69c412d Binary files /dev/null and b/frontend/src/assets/images/hex.jpg differ diff --git a/frontend/src/assets/images/lines.jpg b/frontend/src/assets/images/lines.jpg new file mode 100644 index 0000000..e726ab6 Binary files /dev/null and b/frontend/src/assets/images/lines.jpg differ diff --git a/frontend/src/assets/images/login-church.jpg b/frontend/src/assets/images/login-church.jpg new file mode 100644 index 0000000..f49823d Binary files /dev/null and b/frontend/src/assets/images/login-church.jpg differ diff --git a/frontend/src/assets/images/login-widescreen.jpg b/frontend/src/assets/images/login-widescreen.jpg new file mode 100644 index 0000000..371ab1f Binary files /dev/null and b/frontend/src/assets/images/login-widescreen.jpg differ diff --git a/frontend/src/assets/images/marble.jpg b/frontend/src/assets/images/marble.jpg new file mode 100644 index 0000000..f65d3b1 Binary files /dev/null and b/frontend/src/assets/images/marble.jpg differ diff --git a/frontend/src/assets/images/shapes-background.jpg b/frontend/src/assets/images/shapes-background.jpg new file mode 100644 index 0000000..a62d8c0 Binary files /dev/null and b/frontend/src/assets/images/shapes-background.jpg differ diff --git a/frontend/src/assets/images/shapes-background2.jpg b/frontend/src/assets/images/shapes-background2.jpg new file mode 100644 index 0000000..e372397 Binary files /dev/null and b/frontend/src/assets/images/shapes-background2.jpg differ diff --git a/frontend/src/assets/images/shapes-background3.jpg b/frontend/src/assets/images/shapes-background3.jpg new file mode 100644 index 0000000..ebdc2da Binary files /dev/null and b/frontend/src/assets/images/shapes-background3.jpg differ diff --git a/frontend/src/assets/images/tile.jpg b/frontend/src/assets/images/tile.jpg new file mode 100644 index 0000000..d7c35b9 Binary files /dev/null and b/frontend/src/assets/images/tile.jpg differ diff --git a/frontend/src/assets/images/tiles.jpg b/frontend/src/assets/images/tiles.jpg new file mode 100644 index 0000000..7152107 Binary files /dev/null and b/frontend/src/assets/images/tiles.jpg differ diff --git a/frontend/src/assets/images/wood.jpg b/frontend/src/assets/images/wood.jpg new file mode 100644 index 0000000..2373b45 Binary files /dev/null and b/frontend/src/assets/images/wood.jpg differ diff --git a/frontend/src/assets/logos/.gitkeep b/frontend/src/assets/logos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/assets/logos/bell_systems_horizontal_darkMode.png b/frontend/src/assets/logos/bell_systems_horizontal_darkMode.png new file mode 100644 index 0000000..f80e356 Binary files /dev/null and b/frontend/src/assets/logos/bell_systems_horizontal_darkMode.png differ diff --git a/frontend/src/assets/logos/bell_systems_horizontal_lightMode.png b/frontend/src/assets/logos/bell_systems_horizontal_lightMode.png new file mode 100644 index 0000000..7c969cd Binary files /dev/null and b/frontend/src/assets/logos/bell_systems_horizontal_lightMode.png differ diff --git a/frontend/src/assets/side-menu-icons/.gitkeep b/frontend/src/assets/side-menu-icons/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/assets/striker-mechanisms/certification.png b/frontend/src/assets/striker-mechanisms/certification.png new file mode 100644 index 0000000..f34e660 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/certification.png differ diff --git a/frontend/src/assets/striker-mechanisms/certification2.png b/frontend/src/assets/striker-mechanisms/certification2.png new file mode 100644 index 0000000..1fb2c41 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/certification2.png differ diff --git a/frontend/src/assets/striker-mechanisms/originals/Gemini_Generated_Image_qer1agqer1agqer1.png b/frontend/src/assets/striker-mechanisms/originals/Gemini_Generated_Image_qer1agqer1agqer1.png new file mode 100644 index 0000000..18dff71 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/originals/Gemini_Generated_Image_qer1agqer1agqer1.png differ diff --git a/frontend/src/assets/striker-mechanisms/originals/Size1.png b/frontend/src/assets/striker-mechanisms/originals/Size1.png new file mode 100644 index 0000000..1cc1075 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/originals/Size1.png differ diff --git a/frontend/src/assets/striker-mechanisms/originals/Size3.png b/frontend/src/assets/striker-mechanisms/originals/Size3.png new file mode 100644 index 0000000..11ad611 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/originals/Size3.png differ diff --git a/frontend/src/assets/striker-mechanisms/originals/Size4.png b/frontend/src/assets/striker-mechanisms/originals/Size4.png new file mode 100644 index 0000000..7d27934 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/originals/Size4.png differ diff --git a/frontend/src/assets/striker-mechanisms/originals/Size5-2.png b/frontend/src/assets/striker-mechanisms/originals/Size5-2.png new file mode 100644 index 0000000..758de90 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/originals/Size5-2.png differ diff --git a/frontend/src/assets/striker-mechanisms/originals/Size6.png b/frontend/src/assets/striker-mechanisms/originals/Size6.png new file mode 100644 index 0000000..70bf937 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/originals/Size6.png differ diff --git a/frontend/src/assets/striker-mechanisms/size-1.jpg b/frontend/src/assets/striker-mechanisms/size-1.jpg new file mode 100644 index 0000000..19bbfd0 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/size-1.jpg differ diff --git a/frontend/src/assets/striker-mechanisms/size-2.jpg b/frontend/src/assets/striker-mechanisms/size-2.jpg new file mode 100644 index 0000000..b980d30 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/size-2.jpg differ diff --git a/frontend/src/assets/striker-mechanisms/size-3.jpg b/frontend/src/assets/striker-mechanisms/size-3.jpg new file mode 100644 index 0000000..67aab46 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/size-3.jpg differ diff --git a/frontend/src/assets/striker-mechanisms/size-4.jpg b/frontend/src/assets/striker-mechanisms/size-4.jpg new file mode 100644 index 0000000..b12d947 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/size-4.jpg differ diff --git a/frontend/src/assets/striker-mechanisms/size-5.jpg b/frontend/src/assets/striker-mechanisms/size-5.jpg new file mode 100644 index 0000000..10d7c97 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/size-5.jpg differ diff --git a/frontend/src/assets/striker-mechanisms/size-6.jpg b/frontend/src/assets/striker-mechanisms/size-6.jpg new file mode 100644 index 0000000..8170c47 Binary files /dev/null and b/frontend/src/assets/striker-mechanisms/size-6.jpg differ diff --git a/frontend/src/components/layout/Header.jsx b/frontend/src/components/layout/Header.jsx new file mode 100644 index 0000000..7861b92 --- /dev/null +++ b/frontend/src/components/layout/Header.jsx @@ -0,0 +1,512 @@ +// frontend/src/components/layout/Header.jsx +// Fixed top bar — covers only the content area (left: --sidebar-width). +// +// Left: hamburger (mobile) + breadcrumb trail +// Right: global search · notifications · settings · separator · profile w/ dropdown + +import { useState, useEffect, useRef } from 'react' +import { Link, useLocation, useNavigate } from 'react-router-dom' +import { useAuth } from '@/hooks/useAuth' +import api from '@/lib/api' +import HeaderSearch from '@/components/ui/HeaderSearch' + +// ─── Breadcrumb config ──────────────────────────────────────────────────────── + +const STATIC_LABELS = { + '': 'Home', + dashboard: 'Dashboard', + devices: 'Devices', + users: 'App Users', + mqtt: 'MQTT', + commands: 'Commands', + logs: 'Logs', + equipment: 'Equipment', + notes: 'Issues & Notes', + mail: 'Mail', + crm: 'CRM', + comms: 'Activity Log', + customers: 'Customers', + orders: 'Orders', + products: 'Products', + quotations: 'Quotations', + new: 'New', + edit: 'Edit', + melodies: 'Melodies', + archetypes: 'Archetypes', + settings: 'Settings', + composer: 'Composer', + manufacturing: 'Manufacturing', + batch: 'Batch', + provision: 'Provision Device', + firmware: 'Firmware', + developer: 'Developer', + api: 'API Reference', + staff: 'Staff', + sn: 'S/N Manager', + 'staff-log': 'Staff Log', + 'serial-logs': 'Log Viewer', + 'public-features': 'Public Features', +} + +const ENTITY_RESOLVERS = { + customers: { path: (id) => `/crm/customers/${id}`, label: (d) => [d.name, d.surname].filter(Boolean).join(' ') || d.organization || null }, + products: { path: (id) => `/crm/products/${id}`, label: (d) => d.name || null }, + quotations: { path: (id) => `/crm/quotations/${id}`, label: (d) => d.quotation_number || null }, + devices: { path: (id) => `/devices/${id}`, label: (d) => d.name || d.device_id || null }, + orders: { path: (id) => `/crm/orders/${id}`, label: (d) => d.order_number || null }, + melodies: { path: (id) => `/melodies/${id}`, label: (d) => d.name || null }, + archetypes: { path: (id) => `/builder/melodies/${id}`, label: (d) => d.name || null }, + users: { path: (id) => `/users/${id}`, label: (d) => d.name || d.display_name || null }, + notes: { path: (id) => `/equipment/notes/${id}`, label: (d) => d.title || d.subject || null }, + staff: { path: (id) => `/staff/${id}`, label: (d) => d.name || null }, +} + +const labelCache = {} + +async function fetchEntityLabel(type, id) { + const key = `${type}:${id}` + if (labelCache[key]) return labelCache[key] + const resolver = ENTITY_RESOLVERS[type] + if (!resolver) return id + try { + const data = await api.get(resolver.path(id)) + const raw = resolver.label(data) + if (raw) { + const label = String(raw).length > 28 ? String(raw).slice(0, 26) + '…' : String(raw) + labelCache[key] = label + return label + } + } catch { + // fall back to raw id + } + return id +} + +function parseSegments(pathname) { + const parts = pathname.split('/').filter(Boolean) + const segments = [{ label: 'Home', to: '/' }] + let i = 0 + + while (i < parts.length) { + const part = parts[i] + const built = '/' + parts.slice(0, i + 1).join('/') + + if (part === 'crm') { + segments.push({ label: 'CRM', to: '/crm/customers' }) + i++; continue + } + if (part === 'equipment' && parts[i + 1] === 'notes') { + segments.push({ label: 'Issues & Notes', to: '/equipment/notes' }) + i += 2; continue + } + if (part === 'manufacturing' && parts[i + 1] === 'provision') { + segments.push({ label: 'Manufacturing', to: '/manufacturing' }) + segments.push({ label: 'Provision Device', to: '/manufacturing/provision' }) + i += 2; continue + } + + const staticLabel = STATIC_LABELS[part] + if (staticLabel) { + segments.push({ label: staticLabel, to: built }) + } else { + const prevPart = parts[i - 1] + const fetchType = ENTITY_RESOLVERS[prevPart] ? prevPart : null + segments.push({ label: part, to: built, dynamicId: part, fetchType }) + } + i++ + } + + return segments +} + +// ─── Breadcrumb ─────────────────────────────────────────────────────────────── + +function Breadcrumb() { + const location = useLocation() + const [segments, setSegments] = useState(() => parseSegments(location.pathname)) + + useEffect(() => { + const parsed = parseSegments(location.pathname) + setSegments(parsed) + + const dynamics = parsed.filter((s) => s.fetchType && s.dynamicId) + if (!dynamics.length) return + + let cancelled = false + ;(async () => { + const resolved = [...parsed] + for (const seg of dynamics) { + const label = await fetchEntityLabel(seg.fetchType, seg.dynamicId) + if (cancelled) return + const idx = resolved.findIndex( + (s) => s.dynamicId === seg.dynamicId && s.fetchType === seg.fetchType + ) + if (idx !== -1) resolved[idx] = { ...resolved[idx], label } + } + if (!cancelled) setSegments([...resolved]) + })() + + return () => { cancelled = true } + }, [location.pathname]) + + if (segments.length <= 1) return null + + const display = segments.slice(1) + + return ( + + ) +} + +// ─── Icons ──────────────────────────────────────────────────────────────────── + +function HamburgerIcon() { + return ( + + ) +} + +function BellIcon() { + return ( + + ) +} + +function GearIcon() { + return ( + + ) +} + +function SignOutIcon() { + return ( + + ) +} + +// ─── Profile dropdown ───────────────────────────────────────────────────────── + +function ProfileMenu({ onClose, onSignOut }) { + useEffect(() => { + const handler = (e) => { if (e.key === 'Escape') onClose() } + window.addEventListener('keydown', handler) + return () => window.removeEventListener('keydown', handler) + }, [onClose]) + + return ( +
+ +
+ ) +} + +// ─── Settings dropdown ──────────────────────────────────────────────────────── + +const SETTINGS_ITEMS = [ + { + to: '/settings/staff', + label: 'Staff Management', + icon: ( + + ), + }, + { + to: '/settings/public-features', + label: 'Public Features', + icon: ( + + ), + }, + { + to: '/settings/serial-logs', + label: 'Log Viewer', + icon: ( + + ), + }, + { + to: '/settings/pages', + label: 'Page Settings', + placeholder: true, + icon: ( + + ), + }, +] + +function SettingsMenu({ onClose }) { + const navigate = useNavigate() + + useEffect(() => { + const handler = (e) => { if (e.key === 'Escape') onClose() } + window.addEventListener('keydown', handler) + return () => window.removeEventListener('keydown', handler) + }, [onClose]) + + return ( +
+
+ Console Settings +
+ {SETTINGS_ITEMS.map((item) => ( + + ))} +
+ ) +} + +// ─── Header ─────────────────────────────────────────────────────────────────── + +export default function Header({ onMenuOpen }) { + const { user, logout, hasRole } = useAuth() + const [search, setSearch] = useState('') + const [profileOpen, setProfileOpen] = useState(false) + const [settingsOpen, setSettingsOpen] = useState(false) + const profileRef = useRef(null) + const settingsRef = useRef(null) + const canManageSettings = hasRole('sysadmin', 'admin') + + useEffect(() => { + if (!profileOpen) return + const handler = (e) => { + if (!profileRef.current?.contains(e.target)) setProfileOpen(false) + } + document.addEventListener('mousedown', handler) + return () => document.removeEventListener('mousedown', handler) + }, [profileOpen]) + + useEffect(() => { + if (!settingsOpen) return + const handler = (e) => { + if (!settingsRef.current?.contains(e.target)) setSettingsOpen(false) + } + document.addEventListener('mousedown', handler) + return () => document.removeEventListener('mousedown', handler) + }, [settingsOpen]) + + const initials = user?.name + ? user.name.split(' ').map((w) => w[0]).slice(0, 2).join('').toUpperCase() + : '?' + + return ( +
+ {/* ── Mobile hamburger ── */} + + + {/* ── Breadcrumb ── */} + + + {/* ── Flex spacer ── */} +
+ + {/* ── Right controls ── */} +
+ +
+ +
+ + + + {canManageSettings && ( +
+ + {settingsOpen && ( + setSettingsOpen(false)} /> + )} +
+ )} + + +
+ ) +} diff --git a/frontend/src/components/layout/MainLayout.jsx b/frontend/src/components/layout/MainLayout.jsx new file mode 100644 index 0000000..291e025 --- /dev/null +++ b/frontend/src/components/layout/MainLayout.jsx @@ -0,0 +1,47 @@ +// frontend/src/components/layout/MainLayout.jsx +// Root shell for all authenticated pages. +// +// Structure (sidebar-first): +// — position: fixed, full height, left column (224px) +// .main-area +//
— position: fixed, top-right (left: --sidebar-width) +//
— scrollable content, padded for fixed header +// +// Mobile (< 768px): sidebar hidden, header goes full-width, +// hamburger in header opens . + +import { useState } from 'react' +import { Outlet } from 'react-router-dom' +import Header from './Header' +import Sidebar from './Sidebar' +import MobileDrawer from './MobileDrawer' + +export default function MainLayout() { + const [drawerOpen, setDrawerOpen] = useState(false) + + return ( + <> + {/* Fixed sidebar — full viewport height */} + + + {/* Main area — offset from sidebar, full remaining width */} +
+
setDrawerOpen(true)} /> + +
+ +
+
+ + {/* Mobile slide-over drawer */} + setDrawerOpen(false)} + /> + + ) +} diff --git a/frontend/src/components/layout/MobileDrawer.jsx b/frontend/src/components/layout/MobileDrawer.jsx new file mode 100644 index 0000000..3a70c65 --- /dev/null +++ b/frontend/src/components/layout/MobileDrawer.jsx @@ -0,0 +1,72 @@ +// frontend/src/components/layout/MobileDrawer.jsx +// Full-height slide-over navigation drawer for mobile viewports (< 768px). +// Renders the Sidebar inside an overlay panel. +// +// Props: +// open — boolean — controls visibility +// onClose — fn — called on backdrop click, close button, or Escape + +import { useEffect } from 'react' +import Sidebar from './Sidebar' + +function CloseIcon() { + return ( + + ) +} + +export default function MobileDrawer({ open, onClose }) { + useEffect(() => { + document.body.style.overflow = open ? 'hidden' : '' + return () => { document.body.style.overflow = '' } + }, [open]) + + useEffect(() => { + if (!open) return + const handler = (e) => { if (e.key === 'Escape') onClose() } + window.addEventListener('keydown', handler) + return () => window.removeEventListener('keydown', handler) + }, [open, onClose]) + + if (!open) return null + + return ( + <> + {/* Scrim */} +
+ + {/* Drawer panel */} +
+
+ Navigation + +
+ +
+ +
+
+ + ) +} diff --git a/frontend/src/components/layout/Sidebar.jsx b/frontend/src/components/layout/Sidebar.jsx new file mode 100644 index 0000000..fad8173 --- /dev/null +++ b/frontend/src/components/layout/Sidebar.jsx @@ -0,0 +1,338 @@ +// frontend/src/components/layout/Sidebar.jsx +// Primary navigation sidebar — 224px wide, fixed, full height. +// +// Visual style (matches Stitch reference): +// - Brand header with logo at top +// - Section labels: plain uppercase text, generous padding, no rule lines +// - Nav items: px-6 py-3, full width, 3px left bar + primary-subtle bg when active +// - Collapsible groups: same row height as nav items +// - Children: darker inset bg, deep left-indent, text-color hover +// - Console Settings: pinned at bottom + +import { useState } from 'react' +import { NavLink, useLocation } from 'react-router-dom' +import { useAuth } from '@/hooks/useAuth' +import logoDark from '@/assets/logos/bell_systems_horizontal_darkMode.png' + +// ─── Icon set ───────────────────────────────────────────────────────────────── + +const S = ({ children, ...p }) => ( + +) + +const Icons = { + dashboard: () => , + devices: () => , + deviceOverview: () => , + fleet: () => , + commandCenter: () => , + blackBox: () => , + appUsers: () => , + melodies: () => , + library: () => , + composer: () => , + archetypes: () => , + melodySettings: () => , + communications: () => , + mail: () => , + whatsapp: () => , + sms: () => , + helpdesk: () => , + commsLog: () => , + customers: () => , + customerOverview: () => , + orders: () => , + quotations: () => , + products: () => , + catalog: () => , + snManager: () => , + staffLog: () => , + manufacturing: () => , + inventory: () => , + provisioning: () => , + firmware: () => , + api: () => , + settings: () => , + staff: () => , + publicFeatures: () => , + logViewer: () => , + placeholder: () => , + chevronRight: () => ( + + ), + lock: () => ( + + ), +} + +// ─── Nav structure ──────────────────────────────────────────────────────────── + +const navSections = [ + { type: 'separator', label: 'General' }, + { to: '/', label: 'Dashboard', icon: 'dashboard' }, + + { type: 'separator', label: 'Bell Cloud' }, + { + label: 'Devices', icon: 'devices', permission: 'devices', + children: [ + { to: '/devices/overview', label: 'Overview', icon: 'deviceOverview', placeholder: true }, + { to: '/devices', label: 'Fleet', icon: 'fleet', exact: true }, + { to: '/mqtt/commands', label: 'Command Center', icon: 'commandCenter' }, + { to: '/equipment/notes', label: 'BlackBox', icon: 'blackBox' }, + { to: '/devices/settings', label: 'Device Settings', icon: 'settings', placeholder: true }, + ], + }, + { to: '/users', label: 'App Users', icon: 'appUsers', permission: 'app_users' }, + { + label: 'Melodies', icon: 'melodies', permission: 'melodies', + children: [ + { to: '/melodies', label: 'Library', icon: 'library', exact: true }, + { to: '/melodies/composer', label: 'Composer', icon: 'composer' }, + { to: '/melodies/archetypes', label: 'Archetypes', icon: 'archetypes' }, + { to: '/melodies/settings', label: 'Melody Settings', icon: 'melodySettings' }, + ], + }, + + { type: 'separator', label: 'Headquarters' }, + { + label: 'Communications', icon: 'communications', permission: 'crm', + children: [ + { to: '/mail', label: 'Mailbox', icon: 'mail' }, + { to: '/comms/whatsapp', label: 'WhatsApp', icon: 'whatsapp', placeholder: true }, + { to: '/comms/sms', label: 'SMS', icon: 'sms', placeholder: true }, + { to: '/crm/comms/helpdesk', label: 'Helpdesk', icon: 'helpdesk', exact: true }, + { to: '/crm/comms', label: 'Comms Log', icon: 'commsLog', exact: true }, + ], + }, + { + label: 'Customers', icon: 'customers', permission: 'crm', + children: [ + { to: '/crm/customers', label: 'Overview', icon: 'customerOverview' }, + { to: '/crm/orders', label: 'Orders', icon: 'orders' }, + { to: '/crm/quotations', label: 'Quotations', icon: 'quotations' }, + ], + }, + { + label: 'Products', icon: 'products', permission: 'crm', + children: [ + { to: '/crm/products', label: 'Catalog', icon: 'catalog' }, + { to: '/crm/products/sn', label: 'S/N Manager', icon: 'snManager', placeholder: true }, + ], + }, + { to: '/staff-log', label: 'Staff Log', icon: 'staffLog', placeholder: true }, + + { type: 'separator', label: 'Engineering' }, + { + label: 'Manufacturing', icon: 'manufacturing', permission: 'manufacturing', + children: [ + { to: '/manufacturing', label: 'Inventory', icon: 'inventory', exact: true }, + { to: '/manufacturing/provision', label: 'Provisioning', icon: 'provisioning' }, + ], + }, + { to: '/firmware', label: 'Firmware Manager', icon: 'firmware', permission: 'manufacturing' }, + { to: '/developer/api', label: 'API Reference', icon: 'api', roleRequired: ['sysadmin', 'admin'] }, +] + +// ─── Helpers ────────────────────────────────────────────────────────────────── + +function isGroupActive(children, pathname) { + return children.some((child) => { + if (child.exact) return pathname === child.to + return pathname === child.to || pathname.startsWith(child.to + '/') + }) +} + +// ─── Section separator ──────────────────────────────────────────────────────── + +function SectionSeparator({ label, first }) { + return ( +
+ {label} +
+ ) +} + +// ─── Placeholder item ───────────────────────────────────────────────────────── + +function PlaceholderItem({ label, icon, child = false }) { + const IconComp = Icons[icon] ?? Icons.placeholder + return ( +
+ + {label} + soon +
+ ) +} + +// ─── Collapsible group ──────────────────────────────────────────────────────── + +function CollapsibleGroup({ label, icon, children, currentPath, locked, open, onToggle }) { + const IconComp = Icons[icon] ?? Icons.placeholder + const childActive = isGroupActive(children, currentPath) + const isOpen = open || childActive + + return ( +
+ + + {!locked && isOpen && ( +
+ {children.map((child) => + child.placeholder ? ( + + ) : ( + `nav-child-link${isActive ? ' active' : ''}`} + > + {({ isActive }) => { + const ChildIcon = Icons[child.icon] ?? Icons.placeholder + return ( + <> + + {child.label} + + ) + }} + + ) + )} +
+ )} +
+ ) +} + +// ─── Main Sidebar ───────────────────────────────────────────────────────────── + +export default function Sidebar() { + const { hasPermission, hasRole } = useAuth() + const location = useLocation() + + const [openGroup, setOpenGroup] = useState(() => { + for (const item of navSections) { + if (item.children && isGroupActive(item.children, location.pathname)) { + return item.label + } + } + return null + }) + + const canView = (item) => { + if (item.roleRequired) return hasRole(...item.roleRequired) + if (!item.permission) return true + return hasPermission(item.permission, 'view') + } + + const handleToggle = (label) => setOpenGroup((prev) => (prev === label ? null : label)) + + return ( + + ) +} diff --git a/frontend/src/components/shared/ActivityLog.jsx b/frontend/src/components/shared/ActivityLog.jsx new file mode 100644 index 0000000..2f81a0e --- /dev/null +++ b/frontend/src/components/shared/ActivityLog.jsx @@ -0,0 +1,4 @@ +// TODO: implement +export default function ActivityLog() { + return null +} diff --git a/frontend/src/components/shared/StaffNotesPanel.jsx b/frontend/src/components/shared/StaffNotesPanel.jsx new file mode 100644 index 0000000..9e1c046 --- /dev/null +++ b/frontend/src/components/shared/StaffNotesPanel.jsx @@ -0,0 +1,4 @@ +// TODO: implement +export default function StaffNotesPanel() { + return null +} diff --git a/frontend/src/components/ui/Breadcrumbs.jsx b/frontend/src/components/ui/Breadcrumbs.jsx new file mode 100644 index 0000000..8830eeb --- /dev/null +++ b/frontend/src/components/ui/Breadcrumbs.jsx @@ -0,0 +1,56 @@ +// src/components/ui/Breadcrumbs.jsx +// Standalone breadcrumb trail. Use on detail pages below the page title. +// PageHeader has breadcrumbs built-in; use this component when you need +// breadcrumbs independently of PageHeader. +// +// Props: +// items — Array<{ label: string, href?: string }> +// Last item is always rendered as current page (no link). +// className — extra classes on the