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:
2026-09-10 11:58:24 +02:00
co-authored by Claude Sonnet 5
parent 5059f40d4c
commit 79bd64ce8c
54 changed files with 3514 additions and 301 deletions
+38
View File
@@ -0,0 +1,38 @@
// Guests have no account to list Orders under, so their Order history is
// tracked client-side instead: each placed guest Order's id + access token
// is remembered here so `/orders` can look them back up without a sign-in.
const STORAGE_KEY = 'ghost-kitchen-guest-orders'
export type GuestOrderRef = { orderId: number; guestAccessToken: string }
const isGuestOrderRef = (value: unknown): value is GuestOrderRef =>
typeof value === 'object' &&
value !== null &&
typeof (value as GuestOrderRef).orderId === 'number' &&
typeof (value as GuestOrderRef).guestAccessToken === 'string'
export const getGuestOrderRefs = (): Array<GuestOrderRef> => {
try {
const raw = window.localStorage.getItem(STORAGE_KEY)
if (!raw) return []
const parsed: unknown = JSON.parse(raw)
return Array.isArray(parsed) ? parsed.filter(isGuestOrderRef) : []
} catch {
return []
}
}
export const addGuestOrderRef = (ref: GuestOrderRef) => {
try {
const existing = getGuestOrderRefs().filter(
(stored) => stored.orderId !== ref.orderId,
)
window.localStorage.setItem(
STORAGE_KEY,
JSON.stringify([ref, ...existing]),
)
} catch {
// Unavailable (private mode, quota) — the order still works via its
// own link, it just won't show up in the local Orders list.
}
}