Skip to content

Real kitchen workflow: manual accept/cook/ready + toast notifications - #14

Open
devinder-dev wants to merge 8 commits into
mainfrom
feature/kitchen-workflow
Open

devinder-dev wants to merge 8 commits into
mainfrom
feature/kitchen-workflow

Conversation

@devinder-dev

Copy link
Copy Markdown
Owner

Summary

Replaces the fake, timer-based kitchen (auto-accept, auto-ready after ~3s) with a real human-driven workflow, and adds actual visible notifications on both sides instead of a silently-updating status badge.

  • New lifecycle: placed -> accepted -> cooking -> ready, with a new order.cooking event. Every transition is triggered by a kitchen staff action via the new kitchen dashboard (/kitchen), never a timer.
  • Kitchen gets real HTTP routes (GET /orders, POST /orders/:id/{accept,start-cooking,ready}) and its own outbox (mirroring order's pattern) so these actions get the same publish-after-commit durability guarantee.
  • Gateway proxies the new /api/kitchen/* routes.
  • Order and notification both sync/log the new order.cooking event.
  • Frontend: a new kitchen dashboard page, plus a real toast notification system on both the customer status page and the kitchen dashboard - every state change and every new order gets a visible notice, not just a badge update.

Real bugs found and fixed during this pass

  1. A test-only race from rewriting kitchen's index.ts as a .then() chain instead of top-level await - reverted to the original pattern, since a consumer registered late can silently lose the first published event.
  2. Gateway's proxy unconditionally set content-type: application/json even on bodyless POSTs (kitchen's new staff actions are the first bodyless POSTs in the system) - the upstream rejected them outright. Fixed to only set it when there's an actual body.
  3. A real RabbitMQ concurrency bug: order's event consumer used the default prefetch (10), letting accepted/cooking/ready for the same order process out of order under concurrency - observed live as an order's status getting permanently stuck on "cooking". Fixed with prefetch: 1 on that consumer.

Test plan

  • bun run typecheck clean across all 7 packages
  • bun run test green (76 tests) - confirmed stable across 8+ repeated runs of the e2e/kitchen suites after the prefetch fix
  • Full manual flow verified live via Docker: placed -> pending -> accept -> cooking -> ready, every consumer logging a clear confirmation, customer status syncing at every step
  • Port lockdown re-confirmed - nginx remains the only published port
  • Snyk: 0 issues

Note

Per the same rule as the prior frontend PRs: not merging this automatically - waiting for a manual click-through first.

devinder-dev and others added 8 commits June 22, 2026 22:12
…explanation

Reseeds the menu with Greek dishes (Gyros, Souvlaki, Greek Salad,
Moussaka) instead of placeholder pizza, and redesigns the frontend around
a Mediterranean color palette with food-emoji menu cards instead of a
plain list.

Order history now stores a snapshot of what was actually submitted
(items + total, captured at placement time from the same cart lines sent
to POST /api/orders) instead of just a bare order id, so the history page
shows "2x Gyros, 1x Greek Salad - $31.00" rather than a raw UUID.

The order status screen now explains how the live update actually works
("checking every 1.5s" while polling, "the kitchen service published an
event over RabbitMQ" once ready) instead of just showing a stage tracker
with no explanation of the mechanism.
…torage

A browser that already had order-history entries from before items/
totalCents were added to the stored shape would crash OrderHistoryPage:
entry.items.map(...) throws on the old {orderId, placedAt}-only shape, and
with no error boundary, that unmounts the whole app to a blank page. This
actually happened during manual testing, not hypothetically.

Fixes:
- getOrderHistory now validates each entry's actual shape and silently
  drops anything that doesn't match, instead of trusting raw JSON.
- Added a top-level React error boundary as a backstop for any other
  unexpected render error, so a future bug shows a recoverable message
  instead of a blank page.
- order-events.ts now logs a success line when it syncs an order's status
  from a kitchen event, matching every other consumer in the system
  (kitchen, notification) — it was the only one with no visible
  confirmation that it had actually run.
…tions + kitchen dashboard

In progress - real bug fixes already verified live:
- gateway proxyTo was forcing content-type: application/json on bodyless
  POST requests, which the upstream rejected outright (FST_ERR_CTP_EMPTY_JSON_BODY)
- order's order-events consumer used the default prefetch (10), allowing
  concurrent out-of-order processing of accepted/cooking/ready for the same
  order - fixed with prefetch: 1

Still to do: final Snyk scan, README/CONTEXT.md updates, push, open PR.
Updates the architecture diagram, run instructions, and frontend section
to describe the new placed -> accepted -> cooking -> ready lifecycle
driven by kitchen staff actions instead of a timer, plus the new kitchen
dashboard. Session context records the three real bugs found and fixed
while building this (a test-only consumer-registration race, a gateway
content-type bug on bodyless POSTs, and a real RabbitMQ prefetch
concurrency bug that could leave an order's status stuck).
Cooking/ready toasts never showed up, and order progress was only
visible by digging into history - because useOrderPolling and the
toast-announcing effect both lived entirely inside OrderStatusPage.
Navigate away from /orders/:id for any reason and the component
unmounts, polling stops, and any transition that happens while not
pinned to that exact page is never observed.

Lifts tracking to the app level: new OrderTrackingContext wraps App.tsx
above <Routes>, seeded from the existing order-history localStorage list,
running the same useOrderPolling hook unchanged. A new persistent
ActiveOrderBanner renders above <Routes> too, so progress is visible on
every page - menu, history, kitchen dashboard, anywhere - not just one
detail view. Toasts move to the same global home so they fire regardless
of which page is mounted.

Also bumps the polling timeout from 60s to 30 minutes - that number made
sense for the old 3-second timer-driven kitchen, not a human-paced one.
Restructured for a fast scan (badges, pitch, Mermaid sequence diagram of the
actual outbox → RabbitMQ → kitchen flow, status table) with the deeper
architecture/event-catalog/resilience material folded into collapsible
sections.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant