Case study · Workforce systems
Retail Shift Scheduler: a week's roster in minutes, offline
Weekly retail rosters, leave tracking and payroll export that used to eat a manager's afternoon — done in minutes, offline in a browser, on one of the strongest test suites in my portfolio.
01The problem
Rostering a retail store is deceptively hard. Every week a manager has to cover opening and closing under staffing rules, respect who's on leave, spread duties fairly across departments, and land on hours that reconcile to payroll — usually in a sprawling spreadsheet that no one else can safely touch. It's slow, it's error-prone, and a single mistake shows up in someone's pay. The task is repetitive enough to automate but detailed enough that generic scheduling apps never quite fit how a real store operates.
02Constraints
- Offline and self-contained. Store machines can't be assumed to have reliable internet or permission to install software. It had to run in a browser, no server.
- Real coverage rules. Managers, full-timers and part-timers have different rules; leave types (AL/MC/NG) have to be first-class, not notes in a cell.
- Payroll-grade correctness. The numbers feed pay, so the data model has to be trustworthy and every change traceable.
- Safe to evolve. Real users mean saved data in the wild — updates can't corrupt last month's roster.
03Approach
The heart of the app is a DOM-free scheduling core: pure JavaScript that takes staff, rules, leave and duties and produces a roster, with no dependency on the browser or a network. Around it, persistence is layered — IndexedDB and localStorage for working state, the File System Access API for exporting, and HTML5 Canvas to render printable PDF sheets. Leave (AL/MC/NG), per-department duties on a timetable, month sheets and an audit trail are all built in, with desktop and mobile builds. Because the data outlives any one version, the saved format is explicitly versioned and the app migrates older files forward on load. All of it is defended by roughly 135 automated tests — around 42 unit tests plus a Playwright end-to-end suite.
04Architecture
Inputs
Staff, coverage rules, leave (AL/MC/NG), duties.
DOM-free core
Pure-JS roster engine. No network, no DOM — fully testable.
Versioned store
IndexedDB + localStorage. v5 schema, forward migrations, audit trail.
Output
Payroll export + Canvas PDF/month sheets.
05One hard decision & the trade-off
Build the scheduling logic as a DOM-free, network-free core and invest heavily in a versioned data model with migrations and ~135 tests — before adding features.
It would have been faster to wire logic straight into the UI and skip the migration machinery. But this software feeds payroll and lives on real users' machines, so a bug or a broken upgrade isn't a cosmetic issue — it's someone's pay and a month of lost data. Separating the core made it exhaustively testable, and versioning the schema meant every future change could ship without stranding existing rosters. The cost is upfront engineering that a throwaway tool wouldn't justify; the payoff is that I can keep improving it in production without fear, which for business software is the whole game.
06Outcome
The scheduler is in real, ongoing store use. A weekly roster that used to be an afternoon of spreadsheet wrangling is now minutes of guided work, with leave and coverage handled automatically, payroll exported directly, and an audit trail behind every change. It's the piece I point to when the question is whether "offline, single-file, vanilla JS" can also mean rigorous — a versioned data model and one of my deepest test suites say it can.
Tech
- Vanilla JS (DOM-free core)
- HTML5 Canvas
- IndexedDB
- localStorage
- File System Access API
- Playwright E2E
07FAQ
How does a scheduling app work fully offline?
The core scheduling logic is plain JavaScript with no DOM or network dependency, and state persists locally through IndexedDB, localStorage and the File System Access API. There's no server in the loop, so a store manager can build next week's roster on a laptop with no internet and export it straight to payroll.
What stops an update from corrupting existing rosters?
The saved data carries a schema version — currently v5 — and the app runs forward migrations on load, so older files are upgraded in place rather than breaking. That versioned data model, plus around 135 automated tests including a Playwright end-to-end suite, is what makes it safe to keep shipping changes to software people rely on for payroll.
How much time does it actually save?
Building a weekly retail roster by hand — balancing coverage rules, leave, duties and payroll hours across managers, full-timers and part-timers — is hours of spreadsheet work. The app applies the coverage rules automatically and exports payroll directly, turning that into minutes, with an audit trail so changes are traceable.
Need an internal tool that runs without IT approval?
Let's talk. Rostering, inventory, inspections — if it's an operations headache, it can usually become a tool your team just opens and uses.
Get in touch