private SaaS · showcase build · dispatcher-only console
Dispatch
The internal name is StaFull; the public showcase is Dispatch.
- Built for:
- Operators running mobile-fuel fleets where the dispatcher is the source of truth and every other role’s screen is a view into that truth.
- Not built for:
- Drivers who want a separate native app. The dispatcher console is the product; everything else is a portal into it.
You had four trucks on a whiteboard. Now you have forty, you’re running two shifts, and the whiteboard is fiction by 9 AM. Dispatch replaces it with a single console where the dispatcher sees what the driver, the customer, the operations lead, and the accountant each see — from one place, in real time, with one history.
The problem
A mobile-fuel operation has six roles that each need a different view of the same fleet — dispatcher, driver, customer, operations lead, accountant, owner — and historically each role gets a separate tool, a separate login, a separate audit trail. The seams between those tools are where the work falls through. A driver completes a delivery; the dispatcher doesn’t know for ten minutes; the customer’s ETA is wrong; the invoice is late.
Dispatch is the opposite shape. One backend, one source of truth, six tailored portals on top of it. Every role sees the same data through their own role’s lens, with access scoped by tier. When a driver completes a delivery, every other portal updates in the same tick.
Decisions
Three calls that shaped the scope.
cut2025-Q4
A separate native driver app. The dispatcher console is the source of truth — if every operator needs a second app, the console failed to be that source. Drivers get a portal-flavoured PWA; it covers what they actually need on the road.
kept2025-Q4
Nine access tiers, not three. Three was elegant; nine matches how authority actually distributes in a real operation, where the difference between “view a route” and “reassign a route” matters and the difference between “reassign” and “reprice” matters more.
kept2026-Q1
A 255-input financial sandbox living inside the operations portal — not a separate spreadsheet. The owner’s questions about pricing, margin, fleet expansion, fuel-cost shocks all resolve against live data, in the same tool that runs the day’s deliveries.
System
| Layer | Implementation | Purpose |
|---|---|---|
| Frontend | Next.js 15 + React 19 | Six role-shaped portals on one app shell |
| API | tRPC + Zod | Typed contracts shared between roles |
| Auth | NextAuth + 9-tier scopes | Per-role + per-tier capability gates |
| Database | Postgres + Prisma | Fleet · routes · deliveries · invoicing |
| Maps | Mapbox GL | Live truck tracking · geofence ETAs |
| Sandbox | Custom calc engine | 255 inputs, cached, scenarios diffable |
// Every portal is a typed view over the same Postgres core.
// The dispatcher sees all of it; every other role sees a tier-
// scoped slice. Adding a portal is one Prisma query + one Zod
// shape — the rest of the system doesn't change.
export const driverDay = procedure
.use(tier({ min: 'driver:read' }))
.input(z.object({ shift: z.string() }))
.query(async ({ ctx, input }) => {
const stops = await prisma.delivery.findMany({
where: {
assignedTo: ctx.user.id, // viewAs scopes here
shift: input.shift,
status: { in: ['ready', 'enroute', 'onsite'] },
},
include: { customer: true, route: true },
orderBy: { sequence: 'asc' },
});
return stops.map(toDriverShape); // strip ops/owner fields
});
{
"shift": "2026-04-28-AM",
"stops": [
{
"id": "del_4f2a",
"seq": 3,
"customer": "Riverside Logistics",
"address": "1842 NE Marine Dr, Portland",
"eta": "08:42-07:00",
"gallons_planned": 220,
"status": "enroute"
}
],
"_role": "driver",
"_tier": "driver:read",
"_redacted": ["pricing", "margin", "owner_notes"]
}

What I’d do differently
The 9-tier access scheme should have been data, not code, from week one. v0 hard-coded the tiers in the auth middleware; v1 moved them to a config table; v2 made them editable in the operations portal. The same shape, three rewrites.
Acknowledgments
Dispatch stands on Next.js, Prisma, Mapbox, Postgres, and the operations team that lent their whiteboards as the reference design for what the console had to replace.
← Index