Tonnaro
Tonnaro moves the things ordinary hauliers will not: cranes, lowboy trailers, concrete pumps, oversized loads. We built the platform end to end — a Django/DRF backend with a configurable pricing engine, and three React surfaces on top of it: a public marketing site, a phone-style ordering app for customers, and an operations console for dispatchers. All of it in Georgian, English and Russian.
Year: 2026 · Category: logistics
Our role
Full-stack. We designed the data model, the pricing engine and the assignment logic, and built the whole front end.
The problem
Ordering specialised transport in Georgia ran on phone calls. A customer with a stranded excavator or a third-floor concrete pour would ring around operators, describe the job several times, and get quotes that were guesses — because the real cost depends on the vehicle class, the distance, the load, and, in a country that is mostly mountain, how far the truck has to climb.
The operator's side was no better. Which crane is free on Thursday, which driver is licensed for it, and whether the job was already promised to someone else were facts held in a dispatcher's head. Nothing was written down until it had already gone wrong.
The approach
The platform is built around one idea: a quote should be computed, not remembered.
An order captures the job — service, cargo, pickup and destination pinned on a map — and the backend routes it as a heavy goods vehicle, reads the elevation profile off that route, and runs it through a pricing engine the admin configures rather than one we hardcoded. The dispatcher sees a proposed price, a shortlist of vehicles whose drivers actually hold the licence, and a conflict check against everything already booked.
From there it is a state machine with an offer in the middle: the admin prices and sends, the customer accepts, and only then is the resource committed. Every move is recorded, so the question 'who changed this and when' has an answer.
Highlights
- Pricing is a configurable engine, not a rate card: zone rules are evaluated in the order the admin dragged them into, and the winning zone selects a whole rate row out of the transport-type × zone table. Per-kg weight, an elevation multiplier on the distance line, a per-day multiplier and a flat floor surcharge compose the total; the company fee and VAT are derived from it rather than added to it. Every one of those is an editable table, with a live quote calculator beside them.
- Routes are truck-aware. Every order is costed against an HGV route with a real elevation profile, because a 40-tonne load climbing 2,000 m out of the Mtkvari valley does not cost what the same distance costs on the flat.
- Describing the job in free text suggests the right service — 'my car broke down on the highway' proposes the tow truck — from a keyword engine the admin edits per service.
- Vehicle and driver assignment is checked, not trusted: licence categories must cover the vehicle, and overlapping bookings are refused rather than double-sold.
- The whole product is trilingual down to the content model — every service, category and landing section stores {en, ka, ru}, so an admin edits all three in one form.
- The public site is admin-editable: hero, services, stats, SEO tags and the accent colour of the entire application are data, not deploys.
- Orders carry their own audit trail — every status transition and every per-field admin edit is recorded and rendered back to both sides.
Architecture
- Customer app: A phone-shaped React flow — not a responsive desktop form. A multi-step wizard that suggests a service from the free-text description, reshapes its own fields per service, takes pickup and destination as map pins with multi-stop support, and shows the route, the climb and the estimated price before anything is submitted.
- Operations console: The dispatcher's surface: saved-view tabs with live counts, a polled notification bell that only sounds for the other side's actions, per-order assignment with eligibility and conflict checks, and server-side CSV exports that escape formula-leading cells rather than handing a spreadsheet an injection.
- Pricing engine: Zones, rates per transport type, weight and distance components, elevation buckets, equipment add-ons, company fee and VAT — each an editable table. The same engine serves the customer's live estimate, the admin's quote calculator and the recalculation that fires when a route resolves, so no two screens can disagree about a number.
- Routing: A server-side OpenRouteService proxy for driving-hgv directions with elevation and steepness, keyed and cached so the API key never reaches the browser and a re-render never costs a request. The client draws the polyline coloured by gradient and a smoothed elevation chart beneath it.
- Content and identity: Landing page, site settings, terms and SEO are four singletons with multilingual JSON fields. The colour theme, site name, favicon and contact details are served to the front end at boot, so re-skinning the product is an admin action.
- Backend: Eleven Django apps, 22 models, DRF generics throughout with explicit per-view permissions, JWT with refresh rotation and blacklisting, UUID-renamed uploads with signal-driven cleanup, and scoped rate limits on every sensitive endpoint.
In numbers
API routes: 128 · Django models: 22 · Front-end lines: 33k · Languages: 3
Results
- Quoting moved from a phone call to a computed number a customer sees before they commit.
- Dispatchers stopped double-booking: the conflict check refuses an overlap instead of surfacing it later.
- Adding a service, a zone or a rate became an admin action rather than a deploy.
- One platform serves three audiences — public visitors, customers and dispatchers — in three languages, from one content model.
What is used
- Backend: Django 4.2, Django REST Framework, SimpleJWT, PostgreSQL, Pillow
- Front end: React 18.3, Create React App, Ant Design 5.15, React Router 6.28, axios, dayjs, DOMPurify, dnd-kit, react-quill-new
- Maps, routing and charts: Leaflet 1.9, react-leaflet 4.2, OpenRouteService (driving-hgv, elevation + steepness), Nominatim, OpenStreetMap tiles, Recharts 2.12
- Infrastructure: Docker, nginx, DigitalOcean
The demo
The real front end with its Django backend replaced by an in-browser mock: seed data compiled into the bundle, served from memory. Every write works and survives until you reload. Sign in from the banner as a customer or as a dispatcher. Map tiles are the one thing still fetched from the network; routing, geocoding and pricing all run in the tab.