feat(cms): Vue 3 + Quasar 2 UMD CMS templates
LNbits convention: extends base.html, declares window.app =
Vue.createApp({mixins: [windowMixin], data, methods, created}); the
LNbits init-app.js loads after extension scripts and finishes the
mount with app.use(Quasar) + app.mount('#vue').
Pages
- index.html restaurant list / dashboard with create dialog;
scoped to the logged-in user's wallets.
- menu.html category sidebar + items grid; full item dialog
with price/currency/images/dietary/allergens/
ingredients/calories/stock/availability/featured.
Modifier groups managed in a separate dialog
with required|optional + one|many semantics.
- orders.html filterable q-table with status colors and inline
state-machine actions (accept/ready/complete/
cancel). Polls every 8s.
- kds.html kitchen display: card-per-order, items + selected
modifiers + notes, age-based color escalation
(>5min orange, >15min red), polls every 5s. The
poll loop is a stand-in until SSE/Nostr push
lands.
- settings.html restaurant profile editor + delete + per-instance
ext settings panel (Nostr publish toggle, auto-
accept, invoice expiry).
Static
- js/api.js single REST client (LNbits.api.request wrapper)
used by all pages.
- js/index.js dashboard logic.
- js/menu.js menu CRUD.
- js/orders.js order monitor.
- js/kds.js kitchen display.
- js/settings.js settings persistence.
Customer kiosk UI lives in ~/dev/webapp; this extension only ships
the operator console.
This commit is contained in:
parent
c37b17d474
commit
3382462af4
11 changed files with 1576 additions and 0 deletions
90
static/js/api.js
Normal file
90
static/js/api.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Thin REST client for the restaurant extension CMS.
|
||||
*
|
||||
* Exposes window.RestaurantAPI with one method per resource.
|
||||
* Every call is gated by the calling wallet's admin or invoice key
|
||||
* pulled from g.user.wallets[0] (or a wallet passed in explicitly).
|
||||
*/
|
||||
;(function () {
|
||||
const baseUrl = '/restaurant/api/v1'
|
||||
|
||||
function call(adminkey, method, path, body) {
|
||||
return LNbits.api.request(method, baseUrl + path, adminkey, body)
|
||||
}
|
||||
|
||||
window.RestaurantAPI = {
|
||||
// Restaurants
|
||||
listRestaurants: (key, allWallets = false) =>
|
||||
call(key, 'GET', `/restaurants?all_wallets=${allWallets}`),
|
||||
getRestaurant: (id) => call(null, 'GET', `/restaurants/${id}`),
|
||||
createRestaurant: (key, data) => call(key, 'POST', '/restaurants', data),
|
||||
updateRestaurant: (key, id, data) =>
|
||||
call(key, 'PUT', `/restaurants/${id}`, data),
|
||||
deleteRestaurant: (key, id) =>
|
||||
call(key, 'DELETE', `/restaurants/${id}`),
|
||||
|
||||
// Categories
|
||||
listCategories: (id) => call(null, 'GET', `/restaurants/${id}/categories`),
|
||||
createCategory: (key, data) => call(key, 'POST', '/categories', data),
|
||||
deleteCategory: (key, id) => call(key, 'DELETE', `/categories/${id}`),
|
||||
|
||||
// Subcategories
|
||||
listSubcategories: (catId) =>
|
||||
call(null, 'GET', `/categories/${catId}/subcategories`),
|
||||
createSubcategory: (key, data) => call(key, 'POST', '/subcategories', data),
|
||||
deleteSubcategory: (key, id) => call(key, 'DELETE', `/subcategories/${id}`),
|
||||
|
||||
// Menu items
|
||||
getMenu: (id) => call(null, 'GET', `/restaurants/${id}/menu`),
|
||||
getMenuItem: (id) => call(null, 'GET', `/menu_items/${id}`),
|
||||
createMenuItem: (key, data) => call(key, 'POST', '/menu_items', data),
|
||||
updateMenuItem: (key, id, data) =>
|
||||
call(key, 'PUT', `/menu_items/${id}`, data),
|
||||
deleteMenuItem: (key, id) => call(key, 'DELETE', `/menu_items/${id}`),
|
||||
|
||||
// Modifier groups + modifiers
|
||||
listModifierGroups: (itemId) =>
|
||||
call(null, 'GET', `/menu_items/${itemId}/modifier_groups`),
|
||||
createModifierGroup: (key, data) =>
|
||||
call(key, 'POST', '/modifier_groups', data),
|
||||
deleteModifierGroup: (key, id) =>
|
||||
call(key, 'DELETE', `/modifier_groups/${id}`),
|
||||
listModifiers: (groupId) =>
|
||||
call(null, 'GET', `/modifier_groups/${groupId}/modifiers`),
|
||||
createModifier: (key, data) => call(key, 'POST', '/modifiers', data),
|
||||
deleteModifier: (key, id) => call(key, 'DELETE', `/modifiers/${id}`),
|
||||
|
||||
// Availability windows
|
||||
listAvailability: (itemId) =>
|
||||
call(null, 'GET', `/menu_items/${itemId}/availability_windows`),
|
||||
createAvailability: (key, data) =>
|
||||
call(key, 'POST', '/availability_windows', data),
|
||||
deleteAvailability: (key, id) =>
|
||||
call(key, 'DELETE', `/availability_windows/${id}`),
|
||||
|
||||
// Orders
|
||||
listOrders: (key, restaurantId, statuses, limit = 200) => {
|
||||
const qs = new URLSearchParams({limit})
|
||||
;(statuses || []).forEach((s) => qs.append('statuses', s))
|
||||
return call(key, 'GET', `/restaurants/${restaurantId}/orders?${qs}`)
|
||||
},
|
||||
getOrder: (id) => call(null, 'GET', `/orders/${id}`),
|
||||
placeOrder: (data) => call(null, 'POST', '/orders', data),
|
||||
quoteOrder: (items) => call(null, 'POST', '/orders/quote', items),
|
||||
transitionOrder: (key, id, newStatus) =>
|
||||
call(key, 'PUT', `/orders/${id}/status/${newStatus}`),
|
||||
|
||||
// Print jobs
|
||||
listPrintJobs: (key, restaurantId, status) =>
|
||||
call(
|
||||
key,
|
||||
'GET',
|
||||
`/restaurants/${restaurantId}/print_jobs${status ? `?status=${status}` : ''}`
|
||||
),
|
||||
ackPrintJob: (key, id) => call(key, 'PUT', `/print_jobs/${id}/ack`),
|
||||
|
||||
// Settings
|
||||
getSettings: (key) => call(key, 'GET', '/settings'),
|
||||
updateSettings: (key, data) => call(key, 'PUT', '/settings', data)
|
||||
}
|
||||
})()
|
||||
Loading…
Add table
Add a link
Reference in a new issue