Board and drag to assign
Unassigned orders, the day's routes, and a drop target that disables itself carrying the exact limit it would break as its tooltip.
A dispatch desk and a driver's screen over one browser database. Sixty orders across three days, eight drivers, and a rulebook that refuses an over-capacity route at the drop target and refuses it again inside the write.
Opens logistics.taiphamsolutions.com in a new tab. No account, no signup, and nothing you do in it leaves your browser.
Dispatch gets the board: unassigned orders on one side, the day's routes on the other, drag to assign, and a stop sequence that can be rearranged. A driver gets one route and the stops on it, with pickup, departure, arrival, delivery and failure as the only moves available. An administrator gets drivers, vehicles and the outbox, and a viewer gets all of it and can change none of it.
The rules live in one directory of pure TypeScript with no DOM, no database and no framework in it, and a test walks that directory to keep it that way. The order state machine, the capacity rulebook and the permission table are functions, so the check that disables a drop target is the same one that runs inside the write. A component that forgets to ask cannot introduce a capacity or a permission bug.
A refusal is a value rather than an exception. A guard returns the reason it refused and the reason carries a translation key, so a rejection renders as a sentence in the reader's language instead of a generic error. Statuses, priorities and failure codes are stored as machine codes and translated when they are drawn, which is why switching to Vietnamese also rewrites the historical audit entries.
Drag a bulky order onto a bike route. The drop target disables itself and names the limit it would break, and the attempt still reaches the audit timeline.
60 orders across three days, 8 drivers, 8 vehicles and 18 routes, with one van off the road and one driver off duty, seeded on first open.
Unassigned orders, the day's routes, and a drop target that disables itself carrying the exact limit it would break as its tooltip.
Weight, volume and stop count per vehicle, gated in the interface and asserted again in the repository, so neither half is the only guard.
Pickup, departure, arrival, then a signature drawn on a canvas, a photo, or a failure code with a note. Each step is a legal transition or nothing.
Every mutation appends its events and its outbox entries in the same transaction, so the log cannot drift from the data it describes.
Every step below happens inside the demo. There is no point in it where the work leaves the browser and comes back.
Sixty across three days, each with a pickup and a drop window, a weight, a volume and a priority.
Orders are dragged onto a driver's route, and the vehicle's weight, volume and stop limits decide what the drop target will take.
Dispatch hands the route over. From here the driver is the only role that can move a stop, and only on their own route.
Pickup, depart, arrive, deliver. A jump the state machine does not allow is refused with a translated reason rather than quietly ignored.
A signature on the canvas or a photo closes a stop. So does a failure, with a code and a note, because a day on which nothing failed is not a day.
Entities, audit events and outbox entries are written in one transaction that advances the Lamport counter inside itself, so two tabs cannot mint the same clock value.
There is no server behind any of this, which decides both columns. The left one is what that constraint made worth building properly. The right one is what it rules out.
The write-up sells harder than the application does, so it is on the page rather than in a repository nobody can open.
A demo exists to prove something on the offer list rather than to be impressive on its own. These are the ones this one argues for.
Each one is a different kind of problem, and all of them are in the Demos menu at the top of the page.
This one was built to a constraint nobody would choose. Yours would have a server, real accounts, and the parts of the domain that are actually hard. Tell us what the software has to do and we will tell you what it takes.
Bring the messy version of the problem. That is the conversation we enjoy most.