Files
greatbear/pb/migrations/1788500200_menu_ingredients.js
T
tanshu d9ba227657 Ingredients as many-to-many relations + VS Code/Antigravity launch.json
- new ingredients collection (uid, name, emoji icon, diet_class) synced
  by update-menu.mjs; menu_items.ingredients is an M2M relation — the
  script maps data-file uids to fresh PB record ids
- diet select removed: the green/red FSSAI badge is derived (non-veg when
  any linked ingredient is non-veg); 164 items tagged (11 ingredients)
- menu cards render ingredient chips (icon + name) under the description
- migration 1788500200_menu_ingredients.js (idempotent create — app.save
  returns nil on success in the JSVM, so don't test its return value)
- .vscode/launch.json: PocketBase + Vite dev, build+preview, menu sync,
  full-stack compound (dev PB on :8091 — 8090 is taken on this host)
2026-09-03 19:08:46 +05:30

126 lines
4.4 KiB
JavaScript

/// <reference path="../pb_data/types.d.ts" />
migrate(
(app) => {
// v4: ingredients become a many-to-many relation instead of the diet
// select. Each ingredient carries its own icon (emoji) and diet class;
// an item's veg/non-veg badge is derived from its ingredients. The menu
// records are re-synced by scripts/update-menu.mjs.
const ingredientsCollectionDef = () =>
new Collection({
name: 'ingredients',
type: 'base',
listRule: '',
viewRule: '',
createRule: null,
updateRule: null,
deleteRule: null,
fields: [
{ name: 'uid', type: 'text', required: true },
{ name: 'name', type: 'text', required: true },
{ name: 'icon', type: 'text' },
{ name: 'diet_class', type: 'select', maxSelect: 1, values: ['veg', 'nonveg'], required: true },
{ name: 'sort', type: 'number' },
],
indexes: ['CREATE UNIQUE INDEX idx_ingredients_uid ON ingredients (uid)'],
})
// app.save() returns nil (falsy) on success, so don't test its return
// value — resolve the saved collection by name instead. The lookup-first
// pattern keeps the migration idempotent if a previous run created the
// collection before failing.
let ingredientsCollection
try {
ingredientsCollection = app.findCollectionByNameOrId('ingredients')
} catch (e) {
app.save(ingredientsCollectionDef())
ingredientsCollection = app.findCollectionByNameOrId('ingredients')
}
try {
const oldCollection = app.findCollectionByNameOrId('menu_items')
app.delete(oldCollection)
} catch (e) {
// collection may not exist on a fresh database — nothing to migrate
}
const menuItems = new Collection({
name: 'menu_items',
type: 'base',
listRule: '',
viewRule: '',
createRule: null,
updateRule: null,
deleteRule: null,
fields: [
{ name: 'uid', type: 'text', required: true },
{ name: 'category', type: 'text', required: true },
{ name: 'name', type: 'text', required: true },
{ name: 'variant', type: 'text' },
{ name: 'description', type: 'text' },
{ name: 'price', type: 'number', required: true },
{ name: 'ingredients', type: 'relation', collectionId: ingredientsCollection.id, cascadeDelete: false, maxSelect: 99 },
{ name: 'pairing', type: 'text' },
{ name: 'tag', type: 'text' },
{ name: 'image', type: 'text' },
{ name: 'sku', type: 'text' },
{ name: 'available', type: 'bool' },
{ name: 'tax_category', type: 'text' },
{ name: 'sort', type: 'number' },
],
indexes: [
'CREATE UNIQUE INDEX idx_menu_items_uid ON menu_items (uid)',
'CREATE UNIQUE INDEX idx_menu_items_sku ON menu_items (sku) WHERE sku != \'\'',
],
})
return app.save(menuItems)
},
(app) => {
// Revert to the v3 menu_items (diet select) and drop ingredients.
try {
const newCollection = app.findCollectionByNameOrId('menu_items')
app.delete(newCollection)
} catch (e) {
// nothing to revert
}
const menuItems = new Collection({
name: 'menu_items',
type: 'base',
listRule: '',
viewRule: '',
createRule: null,
updateRule: null,
deleteRule: null,
fields: [
{ name: 'uid', type: 'text', required: true },
{ name: 'category', type: 'text', required: true },
{ name: 'name', type: 'text', required: true },
{ name: 'variant', type: 'text' },
{ name: 'description', type: 'text' },
{ name: 'price', type: 'number', required: true },
{ name: 'diet', type: 'select', maxSelect: 1, values: ['veg', 'nonveg', 'mixed'] },
{ name: 'pairing', type: 'text' },
{ name: 'tag', type: 'text' },
{ name: 'image', type: 'text' },
{ name: 'sku', type: 'text' },
{ name: 'available', type: 'bool' },
{ name: 'tax_category', type: 'text' },
{ name: 'sort', type: 'number' },
],
indexes: [
'CREATE UNIQUE INDEX idx_menu_items_uid ON menu_items (uid)',
'CREATE UNIQUE INDEX idx_menu_items_sku ON menu_items (sku) WHERE sku != \'\'',
],
})
app.save(menuItems)
try {
const ingredients = app.findCollectionByNameOrId('ingredients')
app.delete(ingredients)
} catch (e) {
// nothing to revert
}
return true
}
)