PersonalShipped2024–present

TaskFlow

Lead Developer

An offline-first task-dispatch PWA for hospitality teams. Realtime updates via Socket.IO, BullMQ scheduler for recurring tasks, IndexedDB mutation queue so the app keeps working when the Wi-Fi drops. Multi-tenant SaaS, live and free at tasks.jimrarras.com.

Results

automated test files
178
separate areas, from tasks to rooms to notifications
12
states a task moves through, from created to approved
8
TaskFlow

Stack

  • Fastify
  • PostgreSQL + Drizzle
  • Redis + BullMQ
  • Socket.IO
  • React 19
  • TanStack Query
  • IndexedDB

The situation

In a hotel, a kitchen or an event venue, the work gets handed out on scraps of paper and shouted down a corridor. Staff are moving all shift, and the Wi-Fi is good at reception and absent in the basement. I built TaskFlow on my own to survive exactly that: to hand out work to shifts that are not sitting at a computer, and not to break when the signal goes. It is live and free for any team that wants it.

It works with no signal

The app does not stop when the network drops. Whatever somebody does out of signal is queued on their own device and replays as soon as the connection returns, in the order it happened.

The queue is not the hard part. The hard part is what you do when the server says no. A task somebody else already claimed is not going to be accepted however many times the device tries again, so it does not try again.

How it works
  • The queue: every POST, PATCH and DELETE made offline is written to an IndexedDB store with a timestamp and replayed in timestamp order.
  • What blocks what: actions against the same entity depend on each other, start before submit, so one stuck action holds up only its own task rather than the whole queue.
  • What gets retried: 408 and 429 clear on their own and are replayed. Every other 4xx is a permanent no: it is dropped from the queue and surfaced as a notice naming the action that was refused.
  • What exists offline: the service worker keeps the app shell and the API responses under NetworkFirst with a five second timeout, so a half-open connection does not hang the screen.
  • Updates that ask first: a new version does not activate by itself. It asks, because a deploy mid-shift otherwise changes the app under the user’s hands.

Every task knows whose it is

A task is either given to a named person or dropped into a pool for whoever is free to claim. From there it moves through fixed steps, and nobody can walk it somewhere it is not allowed to go.

The admin has one exception, and it exists for a reason: a shift ends, somebody calls in sick, and the task they had started must not stay stuck to them.

How it works
  • Eight states: pending, in_pool, assigned, claimed, in_progress, submitted, approved, redo.
  • One transition table: every permitted move lives in a shared package and is checked by the same function on the server and the client, so there is no second version of the rule.
  • The admin exception: a worker never walks a task backwards. An admin can park it back at the start of the board, with submitted excluded and approved terminal.
  • Realtime: events are emitted from the API routes and invalidate the matching cache on the client, so the screen updates without anybody refreshing it.

Nobody should have to remember the repeats

Room turnover, the shift handover checklist and the weekly jobs do not need typing in by hand every day. They go in once as a template and appear on their own when they are due.

How it works
  • Every minute: a BullMQ worker checks the schedules every 60 seconds and creates whichever tasks have come due.
  • Three rhythms: daily, weekly and monthly recurrence, all from task templates.
  • Whose job it is: generation runs from the server-side queue rather than from somebody’s browser, so it does not depend on who happens to have the app open.

Many teams, one system

Two hotels on the same deployment do not see each other’s work. And inside each team, the supervisor and the worker do not get the same app: one creates and approves, the other takes and delivers.

How it works
  • Data separation: every entity carries a tenantId and every query is scoped to it.
  • Three roles: superadmin, admin and worker, with the interface split into separate areas per role and access enforced in the routing.
  • Two ways in: a password for admins, a four to six digit PIN for workers. Access tokens live 15 minutes, refresh tokens 7 days, stored as a hash and rotated on every use.
  • Realtime rooms: Socket.IO puts each connection into rooms per team, per user, per pool and per task, so an update reaches only the people it concerns.
  • How it stands up: Fastify 5 with PostgreSQL and Drizzle, Redis for the queue and the realtime layer, all in one monorepo with two apps and three shared packages.
  • Komvos Connect: another multi-tenant SaaS, but for appointment booking rather than task dispatch
  • Zosimades collection-app: another offline-first mobile workflow, but a native Android scanner instead of a PWA
Get in touch

Display preferences

Text

Text size
Line spacing
Letter spacing

Color

Contrast

Motion and images

Reading

Reading aid
No changes