All case studies
Logistics

MorokotGas

Replaced phone-and-spreadsheet LPG dispatch with an end-to-end ordering and delivery system.

Engagement
Client engagement
Industry
Energy & Distribution
Year
2026
Our responsibility
System designNestJS APIFlutter customer & driver appsAdmin dashboard

The challenge

An LPG cylinder distributor took orders by phone, wrote them on paper, and called drivers to dispatch. Stock lived in a spreadsheet that was accurate until the first delivery of the day. Nobody could answer 'where is my cylinder' without three phone calls, and the owner had no view of the day until it was over.

Our approach

The constraint was the drivers, not the technology. Any system they would not use would fail regardless of how good the backend was. We designed the driver app first with large targets, minimal typing and support for affordable Android phones with patchy signal. Then we built the API around it. OTP login removed passwords and the biggest source of support calls.

What it had to achieve

  • Take an order without a phone call, and dispatch it without a second one
  • Give the owner live visibility into orders, drivers and stock
  • Let drivers work from a phone with no training beyond one shift
  • Remove the spreadsheet as the system of record

Architecture

The decisions that made the rest possible.

01

OTP-only authentication

Drivers and customers sign in with a phone number and a code. No password resets, no forgotten credentials, no shared logins written on a wall.

02

Queue-backed side effects

Notifications, receipt generation and stock recalculation run through Bull on Redis. A slow SMS provider delays a message, never an order confirmation.

03

Shared design-tokens package

Colors, spacing and type live in one package consumed by the Flutter apps and the Next.js dashboard, so three surfaces stay visually consistent without three sets of decisions.

04

Order state machine with explicit transitions

An order moves through defined states with recorded timestamps. 'Where is my cylinder' becomes a database read instead of a phone call.

Built with
FlutterNext.jsNestJSPostgreSQLRedisPrisma

What we delivered

  • Customer Flutter app: ordering, reorder, delivery status, order history
  • Driver Flutter app: assigned runs, status updates, proof of delivery
  • Next.js admin dashboard: live orders, driver assignment, stock, reporting
  • NestJS API with OTP authentication and role-scoped permissions
  • Bull/Redis queues for notifications and background processing
  • Shared design-tokens package across web and mobile

Security & reliability

  • Role-scoped permissions separating customer, driver and administrator access
  • Every order state change written with actor and timestamp for dispute resolution
  • Stock adjustments recorded as movements rather than overwrites

Outcomes

What changed once it was live.

  • Orders arrive as records instead of phone notes, so dispatch no longer depends on memory
  • Delivery status is visible to the customer without contacting the office
  • Stock reflects deliveries as they happen instead of at end of day
  • The owner sees the day's operation live rather than reconstructing it after

What we would do differently

Every project teaches something. Publishing it is how you tell whether a team is reflecting or just selling.

  • Designing the driver app before the API was the right call. Every architectural decision downstream had a concrete, low-connectivity user to answer to.
  • OTP login eliminated a whole category of support burden. For non-office users, passwords are a product risk.

Let's find out if this is worth building.

A 45-minute discovery call, free. You describe the problem, we tell you honestly what it takes, what it costs, and whether you should build it at all.