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.
85 lines
2 KiB
JavaScript
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()
|
|
}
|
|
})
|