A ready Order Item now gets the lowest-numbered free Pickup Box (or waits for one to free up), shown as "Pickup box N" on its status. The customer opens the box from their order and closes it again; closing marks the item picked up and hands the box to the next waiting item. On the order card the box button comes first, full width on mobile, with a padlock that lifts on open and snaps shut before the button collapses away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
202 lines
5.5 KiB
TypeScript
202 lines
5.5 KiB
TypeScript
import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
|
|
|
|
import * as schema from '#/db/schema.ts'
|
|
|
|
// Sample Brand/menu/hardware data for local dev and as the shared fixture
|
|
// tests seed against. Specific menu items are explicitly out of scope for
|
|
// the ordering spec — Pizza/Burger here are illustrative, not final content.
|
|
export const seedGhostKitchen = async (db: NodePgDatabase<typeof schema>) => {
|
|
const [brand] = await db
|
|
.insert(schema.brands)
|
|
.values({ name: 'Ghost Kitchen' })
|
|
.returning()
|
|
|
|
const [prepTag] = await db
|
|
.insert(schema.machineTags)
|
|
.values({ name: 'prep' })
|
|
.returning()
|
|
const [ovenTag] = await db
|
|
.insert(schema.machineTags)
|
|
.values({ name: 'oven' })
|
|
.returning()
|
|
const [grillTag] = await db
|
|
.insert(schema.machineTags)
|
|
.values({ name: 'grill' })
|
|
.returning()
|
|
const [assemblyTag] = await db
|
|
.insert(schema.machineTags)
|
|
.values({ name: 'assembly' })
|
|
.returning()
|
|
|
|
const [prep1] = await db
|
|
.insert(schema.machines)
|
|
.values({ name: 'Prep Station 1', machineTagId: prepTag.id })
|
|
.returning()
|
|
const [oven1] = await db
|
|
.insert(schema.machines)
|
|
.values({ name: 'Oven 1', machineTagId: ovenTag.id })
|
|
.returning()
|
|
const [grill1] = await db
|
|
.insert(schema.machines)
|
|
.values({ name: 'Grill 1', machineTagId: grillTag.id })
|
|
.returning()
|
|
const [assembly1] = await db
|
|
.insert(schema.machines)
|
|
.values({ name: 'Assembly Station 1', machineTagId: assemblyTag.id })
|
|
.returning()
|
|
|
|
const pickupBoxes = await db
|
|
.insert(schema.pickupBoxes)
|
|
.values([1, 2, 3, 4].map((number) => ({ number })))
|
|
.returning()
|
|
|
|
const [pizza] = await db
|
|
.insert(schema.menuItems)
|
|
.values({
|
|
brandId: brand.id,
|
|
name: 'Pizza',
|
|
description: 'Classic oven-baked pizza.',
|
|
priceCents: 1299,
|
|
})
|
|
.returning()
|
|
|
|
await db.insert(schema.menuItemSteps).values([
|
|
{
|
|
menuItemId: pizza.id,
|
|
sequence: 1,
|
|
name: 'Flattening dough',
|
|
machineTagId: prepTag.id,
|
|
estimatedDurationSeconds: 30,
|
|
},
|
|
{
|
|
menuItemId: pizza.id,
|
|
sequence: 2,
|
|
name: 'Spreading sauce',
|
|
machineTagId: prepTag.id,
|
|
estimatedDurationSeconds: 20,
|
|
},
|
|
{
|
|
menuItemId: pizza.id,
|
|
sequence: 3,
|
|
name: 'Adding toppings',
|
|
machineTagId: prepTag.id,
|
|
estimatedDurationSeconds: 30,
|
|
},
|
|
{
|
|
menuItemId: pizza.id,
|
|
sequence: 4,
|
|
name: 'Baking in the oven',
|
|
machineTagId: ovenTag.id,
|
|
estimatedDurationSeconds: 600,
|
|
},
|
|
{
|
|
menuItemId: pizza.id,
|
|
sequence: 5,
|
|
name: 'Slicing',
|
|
machineTagId: assemblyTag.id,
|
|
estimatedDurationSeconds: 20,
|
|
},
|
|
{
|
|
menuItemId: pizza.id,
|
|
sequence: 6,
|
|
name: 'Packaging',
|
|
machineTagId: assemblyTag.id,
|
|
estimatedDurationSeconds: 20,
|
|
},
|
|
])
|
|
|
|
const pizzaOptions = await db
|
|
.insert(schema.menuItemOptions)
|
|
.values([
|
|
{ menuItemId: pizza.id, name: 'Extra cheese', priceDeltaCents: 150 },
|
|
{ menuItemId: pizza.id, name: 'No onions', priceDeltaCents: 0 },
|
|
{ menuItemId: pizza.id, name: 'Extra sauce', priceDeltaCents: 0 },
|
|
])
|
|
.returning()
|
|
|
|
const [burger] = await db
|
|
.insert(schema.menuItems)
|
|
.values({
|
|
brandId: brand.id,
|
|
name: 'Burger',
|
|
description: 'Grilled burger, assembled to order.',
|
|
priceCents: 999,
|
|
})
|
|
.returning()
|
|
|
|
await db.insert(schema.menuItemSteps).values([
|
|
{
|
|
menuItemId: burger.id,
|
|
sequence: 1,
|
|
name: 'Grilling patty',
|
|
machineTagId: grillTag.id,
|
|
estimatedDurationSeconds: 240,
|
|
},
|
|
{
|
|
menuItemId: burger.id,
|
|
sequence: 2,
|
|
name: 'Assembling burger',
|
|
machineTagId: assemblyTag.id,
|
|
estimatedDurationSeconds: 90,
|
|
},
|
|
])
|
|
|
|
const burgerOptions = await db
|
|
.insert(schema.menuItemOptions)
|
|
.values([
|
|
{ menuItemId: burger.id, name: 'Extra cheese', priceDeltaCents: 100 },
|
|
{ menuItemId: burger.id, name: 'No pickles', priceDeltaCents: 0 },
|
|
{ menuItemId: burger.id, name: 'Extra patty', priceDeltaCents: 250 },
|
|
])
|
|
.returning()
|
|
|
|
const allergens = await db
|
|
.insert(schema.allergens)
|
|
.values([
|
|
{ name: 'Gluten' },
|
|
{ name: 'Dairy' },
|
|
{ name: 'Eggs' },
|
|
{ name: 'Soy' },
|
|
{ name: 'Peanuts' },
|
|
{ name: 'Tree nuts' },
|
|
{ name: 'Sesame' },
|
|
])
|
|
.returning()
|
|
const allergenByName = Object.fromEntries(
|
|
allergens.map((allergen) => [allergen.name, allergen]),
|
|
)
|
|
|
|
await db.insert(schema.menuItemAllergens).values([
|
|
{ menuItemId: pizza.id, allergenId: allergenByName['Gluten'].id },
|
|
{ menuItemId: pizza.id, allergenId: allergenByName['Dairy'].id },
|
|
{ menuItemId: burger.id, allergenId: allergenByName['Gluten'].id },
|
|
{ menuItemId: burger.id, allergenId: allergenByName['Sesame'].id },
|
|
])
|
|
|
|
return {
|
|
brand,
|
|
machineTags: {
|
|
prep: prepTag,
|
|
oven: ovenTag,
|
|
grill: grillTag,
|
|
assembly: assemblyTag,
|
|
},
|
|
machines: { prep1, oven1, grill1, assembly1 },
|
|
pickupBoxes,
|
|
menuItems: { pizza, burger },
|
|
menuItemOptions: { pizza: pizzaOptions, burger: burgerOptions },
|
|
allergens,
|
|
}
|
|
}
|
|
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
const { config } = await import('dotenv')
|
|
config({ path: ['.env.local', '.env'] })
|
|
|
|
const { drizzle } = await import('drizzle-orm/node-postgres')
|
|
const db = drizzle(process.env.DATABASE_URL!, { schema })
|
|
await seedGhostKitchen(db)
|
|
console.log('Seeded Ghost Kitchen sample data.')
|
|
process.exit(0)
|
|
}
|