LinePilot is a digital queue for service desks. Customers join remotely, get a daily token number (like A-007), watch the line move live, and can leave if their plans change. Staff call the next customer, start service, mark no-shows, complete service, and browse past tokens.
Built by Yashwardhan Verma · GitHub · LinkedIn
Stack: Java 21 · Spring Boot 3 · PostgreSQL 16 · React 18 + TypeScript + Tailwind · Docker Compose
- Customers join a queue without an account and track their token from the same browser (a random ID kept in
localStorage). - Token lifecycle:
WAITING → CALLED → SERVING → COMPLETED, plusSKIPPED(no-show) andCANCELLED. - Two staff pressing Call next at the same moment always get different customers.
- Daily token numbers per queue (
A-001,A-002, …) that never repeat, even under concurrent joins. - Live queue board over Server-Sent Events (SSE).
- Wait estimate based on the last ten completed services.
- Staff sign-in with Spring Security, paginated history, and consistent JSON error responses.
flowchart LR
Browser["React app (customer + staff)"] -->|"REST + SSE"| API["Spring Boot API"]
API --> DB[("PostgreSQL")]
One frontend, one backend, one database. The interesting part is inside the database transaction:
- Call next picks the oldest waiting token with
SELECT … FOR UPDATE SKIP LOCKED, so two staff members can never claim the same token. - Join queue locks the queue's row while it increments the daily counter, so two customers never get the same number. A unique constraint backs this up.
- A partial unique index allows each staff member only one active (
CALLED/SERVING) token. - Live boards are notified only after the change has been committed.
More detail: Architecture · Database
Prerequisite: Docker with Compose.
cp .env.example .env # optional; defaults work
docker compose up --build- App: http://localhost:3000
- Staff dashboard: http://localhost:3000/staff (log in as
staff1orstaff2, passworddemo123) - API docs (Swagger UI): http://localhost:8080/swagger-ui.html
Stop with docker compose down. Add -v to also delete the database volume (needed if you ran an older version of the schema).
Start PostgreSQL, then:
cd backend && mvn spring-boot:runcd frontend && npm install && npm run dev # http://localhost:5173Configuration comes from environment variables; see .env.example and backend/src/main/resources/application.yml.
# list queues
curl http://localhost:3000/api/queues
# join queue 1
curl -i -X POST http://localhost:3000/api/queues/1/tokens \
-H "Content-Type: application/json" -d '{"customerName":"Asha"}'
# staff: call the next customer
curl -u staff1:demo123 -X POST http://localhost:3000/api/staff/queues/1/call-next
# live updates
curl -N http://localhost:3000/api/queues/1/eventsBackend integration tests use Testcontainers, so Docker must be running:
cd backend && mvn testThey cover the token lifecycle, wait-time maths, the database constraints, REST validation and authentication, first-come-first-served ordering, cancellation, and two staff calling next at the same time against a real PostgreSQL.
cd frontend && npm ci && npm run lint && npm test && npm run build- SSE instead of WebSocket: updates only flow from server to browser, and the browser's
EventSourcereconnects by itself. - Database lock instead of a Java lock:
synchronizedonly protects one JVM and isn't tied to the transaction; the database row lock is. - Flyway instead of auto-generated schema: constraints and indexes are written down and reviewable.
- HTTP Basic for staff: simple and stateless for a demo; it must run behind HTTPS outside localhost.
- No Redis, Kafka or Kubernetes: nothing in this app needs them.
- SSE subscribers are kept in memory, so only one backend instance is supported.
- Anyone with a token's tracking link can view or cancel that token; customers have no accounts.
- Queues are created by seed data; there is no admin screen.
- Wait estimates are a simple average and ignore staff count, breaks and time of day.
- No browser end-to-end tests yet.
- Browser end-to-end test of the customer and staff flows (Playwright).
- Session-cookie login for staff instead of HTTP Basic.
- Idempotency key on "join queue" so a double-submit can't create two tokens.
- Admin screen for creating and closing queues.