restaurant/docs/cms.md
Padreug 4739ec0127 feat(cms): KDS cooking-mode bumps item + modifier visibility
When a card hits `accepted` the items section bumps base font to
1.25rem and modifier/note lines to 1.15rem + medium weight; the
muted grey on modifiers drops to inherited color. All via Vue
`:style` bindings — class-based CSS rules lose to lnbits' upstream
`!important` on Quasar typography utilities (even with our own
`!important`), so inline wins without an arms race.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 07:01:49 +02:00

128 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CMS
The operator-facing console — what the restaurant owner sees when
they enable the extension. Lives under `/restaurant/...` and uses
LNbits' built-in **Vue 3.5 + Quasar 2.18 UMD** runtime: no build
step, no bundler, just Jinja templates including a per-page JS file.
## Pages
All pages extend `base.html` (provided by LNbits core) and require a
logged-in user (`check_user_exists`).
| Path | Template | Script | Purpose |
|---|---|---|---|
| `/restaurant/` | `restaurant/index.html` | `static/js/index.js` | Restaurant list / dashboard |
| `/restaurant/{slug}` | `restaurant/menu.html` | `static/js/menu.js` | [[menu-tree\|menu]] builder (q-tree) |
| `/restaurant/{slug}/orders` | `restaurant/orders.html` | `static/js/orders.js` | Order monitor |
| `/restaurant/{slug}/kds` | `restaurant/kds.html` | `static/js/kds.js` | Kitchen Display |
| `/restaurant/{slug}/settings` | `restaurant/settings.html` | `static/js/settings.js` | Restaurant + extension settings |
## Conventions
- `{% extends "base.html" %}` and `{% from "macros.jinja" import window_vars with context %}`.
- Page content goes in `{% block page %}`. Scripts in `{% block scripts %}`, after `{{ window_vars(user) }}`.
- The page JS sets `window.app = Vue.createApp({mixins: [windowMixin], data, methods, created})`. LNbits' `init-app.js` runs after the extension scripts and finishes the mount with `app.use(Quasar)` + `app.mount('#vue')`**don't call `.mount()` yourself**.
- Bootstrap data is injected via `<script>window.X = {{ thing | tojson | safe }}</script>` between the macro and the per-page script.
- The shared REST client is `static/js/api.js`, exposing `window.RestaurantAPI` (one method per resource).
## Menu builder (q-tree)
The menu page uses Quasar's `q-tree` directly off the hydrated tree
returned by `GET /api/v1/restaurants/{id}/menu`. Three-pane layout:
```
+--------------------+----------------+----------------------------+
| sidebar nav | q-tree | Items panel |
| (orders / KDS / | with inline | (filtered by selected |
| settings links) | edit buttons | tree node) |
+--------------------+----------------+----------------------------+
```
Custom `default-header` slot renders:
- node name + item-count badge + child-count hint
- inline buttons: `add` (disabled at depth 3), `edit`,
`drive_file_move`, `delete` (with cascade prompt)
Add-root button sits above the tree (`+ New top-level`).
The Move dialog uses a flat-indented `q-select` of all nodes,
filtered to exclude the moved node + its descendants and any
depth-3 candidate. (The server enforces both checks too — see
[[menu-tree]].)
Drag-drop reorder is **v2**; v1 uses the explicit Move dialog.
## Item dialog
The item dialog includes a flat-indented `q-select` for `node_id`,
populated by walking the tree with em-space indentation per depth
level. An item can land on any node, not just leaves.
Modifier groups + modifiers live in a separate dialog (a child of
the item dialog) with the `chooseOne / chooseMany / required /
optional` semantics from [[data-model|the data model]].
## Order monitor + KDS
Both use the same data source (`GET /restaurants/{id}/orders`)
filtered by status. The KDS view escalates color by age (`>5min`
orange, `>15min` red) and offers one-tap state transitions.
When a card transitions to `accepted` (driven by `cookingMode(order)`
in `kds.js`), three inline `:style` bindings kick in:
- the items `q-card-section` switches base font between `1rem` and
`1.25rem`,
- the modifier list (`.text-caption.text-grey-7`) bumps to `1.15rem`
+ medium weight + `color: inherit` (drops the muted grey),
- the per-line note (`.text-caption.text-amber-9`) bumps to `1.15rem`
+ medium weight; color is left alone so it stays amber.
All cooking-mode styling is inline because an upstream `!important`
rule (likely an lnbits theme override on Quasar's typography
utilities) defeats class-based CSS rules — even with `!important`
on our side. Inline `:style` wins without needing the arms race.
Card chrome and the age-based `bg-{color}-1` from `cardClass()`
are untouched. The amber
per-line note keeps its color because only `.text-grey-7` is
overridden. No background rules; card chrome and the age-based
`bg-{color}-1` from `cardClass()` are untouched.
Today the monitor + KDS poll every 58 s. SSE / Nostr push is on
the roadmap.
### Dark-mode color discipline
Quasar's pale `bg-{color}-1` utility classes (e.g. `bg-orange-1`,
`bg-red-1`, `bg-amber-1`) pair fine with the default light theme
but render **white-on-cream** under LNbits' dark theme — the
q-card otherwise inherits the body's light text color. The KDS
cards pin a dark text class alongside every pale background so
the card stays legible regardless of theme:
```js
if (order.status === 'ready') return 'bg-amber-1 text-grey-9'
if (ageSec > 900) return 'bg-red-1 text-grey-9'
if (ageSec > 300) return 'bg-orange-1 text-grey-9'
return '' // theme-default branch keeps q-card's own text color
```
Any future surface that ages / escalates with `bg-{color}-1` must
do the same. Never assume a pale background "just works" on dark
theme.
## Settings
`settings.html` saves restaurant fields via
`PUT /restaurants/{id}` and (for LNbits admins) extension-wide
toggles via `PUT /settings`. NIP-17 orders toggle is currently
disabled because the unwrap step is stubbed — see [[nostr-layer]].
## See also
- [[architecture]]
- [[menu-tree]]
- [[order-flow]]
- [[api-reference]]