# LogicTrade — Design prototypes

Interactive design prototypes for the new LogicTrade ERP modules. Click-through is functional — search, filter, drawer view, full edit page — running on mocked Dutch data. There is **no build step**: plain HTML loading React + Babel from a CDN, with each component as its own `.jsx` file transpiled in the browser.

Modules:

- **Offerte** (Verkoop) — fully built out: list, detail, drawer, "nieuwe offerte" canvas
- **Service** — aftersales/repairs: list view (table + board), detail (paper canvas), drawer, side panel
- **Planning** — outbound transport scheduling: list view + day view (per-dag-gegroepeerde kaarten), detail, drawer, side panel

## Run it

You need any static file server pointed at the `project/` directory.

```sh
# Option 1 — npx (zero install)
npx serve project -l 3000

# Option 2 — Python
python3 -m http.server 8000 --directory project
```

Then open <http://localhost:3000/> for the welcome page (`index.html`), which links into the SPA at `app.html`. Direct deeplinks: `app.html#offerte`, `app.html#service`, `app.html#planning`.

## How to use the prototype

### Lists

| Gesture / key                                  | Result                                                                                            |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **Single-click** a row · `Space` on active row | Read-only drawer slides in from the right (paper preview)                                         |
| **Double-click** a row · `Enter` on active row | Opens the full editable detail page                                                               |
| **Right-click** a row                          | Context menu (Openen · Wijzigen · Genereer order · Dupliceren · Klembord · Delen ▸ · Verwijderen) |
| `↑` / `↓`                                      | Move the keyboard-active row marker                                                               |

### Inside the drawer (view paper)

| Gesture / key                                                       | Result                                                     |
| ------------------------------------------------------------------- | ---------------------------------------------------------- |
| **Hamburger** ☰ in top-right of paper · `Space` · `↓` on hamburger | Opens the quick-actions menu                               |
| **Sparkle** ✦ in top-right of paper                                 | Opens Assist pre-loaded with this record as context        |
| `Enter` (outside an input)                                          | Opens the full editor — same as the menu's "Bewerken" item |
| `Escape`                                                            | Closes the menu if open, otherwise closes the drawer       |

### Inside the quick-actions menu

| Gesture / key                                 | Result                                         |
| --------------------------------------------- | ---------------------------------------------- |
| Click an action / `Enter` on focused item     | Run that action and close the menu             |
| `↑` / `↓` · `Home` / `End`                    | Move focus between menu items                  |
| `Escape`                                      | Close the menu, focus returns to the hamburger |
| Click outside the menu (on the glass overlay) | Close the menu                                 |

### Other

| Gesture                         | Result                                               |
| ------------------------------- | ---------------------------------------------------- |
| Status pill in detail header    | Click to switch between Definitief / Niet definitief |
| Bottom-right HUD                | Tweaks panel — density, surface, accent colour       |
| KPI card "Openstaande offertes" | Toggles filter `status = ["Niet definitief"]`        |
| Other KPI cards                 | Opens the Inzichten (Insights) page                  |
| Left sidebar chevron            | Collapses sidebar to 56px icon-only strip            |
| Right panel chevron             | Collapses right panel to 48px icon-only strip        |

`Nieuwe offerte.html` is a separate design canvas with two variants of the new-quote flow — useful for design review, not part of the main runtime.

## File structure

