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,31 @@
|
||||
export const formatDuration = (totalSeconds: number) => {
|
||||
const totalMinutes = Math.max(1, Math.ceil(totalSeconds / 60))
|
||||
const hours = Math.floor(totalMinutes / 60)
|
||||
const minutes = totalMinutes % 60
|
||||
|
||||
if (hours === 0) return `${minutes} min`
|
||||
if (minutes === 0) return `${hours} hr`
|
||||
return `${hours} hr ${minutes} min`
|
||||
}
|
||||
|
||||
const startOfDay = (date: Date) =>
|
||||
new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
|
||||
export const formatEtaClock = (etaSeconds: number, now = new Date()) => {
|
||||
const target = new Date(now.getTime() + etaSeconds * 1000)
|
||||
const time = target.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||
const dayDiff = Math.round(
|
||||
(startOfDay(target).getTime() - startOfDay(now).getTime()) / 86_400_000,
|
||||
)
|
||||
|
||||
if (dayDiff === 0) return `today, ${time}`
|
||||
if (dayDiff === 1) return `tomorrow, ${time}`
|
||||
return `${target.toLocaleDateString([], { weekday: 'long' })}, ${time}`
|
||||
}
|
||||
|
||||
const ABSOLUTE_TIME_THRESHOLD_SECONDS = 3 * 60 * 60
|
||||
|
||||
export const formatEtaMessage = (etaSeconds: number, now = new Date()) =>
|
||||
etaSeconds > ABSOLUTE_TIME_THRESHOLD_SECONDS
|
||||
? `Ready ${formatEtaClock(etaSeconds, now)}`
|
||||
: `Ready in about ${formatDuration(etaSeconds)}`
|
||||
Reference in New Issue
Block a user