One order, one record, two service windows.
F&B Order Ecosystem is an internal order system for a Johor Bahru food-delivery business that cooks against two daily batches, 1PM and 8PM. The owners take the order and enter it once. The kitchen board and a Telegram job message both read from that same record, and every step carries the name of the person who moved it.
- Client
- Johor Bahru food-delivery business (name withheld)
- Sector
- Food and beverage — delivery and pickup, two daily order windows
- Our scope
- Product design, application build, kitchen board, Telegram job layer, reporting, deployment and runbook
- Stack
- React · Vite · Supabase Postgres · Vercel functions and crons · Telegram Bot API · WhatsApp deep links · installable web app
- Running cost
- Free-tier hosting and database, no server to maintain
- Scale
- Five people, two service windows a day
The problem
Orders arrive by phone and WhatsApp, and the kitchen cooks against two batches a day. With five people and a printed menu, the same four failures repeat, and each one costs a dish or a customer:
- An order cooked twice, or not at all. Two people read the same message and both start it; or everyone assumes someone else did.
- The 8PM batch started while 1PM is still open. Nothing on paper tells the kitchen what is genuinely outstanding at 7:50.
- "Done" with no record of who did it. When a customer calls back, the trail is a chat thread and a memory.
- The month assembled from memory. Revenue, average order value and what actually sold, re-added by hand at month end.
What we built
One entry, once. The order is entered against the menu by category, with per-item options that carry their own price adjustment, quantity, a paid or unpaid marker, delivery fee, discount and notes. A repeated phone number pulls that customer's last name, address and pickup-or-delivery choice, so a regular is two taps rather than a conversation. The order number is issued by the database as F2D-YYYYMMDD-00001 and resets daily. A test mode walks the entire flow, writes nothing, and redirects every customer message to the developer with a clear prefix.
The kitchen board. A separate install on the kitchen phones, behind a staff PIN with a twelve-hour shift session. Cards are grouped New, Prep, Done, refresh every eight seconds, and show how long each has been waiting; past twenty-five minutes a card turns late-coloured. New jobs chime. A press on a card records who pressed it.
The Telegram layer. This is the part that made the system survive a busy night. Every new order posts a job message into the kitchen group with a Start prep button. The first press claims the job and names the person; a second person pressing is told it is already taken, which is how an order stops being cooked twice. The message is then edited in place, never reposted, so the group does not fill with buttons for work that is finished. A sweep every thirty minutes renews any job that has sat for two hours — deleting the stale message and posting a fresh one that says how long it has waited — so nothing is quietly forgotten between windows.
Who moved it. Every state change goes through a single database function that locks the order row, skips no-op changes, and writes a history row with the person's name. "Who marked this done, and when" is a query, not a recollection. The same function serves the app and the Telegram buttons, so the two surfaces can never disagree.
Closing, and the month. The day view gives orders, revenue, discounts and counts by state. The month view gives revenue, average order value, a daily chart, peak hours and top items, with CSV and PDF export for the accountant.
Staying up. A daily snapshot of orders, lines, menu and settings into a backups table with ninety days of retention; a daily keepalive so a free-tier database never pauses for inactivity; and a runbook for the four failures that actually happen — paused database, silent bot, dead webhook, stale message. The whole system — manager app, kitchen board and six serverless functions — is about 7,400 lines of hand-written source.
Decisions that mattered
- Telegram, not another app. The kitchen already had Telegram on their phones. A job message with one button is a tool they use on the first night; a native app for five people would have been a worse product at ten times the cost.
- One claim, one owner. Claiming a job is exclusive and named. Two people starting the same order is a cost with no upside, and the fix is a rule in the message rather than training.
- Edit the message, never post a new one. Job messages are rewritten in place as the state changes, so a finished order never keeps a live Start prep button. A scheduled sweep replaces the ones that have gone quiet.
- Reminders from outside the platform. Free-tier scheduled functions run once a day, which is not the cadence a kitchen needs. The thirty-minute tick lives in a separate scheduled workflow, with a monthly commit so the schedule is never disabled for inactivity.
- Cheap to run is a design goal. Managed database, static front end, serverless functions, no paid messaging API. There is no server for the owners to patch, and the monthly bill is the domain.
What it does not do
Customers do not order through it: there is no public storefront, and orders still arrive by phone and WhatsApp. Access is a PIN gate rather than per-person accounts — adequate for five trusted people on an internal tool, and it would have to become real authentication before the system faced customers. There is no receipt printer, no recipe-level stock deduction, and no accounting integration; the CSV and PDF exports are the handover point. Single outlet, single currency, no multi-branch consolidation.
The test we build to: at 7:50 the kitchen can see exactly what is still open, and who has it.
Something similar on your side?
If your team coordinates by phone and chat, and the record of what happened lives in a thread, that is a small system with a fast payback.