```
project/
├── index.html                # Welcome landing — links into the SPA
├── app.html                  # SPA shell — loads every module
├── Nieuwe offerte.html       # Design canvas: new-quote flow variants (offerte module)
│
├── shared/                   # Cross-module primitives + app shell
│   ├── app.jsx               # Root: module router, toasts, tweaks, hash deeplinks
│   ├── sidebar.jsx           # Left nav + module switcher
│   ├── topbar.jsx            # Top bar with the Assist button
│   ├── icons.jsx             # window.Icon — inline SVG icon set
│   ├── data.jsx              # Shared mock data (customers, sellers, shops, products, QUOTES)
│   ├── tweaks-panel.jsx      # Bottom-right tweak HUD (density / surface / accent)
│   ├── filters.jsx           # Config-driven FilterBar + applyFilters helpers
│   ├── klant-picker.jsx      # KlantPicker, ArtikelAddRow, InlineSelect, InlineDate
│   ├── styles.css            # Main stylesheet (design tokens + every component)
│   └── lt-logo.svg           # Brand mark
│
├── offerte/                  # Verkoop / quotation module
│   ├── offerte-root.jsx      # Module entry — owns state, registers in window.MODULES
│   ├── offerte-list.jsx      # Overview: KPIs, Inzichten, search, filters, table, context menu
│   ├── offerte-detail.jsx    # Editable detail page (ItemRow, SidePanel, modals, totals)
│   ├── offerte-drawer.jsx    # Read-only quick-view drawer (paper-quote layout)
│   ├── nieuwe-offerte.jsx    # Variant A + B of the new-quote flow
│   ├── nieuwe-offerte.css    # Supplementary styles for the nieuwe-offerte canvas
│   └── design-canvas.jsx     # Figma-ish wrapper used by Nieuwe offerte.html
│
├── service/                  # Aftersales / service tickets module
│   ├── service-root.jsx      # Module entry — registers in window.MODULES
│   ├── service-list.jsx      # Overview: KPIs, search, table + board toggle
│   ├── service-board.jsx     # Kanban-by-status board view (alternative to the table)
│   ├── service-detail.jsx    # Editable paper canvas (melding + artikelen + oplossing)
│   ├── service-drawer.jsx    # Read-only quick-view drawer (paper preview)
│   ├── service-side-panel.jsx  # Right rail accordions
│   ├── service-data.jsx      # Sample tickets, statuses, types, priorities
│   └── service.css           # Module-scoped styles (board cards, label chips, …)
│
└── planning/                 # Planning module — outbound transport scheduling
    ├── planning-root.jsx     # Module entry — owns afspraken state, routes list ↔ detail
    ├── planning-list.jsx     # Overview: search, filters, table OR day view (toggle)
    ├── planning-day.jsx      # Day-grouped card view (alternative to the table)
    ├── planning-detail.jsx   # Editable "werkbon" paper (adres, afspraak meta, artikelen, opmerking)
    ├── planning-drawer.jsx   # Read-only werkbon preview (single-click peek)
    ├── planning-side-panel.jsx  # Right rail: Notities + Historie accordions
    ├── planning-data.jsx     # Sample afspraken, statuses, soorten, hulpbronnen + bronrecord lookup
    └── planning.css          # Module-scoped styles (day cards, items table, betreft link, …)
```

### Module pattern

Each module lives in its own folder and exports a root component to `window.MODULES[id]`:

```js
window.MODULES.<id> = { id, label, Root: <ModuleRoot> };
```

`shared/app.jsx` reads this registry, renders the active module inside the persistent shell (sidebar + topbar + tweaks panel), and tracks the active module in the URL hash (`#offerte`, `#service`, `#planning`) so links into a specific module work.

## Architecture

Each `.jsx` file registers its components on `window` (e.g. `window.OfferteList = OfferteList`) so other Babel-compiled files can reference them — there are **no ES-module imports**. Load order in `app.html` matters: shared primitives load first, then each module, then `shared/app.jsx` last (it reads `window.MODULES`).

State lives where it belongs:

- `shared/app.jsx` — `moduleId` (active module, mirrored to URL hash), `tweaks`, `toasts`.
- `offerte/offerte-root.jsx` — `quotes` (mock offertes, mutable in-session) and `openId` (list vs detail).
- `service/service-root.jsx` — `tickets` and `openId` (same pattern as offerte).
- `planning/planning-root.jsx` — `afspraken` and `openId` (same pattern).

The drawer ("view") and edit page (`OfferteDetail`) are **separate components** rendered conditionally. The drawer is read-only by design; the **Bewerken** item in its quick-actions menu (or pressing `Enter` directly on the drawer) transitions you to the editable page.

### Cross-module reuse

When a useful component lives in another module, the prescribed escape hatch is to **expose it on `window`** and parametrize anything that's hard-coded copy. Currently:

