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
+97 -3
View File
@@ -10,6 +10,10 @@
import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as BasketRouteImport } from './routes/basket'
import { Route as LoginRouteImport } from './routes/login'
import { Route as SignupRouteImport } from './routes/signup'
import { Route as OrdersIndexRouteImport } from './routes/orders/index'
import { Route as OrdersOrderIdRouteImport } from './routes/orders/$orderId'
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$'
@@ -18,6 +22,26 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const BasketRoute = BasketRouteImport.update({
id: '/basket',
path: '/basket',
getParentRoute: () => rootRouteImport,
} as any)
const LoginRoute = LoginRouteImport.update({
id: '/login',
path: '/login',
getParentRoute: () => rootRouteImport,
} as any)
const SignupRoute = SignupRouteImport.update({
id: '/signup',
path: '/signup',
getParentRoute: () => rootRouteImport,
} as any)
const OrdersIndexRoute = OrdersIndexRouteImport.update({
id: '/orders/',
path: '/orders/',
getParentRoute: () => rootRouteImport,
} as any)
const OrdersOrderIdRoute = OrdersOrderIdRouteImport.update({
id: '/orders/$orderId',
path: '/orders/$orderId',
@@ -31,31 +55,69 @@ const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/basket': typeof BasketRoute
'/login': typeof LoginRoute
'/signup': typeof SignupRoute
'/orders/$orderId': typeof OrdersOrderIdRoute
'/orders/': typeof OrdersIndexRoute
'/api/auth/$': typeof ApiAuthSplatRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/basket': typeof BasketRoute
'/login': typeof LoginRoute
'/signup': typeof SignupRoute
'/orders/$orderId': typeof OrdersOrderIdRoute
'/orders': typeof OrdersIndexRoute
'/api/auth/$': typeof ApiAuthSplatRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/basket': typeof BasketRoute
'/login': typeof LoginRoute
'/signup': typeof SignupRoute
'/orders/$orderId': typeof OrdersOrderIdRoute
'/orders/': typeof OrdersIndexRoute
'/api/auth/$': typeof ApiAuthSplatRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/orders/$orderId' | '/api/auth/$'
fullPaths:
| '/'
| '/basket'
| '/login'
| '/signup'
| '/orders/$orderId'
| '/orders/'
| '/api/auth/$'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/orders/$orderId' | '/api/auth/$'
id: '__root__' | '/' | '/orders/$orderId' | '/api/auth/$'
to:
| '/'
| '/basket'
| '/login'
| '/signup'
| '/orders/$orderId'
| '/orders'
| '/api/auth/$'
id:
| '__root__'
| '/'
| '/basket'
| '/login'
| '/signup'
| '/orders/$orderId'
| '/orders/'
| '/api/auth/$'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
BasketRoute: typeof BasketRoute
LoginRoute: typeof LoginRoute
SignupRoute: typeof SignupRoute
OrdersOrderIdRoute: typeof OrdersOrderIdRoute
OrdersIndexRoute: typeof OrdersIndexRoute
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
}
@@ -68,6 +130,34 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/basket': {
id: '/basket'
path: '/basket'
fullPath: '/basket'
preLoaderRoute: typeof BasketRouteImport
parentRoute: typeof rootRouteImport
}
'/login': {
id: '/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof LoginRouteImport
parentRoute: typeof rootRouteImport
}
'/signup': {
id: '/signup'
path: '/signup'
fullPath: '/signup'
preLoaderRoute: typeof SignupRouteImport
parentRoute: typeof rootRouteImport
}
'/orders/': {
id: '/orders/'
path: '/orders'
fullPath: '/orders/'
preLoaderRoute: typeof OrdersIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/orders/$orderId': {
id: '/orders/$orderId'
path: '/orders/$orderId'
@@ -87,7 +177,11 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
BasketRoute: BasketRoute,
LoginRoute: LoginRoute,
SignupRoute: SignupRoute,
OrdersOrderIdRoute: OrdersOrderIdRoute,
OrdersIndexRoute: OrdersIndexRoute,
ApiAuthSplatRoute: ApiAuthSplatRoute,
}
export const routeTree = rootRouteImport