restaurant/static/js/index.js
Padreug 3382462af4 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.
2026-05-09 07:11:06 +02:00

85 lines
2 KiB
JavaScript

window.app = Vue.createApp({
el: '#vue',
mixins: [windowMixin],
data() {
return {
restaurants: [],
restaurantDialog: {
show: false,
data: this._blankRestaurant()
}
}
},
methods: {
_blankRestaurant() {
return {
wallet: '',
name: '',
slug: '',
description: '',
location: '',
currency: 'sat',
timezone: 'UTC'
}
},
openRestaurantDialog() {
this.restaurantDialog.data = this._blankRestaurant()
if (this.g.user.wallets.length) {
this.restaurantDialog.data.wallet = this.g.user.wallets[0].id
}
this.restaurantDialog.show = true
},
async fetchRestaurants() {
if (!this.g.user.wallets.length) return
const key = this.g.user.wallets[0].adminkey
try {
const {data} = await RestaurantAPI.listRestaurants(key, true)
this.restaurants = data
} catch (err) {
LNbits.utils.notifyApiError(err)
}
},
async createRestaurant() {
const key = this.g.user.wallets.find(
(w) => w.id === this.restaurantDialog.data.wallet
)?.adminkey
if (!key) {
Quasar.Notify.create({type: 'negative', message: 'Pick a wallet'})
return
}
try {
const {data} = await RestaurantAPI.createRestaurant(
key,
this.restaurantDialog.data
)
this.restaurants.unshift(data)
this.restaurantDialog.show = false
Quasar.Notify.create({
type: 'positive',
message: `Created ${data.name}`
})
} catch (err) {
LNbits.utils.notifyApiError(err)
}
}
},
computed: {
'g.user.walletOptions'() {
return this.g.user.wallets.map((w) => ({
label: w.name,
value: w.id
}))
}
},
async created() {
// Decorate g.user with wallet options for the dialog select.
this.g.user.walletOptions = this.g.user.wallets.map((w) => ({
label: w.name,
value: w.id
}))
await this.fetchRestaurants()
}
})