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>
5.4 KiB
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 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.jsruns after the extension scripts and finishes the mount withapp.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, exposingwindow.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.
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-sectionswitches base font between1remand1.25rem, - the modifier list (
.text-caption.text-grey-7) bumps to1.15rem- medium weight +
color: inherit(drops the muted grey),
- medium weight +
- the per-line note (
.text-caption.text-amber-9) bumps to1.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 5–8 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:
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.