Add guest/account auth flows, order tracking, and order history UI
- Wire up Better Auth (email/password + Google/Apple SSO) with a sign-in/sign-up UI, header account menu, and a basket that follows the signed-in-or-guest state. - Give the order status page a vertical timeline per preparation step (with a distinct customer-facing name separate from the machine tag, a done/in-progress/queued/pending look, and a sonar-ping highlight on the active step), a formatted ETA (relative under 3h, absolute clock time beyond that), and Buy again / Get help / Cancel actions. - Expand Pizza's recipe into 6 steps across a shared prep station and the existing oven/assembly stations, with matching schema, seed, and domain changes plus updated/added test coverage. - Add an Orders list (account orders via session, guest orders via a locally remembered access-token list) with a shared getOrdersForCustomer domain summary, and remember a guest's name/contact after their first order so checkout doesn't ask again. - Improve page transitions with the View Transitions API: a shared element morph between an order card and its detail page, a stable (non-jumping) header across navigations, and reduced-motion handling. - Add a Ghost Kitchen wordmark (Space Grotesk, faded "Ghost", ghost icon standing in for the "o") and shrink the basket icon to match the rest of the header controls. - Add a run-ghost-kitchen agent skill documenting local setup, the dev-server driver, and the gotchas hit along the way. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { Link } from '@tanstack/react-router'
|
||||
|
||||
import type { CustomerOrderSummary } from '#/domain/ordering.ts'
|
||||
|
||||
import styles from './OrdersListView.module.scss'
|
||||
|
||||
type OrdersListViewProps = {
|
||||
orders: Array<CustomerOrderSummary>
|
||||
}
|
||||
|
||||
const formatPrice = (cents: number) => `$${(cents / 100).toFixed(2)}`
|
||||
|
||||
const formatItems = (items: CustomerOrderSummary['items']) =>
|
||||
items
|
||||
.map(({ menuItemName, quantity }) =>
|
||||
quantity > 1 ? `${quantity}× ${menuItemName}` : menuItemName,
|
||||
)
|
||||
.join(', ')
|
||||
|
||||
export const OrdersListView = ({ orders }: OrdersListViewProps) => (
|
||||
<section className={styles.page}>
|
||||
<h1 className={styles.heading}>Your orders</h1>
|
||||
{orders.length === 0 ? (
|
||||
<p className={styles.empty}>
|
||||
You haven't placed an order yet — <Link to="/">browse the menu</Link>{' '}
|
||||
to get started.
|
||||
</p>
|
||||
) : (
|
||||
<ul className={styles.orders}>
|
||||
{orders.map((order) => (
|
||||
<li key={order.id}>
|
||||
<Link
|
||||
to="/orders/$orderId"
|
||||
params={{ orderId: String(order.id) }}
|
||||
search={
|
||||
order.guestAccessToken
|
||||
? { token: order.guestAccessToken }
|
||||
: undefined
|
||||
}
|
||||
className={styles.order}
|
||||
style={{ viewTransitionName: `order-card-${order.id}` }}
|
||||
>
|
||||
<div className={styles.orderHeader}>
|
||||
<span className={styles.orderName}>Order #{order.id}</span>
|
||||
<span className={styles.orderStatus}>{order.status}</span>
|
||||
</div>
|
||||
<p className={styles.orderItems}>{formatItems(order.items)}</p>
|
||||
<p className={styles.orderMeta}>
|
||||
{order.createdAt.toLocaleDateString([], {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}{' '}
|
||||
· {formatPrice(order.totalCents)}
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
Reference in New Issue
Block a user