Implement ordering MVP domain layer per spec (issue #1)
Scaffolds the TanStack Start + Drizzle/Postgres + Better Auth app and implements the core ordering domain: menu browsing, guest/account order placement with online payment, per-Order-Item independent cooking and cancellation, dynamic Machine-Tag pool assignment, and multi-step recipes (see docs/adr/0001-0003). Adds a guest-access-token / session-based ownership check on every read/mutation so an order can't be viewed, cancelled, or picked up by anyone but its owner. Covered by 14 tests running the domain layer directly against a real, ephemeral Postgres (testcontainers) — createServerFn couldn't be called outside a real request context, so tests exercise the domain functions the server functions thinly wrap instead of the server functions themselves. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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 [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 [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 [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,
|
||||
machineTagId: ovenTag.id,
|
||||
estimatedDurationSeconds: 600,
|
||||
})
|
||||
|
||||
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,
|
||||
machineTagId: grillTag.id,
|
||||
estimatedDurationSeconds: 240,
|
||||
},
|
||||
{
|
||||
menuItemId: burger.id,
|
||||
sequence: 2,
|
||||
machineTagId: assemblyTag.id,
|
||||
estimatedDurationSeconds: 90,
|
||||
},
|
||||
])
|
||||
|
||||
return {
|
||||
brand,
|
||||
machineTags: { oven: ovenTag, grill: grillTag, assembly: assemblyTag },
|
||||
machines: { oven1, grill1, assembly1 },
|
||||
menuItems: { pizza, burger },
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user