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

5.4 KiB
Raw Permalink Blame History

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

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:

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