Ride-sharing admin panel
One console for a ride-hailing platform: riders, driver onboarding, rides, driver earnings and commission, payments, vehicle tiers, pricing rules and support tickets.
- Role
- Full-stack engineer
- Status
- Built
- React
- TypeScript
- Vite
- Firebase (Auth, Firestore)

The problem
- Context
- A ride-hailing platform needs one console for riders, driver onboarding, rides, driver earnings and commission, payments, vehicle tiers, pricing and support tickets. Operators change fares, onboard drivers and answer tickets without an engineer for each change.
- Constraints
- The console is a client-side React app talking directly to Firebase, with no backend of its own, so all data shaping happens in the browser. The Firestore data had also evolved over time, with older and newer field and collection names side by side, and the console had to read both.
- What was at stake
- Fares, commission and what each driver is owed have to be traceable from the ride to the payment, or drivers cannot be settled correctly. A console that breaks on older records leaves operators blind to part of their own history.
What I built
Money and traceability
Earnings joined back to rides and payments
Every earning and payment row is joined in memory to its ride, rider and driver, and earning rows carry the payment they were settled by, so any figure can be traced back. The trade-off: joins run in the browser over whole collections, which suits an operations console, not a data warehouse.
Commission and payable worked out, not typed in
Gross fare, platform commission and the amount payable to each driver are computed per driver and in total from the earning records, rather than kept as separate figures that could drift.
Payments are monitoring only
Payments can be filtered and searched but never changed, and there is no automated payout path at all. Moving money stays a deliberate, separate step.
Trip time rebuilt from the status log
When a ride has no stored duration, the console derives it from the first and last entries in the ride's status history instead of showing nothing.
Data that changed shape
One layer absorbs a drifted schema
A single normalisation layer maps about twenty older field names to their current ones and falls back to older collection names when the current one is empty. Every screen reads one consistent shape, at the cost of each lookup trying two or three names.
Pricing
Fares as data per vehicle tier
Base fare, per-kilometre and per-minute rates are edited per vehicle tier by an operator, with tiers switched on or off without a deploy. Commission and surge settings are shown alongside, read-only, so the full fare picture sits in one place.
Admin access
Admins added without losing your session
New admin accounts are created from inside the panel through a separate authentication instance, so creating someone never signs out the person doing it. The first account becomes the super admin, and only a super admin can add others.
Keeps working when the data is out of reach
If the database refuses access, the console switches to a local demo dataset and says so, and every edit works the same way against either source. Useful for demos and onboarding.
Tell me what you’re building and where it’s stuck.
I’ll tell you the cleanest path forward, including if it’s “don’t build that.”
Or write tocontact@alihassan.dev
