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,39 @@
|
||||
// A guest's name/contact, remembered locally so checkout doesn't ask again
|
||||
// on their next order from this browser.
|
||||
const STORAGE_KEY = 'ghost-kitchen-guest-identity'
|
||||
|
||||
export type GuestIdentity = { name: string; contact: string }
|
||||
|
||||
const isGuestIdentity = (value: unknown): value is GuestIdentity =>
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
typeof (value as GuestIdentity).name === 'string' &&
|
||||
typeof (value as GuestIdentity).contact === 'string'
|
||||
|
||||
export const getGuestIdentity = (): GuestIdentity | null => {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY)
|
||||
if (!raw) return null
|
||||
const parsed: unknown = JSON.parse(raw)
|
||||
return isGuestIdentity(parsed) ? parsed : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const saveGuestIdentity = (identity: GuestIdentity) => {
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(identity))
|
||||
} catch {
|
||||
// Unavailable (private mode, quota) — checkout still works, it just
|
||||
// asks again next time.
|
||||
}
|
||||
}
|
||||
|
||||
export const clearGuestIdentity = () => {
|
||||
try {
|
||||
window.localStorage.removeItem(STORAGE_KEY)
|
||||
} catch {
|
||||
// Unavailable — nothing to clear.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user