karan.dabare
selected work

03/2026 – present · Opezey.com

Snooker Cafe Management System

Cross-platform venue management — a React Native app for staff, a Next.js platform for owners, kept in real-time sync over a Socket.io event layer.

React NativeNext.jsTypeScriptRedux ToolkitSocket.ioNode.js
overview

A venue management system for a snooker café business: staff run tables, orders, and kitchen tickets from a React Native app on the floor, while owners monitor bookings and daily operations from a Next.js admin platform.

Both clients subscribe to the same Socket.io event layer, so a booking made at the counter appears on the owner's timeline the moment it exists — no refresh, no polling.

the problem

The system this replaced modeled reservations as static slots. A slot grid can answer "is 7pm free?" — it cannot answer "which tables are running long right now, and what does that do to the next hour?" In a venue where sessions extend and walk-ins happen, a grid is wrong within minutes of opening.

The naive fix — every client polling the server for fresh state — makes staleness a tuning knob: poll fast and the server pays for it, poll slow and the staff app and the admin panel show different floors. Neither side should ever be looking at a different truth than the other.

architecture

How it fits together

A single Node.js backend owns all booking and table state. Clients mutate state through REST endpoints; every accepted mutation is broadcast as a typed Socket.io event to all subscribed clients — the staff app and the admin platform hold local Redux stores that are written to by events, not by refetching.

The timeline view renders straight from that store: tables as rows, reservations and live sessions as ranges on a time axis. Because the store is event-fed, the timeline is as current as the last broadcast, on every screen, at the same moment.

The admin platform ships as a Progressive Web App, so owner and staff devices get install-free access without an app-store release cycle.

Node.js backendsingle owner · booking + table stateSocket.io event layeraccepted mutations broadcast as typed eventsevents ↓events ↓Staff appReact Native · the floorRedux store · event-fedAdmin platformNext.js · PWARedux store · event-fedREST ↑REST ↑the decision: push, don't pollREST mutationsSolo build — entire system authored.
The Socket.io event layer is the spine: REST for mutations, events for truth distribution.
what I owned

Solo build — greenfield. Nothing here was inherited; every layer below is mine to defend.

authored
  • System design: the decision to make the event layer the source of UI truth, and the REST-mutate / event-distribute split.
  • The React Native staff app — table operations, orders, kitchen flow.
  • The Next.js admin platform and its live reservation timeline.
  • The Node.js backend, the Socket.io event contracts, and the PWA build.
  • Deployment: hosting and domain configuration, and post-launch debugging with the client.
what it does

Reservations

  • Live timeline-based reservation view — table availability and booking details at a glance, replacing static slots.
  • Real-time propagation: bookings, extensions, and cancellations appear on all connected clients as they happen.

Floor operations

  • Table lifecycle management from the staff app.
  • Order and kitchen flows tied to the table a session belongs to.

Access

  • Separate surfaces for separate jobs: a phone-first app for staff, a dashboard for owners.
  • PWA build for install-free access across devices.
stack, and why
  • React Nativestaff work standing up, on phones — the floor tool had to be an app, not a responsive page
  • Next.jsthe admin platform and the PWA build share one codebase
  • TypeScriptthe event contracts cross three runtimes; types are what keep them honest
  • Redux Toolkitone client store that Socket.io events write into — the UI never asks twice
  • Socket.iothe headline decision: push state changes instead of letting every client poll
  • Node.jsbaseline choice

rationale is given only where the choice wasn't the obvious one.

hard problems

Where it got difficult

Two clients, one truth

  1. symptom

    With each client fetching on its own schedule, the staff app and the admin panel could legitimately disagree about the same table — not through a bug, but by design: polling makes staleness a per-client property.

  2. investigation

    The options were to poll faster — cost scales with clients × frequency, and the gap never actually closes — or to invert the flow entirely and have the server announce changes.

  3. root cause

    Booking state lived in one place but was consumed via N independent snapshots. The architecture, not the code, was what made the views diverge.

  4. fix

    A Socket.io event layer as the only path by which state reaches a screen. Clients mutate over REST; the server validates, commits, and broadcasts; every connected store applies the same event. The timeline is a projection of the event stream, not a cached query.

  5. tradeoff

    Persistent connections are a liability you carry: reconnection needs a resync path (snapshot fetch, then resume events), and the server now owns delivery, not just data. Accepted — sync correctness was the product.

A timeline is a harder UI than a grid

  1. symptom

    The slot grid it replaced was trivially easy to render — and wrong. A timeline has to show reservations and in-progress sessions as ranges against real clock time, on a phone screen, while the data underneath it moves.

  2. investigation

    The hard cases are visual, not computational: a session running past its booking, a reservation starting mid-view, updates landing while a staff member is mid-scroll.

  3. root cause

    Time is continuous and slots are discrete — the entire reason the grid failed is the same reason the timeline is harder to draw.

  4. fix

    Render ranges from the event-fed store against a time axis, and let updated events restyle in place rather than re-layout, so the view stays stable under live changes.

  5. tradeoff

    More rendering logic living on the client, and a component that needs real care on small screens. The grid was cheaper; it just wasn't true.

interface
TODO: asset — admin reservation timeline, live state
TODO: asset — staff app, table and order view
what it taught me
  • Push-based sync doesn't delete the hard part, it relocates it: the complexity you remove from polling reappears in the reconnect path. Budget for it on day one.
  • On a solo project the scarce resource is attention. Every boring infrastructure choice — managed hosting, one database — was a deliberate purchase of attention for the timeline, which is the part users judge.
what's next
  • Version the Socket.io event payloads — right now a client and server must deploy in step, and that coupling will bite as the surface grows.
  • Persist an event log server-side for replay and audit, which would also simplify the reconnect/resync path.
  • Load-test the socket layer against the venue's peak concurrency before scaling to more locations, rather than after.