§ ShopBot — Telegram commerce demo
The live dashboard — SSE, design & the e2e bar
The dashboard is the demo's showpiece: a dark "control-room" board where a business owner watches the shop run. Open inventory.oaoisme.top and you see the catalogue with live stock, a KPI strip, an order feed, and a ticker — all of which update the instant something happens, with no page refresh.
How "live" works — Server-Sent Events
The backend exposes GET /api/stream, a Server-Sent Events (SSE) endpoint.
The browser opens it once (new EventSource('/api/stream')) and keeps it open;
the server pushes named events down that one connection as they occur:
| Event | Fired when | Dashboard reaction |
|---|---|---|
order.created |
a new order is placed | prepend a card to the feed, bump KPIs, ticker |
order.paid |
a payment clears (webhook) | flip the card to PAID, raise revenue, ticker |
stock.changed |
stock decrements or a restock | update the product's stock pill, flash it |
reset |
the demo is reset | reload everything |
SSE was chosen over WebSockets because the data flow is one-way (server →
browser) and SSE is far simpler: it's just an HTTP response that never ends,
auto-reconnects in the browser, and needs no extra protocol. On the server it's
a reply.hijack() and a Set of open responses that get written to on each
event. A 25-second comment ping keeps proxies from closing the idle connection.
A subtle but important detail for nginx: SSE responses set X-Accel-Buffering: no so nginx streams them through instead of buffering — otherwise events would
arrive in batches, not live.
The design language — "The Counter"
The look is documented in /root/apps/shopbot/design.md. In one line: a warm
espresso-black point-of-sale terminal with a single scarce magenta accent and
tabular-mono numerics. The deliberate moves:
- One accent colour (magenta) used only on the brand, primary action, focus ring, and the flash when live data lands — never decoratively. Status uses a separate, disciplined green/amber/red triad, so the brand never collides with "paid / awaiting / out of stock".
- Flat depth — 1px hairline borders and surface layering do the work that drop-shadows would; the only soft light is the data-update flash.
- Tabular mono numbers everywhere they can change (prices, stock, totals) so figures tick like a ticker instead of jittering as their width changes.
- Signature structures: a marquee ticker tape of recent events, and an order feed laid out as a "StoryStream" timeline on a dashed rule.
The direction was chosen to be visibly different from every other site on the box — no blueprint-navy (that's the monitor), no teal (the forge), no editorial paper (the landing/notes). A stranger should believe a different studio made it.
Reduced motion is a real feature, not a checkbox
Under prefers-reduced-motion: reduce the ticker stops scrolling and becomes a
static newest-first list, the data flashes are suppressed (values update
instantly), and the LIVE pulse stops animating. The data is always readable
immediately; motion is only ever an enhancement.
The receipt page
paid.html is where a customer lands after paying. It polls the order until the
webhook confirms payment, then shows a receipt with a green seal. A clean detail:
a 404 (order genuinely not found) stops immediately with a clear error rather
than retrying — so a bad reference doesn't spam the console with eight failed
requests, and the user gets an honest answer fast.
The testing bar
Per the box's house rule, the dashboard ships with a Playwright e2e pass
(inventory/e2e.mjs) that drives the live HTTPS URL in a real headless
browser and asserts every feature: catalogue + stock pills, search (match +
empty state), restock, the full order.created → order.paid live pipeline
(driven by posting a signed webhook), the LOW/OUT pills, the receipt page (paid
/ unknown / missing ref), plus desktop and a 390px mobile viewport and
reduced-motion — with a zero-tolerance bar for console / page / request
errors. The suite proves the SSE pipeline truly updates the board, not just that
the page loads. Result at build time: 26/26 green.