Booking board demo with conflict detection

A salon board for six staff and fifteen days, where the conflict engine is the product and everything else is chrome around it. Time is integer minutes rather than dates, so two appointments touching at ten o'clock is legal and one minute of overlap is not.

Open the demo

Opens booking.taiphamsolutions.com in a new tab. No account, no signup, and nothing you do in it leaves your browser.

What it does

The board is a day of staff columns with appointments, breaks and time off drawn on it. All three are the same shape on purpose, so the engine has exactly one code path and cannot treat one of them inconsistently. Drag an appointment and the ghost turns red the moment it would collide, with the blocking block named and the exact minutes of overlap counted.

Three decisions carry the whole thing. Time is integer minutes since the epoch rather than a date object, so one end equalling another start is exact and snapping to the grid cannot drift. Buffers are folded into the interval before anything is compared, and then one half-open predicate answers, strict on both sides and guarded against empty spans. And the hard block is enforced inside the database transaction rather than in the preview, because between a check and a write another tab takes the slot.

Four roles decide who may do what. Staff may touch their own column only, a manager or an administrator may wave away a soft warning such as a workload cap, and nobody at all may override a hard conflict. A force button would make every other guarantee on the board conditional, so there is not one.

Open the boundary matrix and watch all 37 cases run live in your browser. They execute against the same engine the board uses, so they cannot drift from it.

Features

Six staff, eight services carrying their own prep and cleanup buffers, three rooms, 24 customers, and fifteen days of shifts and appointments around today.

The conflict engine

One half-open predicate over integer minutes with buffers already folded in, running in the drag preview and again inside the write.

The boundary matrix

All 37 boundary cases executed live in your browser against the same engine the board runs on, each with a miniature timeline explaining what it proves.

Planted edge cases

Six marked appointments on today's board: two touching at the minute, one starting as a cleanup ends, a cancelled booking under a live one, a break, half a day off, and a double booking the engine would never have made.

Roles and refusals

Staff confined to their own column, soft warnings a manager may wave away, and a hard conflict no role can override.

The flow it closes, end to end

Every step below happens inside the demo. There is no point in it where the work leaves the browser and comes back.

  1. Pick a slot

    Staff, service and time. The service carries its own prep and cleanup buffers, and from here they are part of the interval.

  2. The preview answers

    The drag ghost turns red as soon as the folded interval overlaps another, and the panel names the block and counts the minutes.

  3. The write asks again

    Committing re-reads the world inside a read-write transaction and runs the same function the preview ran, because a preview cannot enforce anything.

  4. One tab wins

    Race two tabs onto the same slot and the loser is refused for losing the transaction rather than for being slower.

  5. The refusal is recorded

    Every attempt reaches the transaction, so the log holds the refusals as well as the bookings, which are the entries worth having.

  6. Reschedule, or cancel

    Cancelling a booking that already overlaps another is allowed, because an engine must never block the action that resolves the conflict.

What is real, and what is not

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.

Actually handled

  • Touching at the minute allowed and one minute of overlap refused, at all 37 boundaries
  • The hard block enforced inside the transaction, so two tabs racing one slot produce one booking
  • A cancellation that resolves an existing overlap, rather than being refused by the rule it fixes
  • Vietnamese switching the time gutter to 24 hour, through Intl rather than through a second string table

Deliberately not real

  • The role switcher is not authentication. It demonstrates role-dependent behaviour and nothing more
  • No confirmation is sent, by email or by message, and no deposit or payment is taken
  • There is no customer-facing booking page. This is the staff side of the board
  • The data is per browser profile, so two devices are two boards

How it is built

The write-up sells harder than the application does, so it is on the page rather than in a repository nobody can open.

Stack
Solid and its router, TypeScript and Vite, with IndexedDB through idb. One static bundle and no server behind it.
Time
Integer minutes since the epoch throughout, never a date object and never a float, so an end equalling a start is exact and grid snapping cannot drift.
Boundary
The engine is a pure function over plain data. The preview calls it, the transaction calls it again after re-reading the world, and the published matrix runs the same one, so the specification cannot drift from the implementation.
Tests
90 assertions including all 37 boundary cases, and a build that enforces a 70 KB gzip budget for JavaScript and 15 KB for CSS.

Want the version of this with your rules in it?

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.

Email us