Request and dispatch ambulances in real time
Ambulance dispatch platform
Web interfaces for a healthcare logistics platform: people request an ambulance for an emergency or an event, providers offer their ambulances, and administrators run the operation.
- Role
- Frontend developer
- Period
- 2025 – present
- Tools
Tools used: Next.js, React, TypeScript, Socket.IO, TanStack Query, Zustand, Tailwind CSS
The problem
The platform has three sides: consumers requesting ambulances, providers running them, and administrators overseeing both. The product hasn't launched yet and the client is under confidentiality, so there are no screenshots or name here.
What I built
- Consumer, provider and admin interfaces in one Next.js app, each with its own layout and context.
- Real-time requests, offers, booking status updates and booking chat over Socket.IO.
- Emergency and event booking. When several providers offer ambulances, the consumer confirms or reserves as many as they asked for.
- Admin tools for accounts, bookings, ambulances, staff, facilities, pricing, transactions, and admin roles and permissions.
- Cursor-aware pagination for the admin lists, a pattern I later brought to Swyftng.
Real-time dispatch over Socket.IO
A request has to reach providers the moment it is made, and their offers have to reach the consumer just as fast.
When a signed-in consumer or provider opens the app, their interface opens one Socket.IO connection, authenticated with their token, and closes it when they leave. The connection lives in a small Zustand store, so any component can read the latest event or send one without passing the socket around. Listeners and actions are gated by role: only a consumer can ask for available ambulances, only a provider can offer one, and only consumers listen for booking status changes. Actions wait for the server's acknowledgement before the screen changes, so a provider's offer shows as sent only once the server has it. Consumer and provider can message each other about a booking in a chat sheet that follows them across the app. Admins get a live feed of booking status changes on their dashboard, with its own connection, the latest seven updates, a Live, Offline or Error indicator and a retry button, so a dropped connection is visible instead of silently stale. Before the backend's events were ready, the client was tested against a small mock Socket.IO server that answers a request and has a provider accept it five seconds later.
Cursor pagination that never looks frozen
The admin lists page through the server with cursors.
A cursor marks a position in one particular result set, so the moment a filter or the page size changes, the old cursor means nothing. The common bug is changing a filter and forgetting to reset it. I made that bug impossible to write. All of a list's filters, page size and cursor live in one object, changed only through one function that every call site must tell whether to reset the cursor. Filter and page-size changes reset it; next and previous move it. The whole object is the query key, so every field that affects the response triggers a refetch, and a page that was already fetched with the same key comes straight from cache. The first list I moved to this pattern had a search box that did nothing, because search was missing from its query key. That class of bug can't happen in a list built this way. While a page loads, the buttons disable, so a double click can't skip a page, and the page number turns into a spinner, so the table never looks stuck. If the first load fails, the table shows an error with a retry. If a later page fails, the rows already on screen stay and a small banner offers a retry, which re-runs exactly the request that failed because its cursor is already in state.
| Booking | Type | Status | Amount |
|---|---|---|---|
Query key
{
"limit": "5",
"cursor": null
}Request log
- Waiting for the first response
Newman Adesan