Add the customer ordering page
Builds the main customer-facing flow: browse the Brand's menu, add items to a cart, and check out either as a guest (name + contact) or, if signed in, as the account customer — both call the existing placeOrder server function, which already distinguishes the two. On success, navigates to a new /orders/$orderId status page showing each Order Item's Preparation Steps, queue position, ETA, and (per the domain rules already established) a Cancel button while cancellable and a pickup-confirmation button once ready. Styled with SCSS Modules per CLAUDE.md; added the sass dependency and the src/styles/ structure it describes (globals.scss imported once in __root.tsx, alongside the existing Tailwind stylesheet the not-yet- mounted header-user.tsx component depends on). Verified end-to-end against a throwaway local Postgres: menu renders with seeded items, cart quantities update, guest checkout places a real order, and the status page reflects a real cancellation. Also fixes CLAUDE.md's Commands section, which documented pnpm even though this repo only has npm (package-lock.json, no pnpm installed). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+76
-10
@@ -1,12 +1,78 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute, useNavigate } from '@tanstack/react-router'
|
||||
import { useState } from 'react'
|
||||
|
||||
const Home = () => (
|
||||
<div className="p-8">
|
||||
<h1 className="text-4xl font-bold">Welcome to TanStack Start</h1>
|
||||
<p className="mt-4 text-lg">
|
||||
Edit <code>src/routes/index.tsx</code> to get started.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
import { CheckoutSection } from '#/components/ordering/CheckoutSection.tsx'
|
||||
import { MenuList } from '#/components/ordering/MenuList.tsx'
|
||||
import { OrderSummary } from '#/components/ordering/OrderSummary.tsx'
|
||||
import { getBrand } from '#/server/brand.ts'
|
||||
import { getMenu } from '#/server/menu.ts'
|
||||
|
||||
export const Route = createFileRoute('/')({ component: Home })
|
||||
import styles from './index.module.scss'
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
loader: async () => {
|
||||
const brand = await getBrand()
|
||||
const menu = await getMenu({ data: { brandId: brand.id } })
|
||||
return { brand, menu }
|
||||
},
|
||||
component: OrderingPage,
|
||||
})
|
||||
|
||||
function OrderingPage() {
|
||||
const { brand, menu } = Route.useLoaderData()
|
||||
const navigate = useNavigate()
|
||||
const [quantities, setQuantities] = useState<Record<number, number>>({})
|
||||
|
||||
const add = (menuItemId: number) =>
|
||||
setQuantities((prev) => ({
|
||||
...prev,
|
||||
[menuItemId]: (prev[menuItemId] ?? 0) + 1,
|
||||
}))
|
||||
|
||||
const remove = (menuItemId: number) =>
|
||||
setQuantities((prev) => {
|
||||
const current = prev[menuItemId] ?? 0
|
||||
if (current <= 1) {
|
||||
const next = { ...prev }
|
||||
delete next[menuItemId]
|
||||
return next
|
||||
}
|
||||
return { ...prev, [menuItemId]: current - 1 }
|
||||
})
|
||||
|
||||
const items = Object.entries(quantities).flatMap(([menuItemId, quantity]) =>
|
||||
Array.from({ length: quantity }, () => ({
|
||||
menuItemId: Number(menuItemId),
|
||||
})),
|
||||
)
|
||||
|
||||
return (
|
||||
<main className={styles.page}>
|
||||
<h1 className={styles.heading}>{brand.name}</h1>
|
||||
<div className={styles.layout}>
|
||||
<MenuList
|
||||
menu={menu}
|
||||
quantities={quantities}
|
||||
onAdd={add}
|
||||
onRemove={remove}
|
||||
/>
|
||||
<aside className={styles.sidebar}>
|
||||
<OrderSummary menu={menu} quantities={quantities} />
|
||||
<CheckoutSection
|
||||
items={items}
|
||||
disabled={items.length === 0}
|
||||
onPlaced={(order) =>
|
||||
navigate({
|
||||
to: '/orders/$orderId',
|
||||
params: { orderId: String(order.id) },
|
||||
search: order.guestAccessToken
|
||||
? { token: order.guestAccessToken }
|
||||
: {},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user