- `window.KlantkaartModal` (from `offerte/offerte-detail.jsx`) — reused by planning's detail. Accepts an `unlinkLabel` prop so the footer button can read "Loskoppelen van afspraak" instead of "Loskoppelen van offerte".
- `window.getServiceAllOrders` (from `service/service-data.jsx`) — used by planning's `BetreftInlineField` to populate the verkooporders portion of the cross-module record search.

The same `.jsx`-on-`window` discipline applies: load order in `app.html` must put the providing module before the consumer.

The CSS cascade order is important: `styles.css` loads before `nieuwe-offerte.css`, so same-specificity rules in `nieuwe-offerte.css` **win**. Use `nieuwe-offerte.css` to override base styles without raising specificity.

Smooth CSS transitions (sidebar collapse, right panel collapse) require **both DOM states to always be rendered** — toggling between conditional JSX branches defeats transitions. Use opacity + pointer-events instead.

## Design system

A separate **[STYLEGUIDE.md](./STYLEGUIDE.md)** documents the full design system: tokens, components, motion rules, UX flows (including the keyboard/focus/drawer rules referenced above), and the rules-only checklist for matching this look in a new module. Read that for anything visual or behavioral; this README stays focused on "what is this and how to run it".

## Design tokens (quick reference)

CSS variables at the top of `styles.css`:

```
--accent-blue:    #0d35a0   Primary brand blue
--accent-magenta: #E6007E   Secondary brand accent (magenta)
--accent-green:   #009a82   Success / primary CTA
--ink-900 / 700 / 500 / 400 / 300   Text scale (5 steps, intentionally short)
--radius-md:      8px       Standard button/card border-radius
--sidebar-w:                CSS var set via JS (230px expanded / 56px collapsed)
```

### Colour usage conventions

| Colour             | Where used                                                                                                                                                                                                   |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--accent-blue`    | Links, active states (general), focus rings, appointment date badges, grand total in offerte                                                                                                                 |
| `--accent-magenta` | Active nav item (left border + icon), pagination active page, appointment counter badge, history timeline dot (most recent), active accordion icon, filter chip icon when set, "Niet definitief" status pill |
| `--accent-green`   | Primary CTA buttons ("Genereer order", "Nieuw")                                                                                                                                                              |

**Magenta is not used for body text** — only for backgrounds, borders, icons, and badges where it won't be perceived as a warning status.

### Button conventions

All buttons share: `height: 36px`, `border-radius: var(--radius-md)` (8px), `font-size: 13px`, `font-weight: 500`.

| Class               | Use                                                               |
| ------------------- | ----------------------------------------------------------------- |
| `.btn.btn--primary` | Primary filled CTA (green)                                        |
| `.btn.btn--ghost`   | Secondary outlined action                                         |
| `.btn--icon`        | Square icon-only button (36×36px)                                 |
| `.acc__add`         | Full-width dashed border action inside accordion panels           |
| `.add-row`          | Full-width dashed border button at the bottom of the article list |

### KPI cards

Four cards on the list overview. Each card has a coloured left border, trend pill, and period footnote:

- Blue — "Actuele orderwaarde" → opens Inzichten page
- Pink — "Openstaande offertes" → toggles `status = ["Niet definitief"]` filter
- Green — "Conversie ratio" → opens Inzichten page
- Purple — "Gem. offertebedrag" → opens Inzichten page

Hover reveals a coloured pill (top-right, `kpi__open-hint`) indicating the action.

### Right side panel (SidePanel)

Always renders **both** the icon strip and the accordion content in the DOM (CSS opacity/width transitions, not conditional rendering). `collapsed` state drives the CSS class `side-panel--collapsed`.

### Discount UX

Inline editable korting row in the totals section. When `headerDiscountPct > 0`: shows an editable % input and € input inside the totals row, with an X button outside the row on hover. When 0: shows "Korting toevoegen" button below the totals.

## Cache busting

Each `<script>` and `<link>` in `app.html` has a `?v=NN` query string. **Bump the number whenever you edit a file** so the browser fetches the new version instead of the cached one. `app.html` is the source of truth for current versions — there's no snapshot here on purpose, since it'd drift instantly.

## What this is not

Production code. No build pipeline, no tests, no minification — Babel transpiles in the browser at page-load. Fine for a click-through prototype; rebuild as a proper React/Vite/Next.js app for production.
