feat(restaurant): customer-friendly order status labels

Order status came through to the customer as raw operational
strings — 'paid', 'accepted', 'ready'. These are fine for the
operator's KDS but unfriendly for the customer waiting on their
food.

types/restaurant.ts:
  + FRIENDLY_ORDER_STATUS map (status → label)
      pending     → 'Awaiting payment'
      paid        → 'Order received'
      accepted    → 'Cooking'
      ready       → 'Ready for pickup'
      completed   → 'Served'
      canceled    → 'Canceled'
      refunded    → 'Refunded'
  + friendlyOrderStatus(status) helper. Unknown statuses (future
    kitchen-workflow values from aiolabs/restaurant#4 — e.g.
    'preparing', 'plating', 'in_service') fall through to a
    titlecased version of the raw key so the build stays green
    and the surface stays readable.

views/OrderStatusPage.vue:
  - Status Badge uses friendlyOrderStatus().
  - Alert sections now have one per status with appropriate copy:
      paid       → 'Order received / Payment confirmed — the
                    kitchen will start preparing it shortly.'
      accepted   → 'Cooking / Your food is being made.'
      ready      → 'Ready for pickup / Pick up at the counter.'
      completed  → 'Served / Enjoy! Thanks for ordering.'

views/CheckoutPage.vue: Phase 2 status badge uses
friendlyOrderStatus() so the checkout's live per-restaurant
status pill matches the language on the order page.

Deeper kitchen workflow (prep stations, courses, ETA, per-station
status) stays on aiolabs/restaurant#4 — this commit is the cheap
win that ships with the existing data model unchanged.
This commit is contained in:
Padreug 2026-05-11 18:15:26 +02:00
commit 15545c9b5e
3 changed files with 57 additions and 6 deletions

View file

@ -233,6 +233,34 @@ export const KNOWN_ORDER_STATUSES = [
export type KnownOrderStatus = (typeof KNOWN_ORDER_STATUSES)[number] export type KnownOrderStatus = (typeof KNOWN_ORDER_STATUSES)[number]
export type OrderStatus = string export type OrderStatus = string
/**
* Customer-facing labels for order statuses. The extension's raw
* status names are operational ('paid' / 'accepted' / 'ready') but
* customers prefer human-friendly framing ('Order received' /
* 'Cooking' / 'Ready for pickup').
*
* Future statuses from aiolabs/restaurant#4 (kitchen workflow)
* 'preparing', 'plating', 'at_pass', 'in_service', etc can land
* here as they arrive. Unknown values fall through to the raw
* status string titlecased.
*/
export const FRIENDLY_ORDER_STATUS: Record<string, string> = {
pending: 'Awaiting payment',
paid: 'Order received',
accepted: 'Cooking',
ready: 'Ready for pickup',
completed: 'Served',
canceled: 'Canceled',
refunded: 'Refunded',
}
export function friendlyOrderStatus(status: OrderStatus): string {
if (status in FRIENDLY_ORDER_STATUS) return FRIENDLY_ORDER_STATUS[status]
// Unknown status — titlecase the raw key as a graceful fallback.
if (!status) return ''
return status.charAt(0).toUpperCase() + status.slice(1).replace(/_/g, ' ')
}
export interface Order { export interface Order {
id: string id: string
restaurant_id: string restaurant_id: string

View file

@ -46,6 +46,7 @@ import { Separator } from '@/components/ui/separator'
import OrderInvoiceCard from '../components/OrderInvoiceCard.vue' import OrderInvoiceCard from '../components/OrderInvoiceCard.vue'
import { useCartStore } from '../stores/cart' import { useCartStore } from '../stores/cart'
import { useCheckout, type PlacedOrder } from '../composables/useCheckout' import { useCheckout, type PlacedOrder } from '../composables/useCheckout'
import { friendlyOrderStatus } from '../types/restaurant'
import { import {
injectService, injectService,
tryInjectService, tryInjectService,
@ -383,7 +384,7 @@ function buildOrderInvoice(p: PlacedOrder) {
:variant="isPaid(placed.order.id) ? 'default' : 'outline'" :variant="isPaid(placed.order.id) ? 'default' : 'outline'"
class="text-xs" class="text-xs"
> >
{{ statusOf(placed.order.id) }} {{ friendlyOrderStatus(statusOf(placed.order.id)) }}
</Badge> </Badge>
</div> </div>
<OrderInvoiceCard <OrderInvoiceCard

View file

@ -31,6 +31,7 @@ import OrderInvoiceCard from '../components/OrderInvoiceCard.vue'
import { useOrder } from '../composables/useOrder' import { useOrder } from '../composables/useOrder'
import { import {
KNOWN_ORDER_STATUSES, KNOWN_ORDER_STATUSES,
friendlyOrderStatus,
type OrderInvoice, type OrderInvoice,
} from '../types/restaurant' } from '../types/restaurant'
@ -150,7 +151,7 @@ const timeline = computed(() => {
</p> </p>
</div> </div>
<Badge :variant="statusStyle" class="text-sm"> <Badge :variant="statusStyle" class="text-sm">
{{ order.status }} {{ friendlyOrderStatus(order.status) }}
</Badge> </Badge>
</header> </header>
@ -161,13 +162,25 @@ const timeline = computed(() => {
/> />
<Alert <Alert
v-else-if="['paid', 'accepted'].includes(order.status)" v-else-if="order.status === 'paid'"
class="mb-4 border-emerald-500/40" class="mb-4 border-emerald-500/40"
> >
<CheckCircle2 class="h-4 w-4 text-emerald-500" /> <CheckCircle2 class="h-4 w-4 text-emerald-500" />
<AlertTitle>Payment received</AlertTitle> <AlertTitle>Order received</AlertTitle>
<AlertDescription> <AlertDescription>
The kitchen is on it. Payment confirmed the kitchen will start preparing it
shortly.
</AlertDescription>
</Alert>
<Alert
v-else-if="order.status === 'accepted'"
class="mb-4 border-emerald-500/40"
>
<CheckCircle2 class="h-4 w-4 text-emerald-500" />
<AlertTitle>Cooking</AlertTitle>
<AlertDescription>
Your food is being made.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -176,12 +189,21 @@ const timeline = computed(() => {
class="mb-4 border-amber-500/40" class="mb-4 border-amber-500/40"
> >
<Clock class="h-4 w-4 text-amber-500" /> <Clock class="h-4 w-4 text-amber-500" />
<AlertTitle>Ready</AlertTitle> <AlertTitle>Ready for pickup</AlertTitle>
<AlertDescription> <AlertDescription>
Pick up at the counter. Pick up at the counter.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
<Alert
v-else-if="order.status === 'completed'"
class="mb-4 border-emerald-500/40"
>
<CheckCircle2 class="h-4 w-4 text-emerald-500" />
<AlertTitle>Served</AlertTitle>
<AlertDescription>Enjoy! Thanks for ordering.</AlertDescription>
</Alert>
<Card class="mb-4"> <Card class="mb-4">
<CardHeader> <CardHeader>
<CardTitle class="text-base">Items</CardTitle> <CardTitle class="text-base">Items</CardTitle>