Skip to main content

Le Garage

HospitalityRestaurantActive

The website for a two-venue restaurant: a menu mirrored from the till system, online ordering with orders pushed to the kitchen over Telegram, and the allergen, nutrition and addon data that the till system does not hold.

Client
Le Garage, a restaurant in El Gouna and New Cairo
My role
Architect and sole developer - site, admin, ordering, sync and infrastructure
Period
2026 - present
Le Garage

Project Overview

The menu already existed, in a till system with no API, which meant the site could either duplicate it and drift, or mirror it and add only what the source does not know. It mirrors it. Names, prices and photos come from the till and are read-only here; everything the kitchen cannot express there - allergens, nutrition, which extras belong to which dish, what to hide from guests - lives on our side and survives every sync. The rest of the project is ordering: a cart, a checkout, and an order that reaches the kitchen while the guest is still looking at the confirmation. Plus an admin panel for the staff, two venues with genuinely different rules, and a dev database where the menu is edited against a production one where real orders live.

The project in numbers

Allergens

"Unknown" and "none" are different answers

The fourteen allergens of EU 1169/2011, plus honey. A null means nobody has looked at that dish and the page says nothing; an empty list means somebody has looked and found none, and that is printed in words. Collapsing the two into one value would eventually promise safety where nothing was ever checked.

Nutrition

Marked as estimated, on purpose

Figures are derived from the dish description, not measured in a lab, so they carry an ESTIMATED flag and the page carries a disclaimer. People count diets from these numbers; passing an estimate off as a measurement is not a rounding error.

Menu sync

Never deletes anything

The menu mirrors the till system, which has no API - the catalogue arrives as inline JSON inside a script tag and is parsed. During holidays and short-menu days the kitchen removes dishes from the source and restores them a day later, so a sync that deleted would take the URLs, the photos and our own nutrition data with it. An empty response is treated as a failure, not as an empty menu.

Delivery and ordering

Two switches, never one

Whether the site takes orders and whether the kitchen drives at all are separate facts - New Cairo has no delivery. The promise of delivery appears in six places, so the substitution happens once where locations are read rather than page by page, where a list of places to remember would eventually miss one.

Kitchen notifications

Telegram, within a second of checkout

Chosen over WhatsApp because it has no message templates, no approval process, no 24-hour window and no per-message cost. Sent after the response, so the guest sees an order number immediately rather than waiting on a third-party API.

Challenges

  • Mirroring a menu from a system with no API, without the site drifting out of step with the till.
  • Surviving holidays and short-menu days, when the kitchen empties half the source and refills it the next morning.
  • Saying something useful about allergens without ever implying a guarantee the kitchen has not given.
  • Publishing nutrition figures that are estimates, without them being read as measurements.
  • Two venues where one delivers and the other does not, with the promise of delivery written into six different places.
  • Getting an order in front of the kitchen immediately, without making the guest wait on a messaging API at checkout.
  • Keeping a development database where the menu is edited and a production one where real orders live, without one overwriting the other.

Solutions

  • Built the sync as a parser over the source page's inline JSON, since the till system exposes no API, and made it strictly additive: it never deletes, it marks unavailable, and an empty response counts as a failure rather than an empty menu.
  • Gave every one of our own fields precedence over the sync, so a correction made in the admin panel is never overwritten by the next run.
  • Modelled allergens on EU 1169/2011 with a deliberate distinction between not examined and examined and clear, and kept them out of the Schema.org markup entirely - there is no honest property for an allergen list derived from a description.
  • Derived nutrition from dish descriptions, flagged it as estimated, required the disclaimer on the page, and made a hand-corrected value permanent.
  • Grouped the source's flat Extras category into addon groups attached to menu sections, because the source does not record which extra belongs to which dish.
  • Split delivery into two independent switches and resolved the wording once, where locations are loaded, so one toggle changes every page that mentions it.
  • Pushed new orders to the kitchen's Telegram chat with status buttons, and to the delivery chat when an order is ready, sending after the response so checkout never waits on the API.
  • Built the dev-to-production transfer to move only our own additions, matched by slug rather than by auto-increment id - ids differ between the two databases, and matching on them would have scattered nutrition data across the menu.
  • Kept venue settings out of that transfer, so a development flag for online ordering can never switch on real sales.
  • Gave the admin panel its own scoped Tailwind layer with a custom reset, so it could be styled properly without touching the public site's hand-written design.
  • Served link previews from a route rather than the framework's file convention, which in development bakes localhost into the shared URL.
  • Put the site behind Cloudflare with public HTML cached and the admin, account, API and checkout never cached, pairing every revalidation with an edge purge.

Key Features

  • Mirrored Menu - Names, prices and photos synced from the till system on a schedule, additively and never destructively.
  • Online Ordering - Cart, checkout, order history and reviews, per venue.
  • Telegram to the Kitchen - New orders with status buttons, and a card to the delivery chat when an order is ready.
  • Allergens - The EU 1169/2011 list, with silence where nothing was checked and a plain statement where it was.
  • Nutrition - Per-dish figures, marked as estimates, correctable by hand and then permanent.
  • Addons - The source's flat extras reorganised into groups offered against the right dishes.
  • Two Venues, Different Rules - Independent switches for ordering and for delivery, resolved in one place.
  • Admin Panel - Menu, availability, venues, orders and settings, in its own styled shell.
  • Structured Data & Link Previews - Schema.org built from the database, and preview images served from a route.

Project Info

Launch Date:
September 16, 2026
Status:
Active

Technologies

Frontend

Next.js 16 (App Router)ReactTypeScriptHand-written CSSTailwind (admin only)

Backend

Node.jsPrismaMySQLNextAuthZodServer Actions

Messaging

Telegram Bot API

Caching

Cloudflare edge cacheNext.js revalidation paired with edge purge

Security

Role-based adminUncached checkout and account routes

Hosting & Deployment

DockerDocker ComposenginxCloudflareAWS S3

DevOps & CI/CD

Scheduled menu syncHealth checksDev-to-production data transfer by slug

Monitoring & Analytics

Sync run history
Available for new projects

Need something like this?

Tell me what you are building and where it is stuck. I will tell you plainly whether it is work I should be doing.