The problem
When I took over the store, its operating infrastructure was the one a large part of UK independent retail still runs on. The rota was built in Excel every Sunday and re-emailed every time something changed. Stock was reordered from memory, with no sell-through data behind it. Date checks were done by whoever remembered, with no handover, so work was repeated or missed. Holidays were requested by text and approved by word of mouth, with no audit trail. Takings went on paper, then into a spreadsheet.
None of this was unusual, and that is the point: it was slow, undocumented, and dependent on one person's memory. The off-the-shelf answers did not fit either. Generic HR platforms do not understand stock, generic stock systems do not understand people, and enterprise tools are priced at £200 to £500 a month for a five-person store, none of which produce a demand forecast. So I set out to build the tool that did fit.
Requirements gathering
I gathered requirements over the first two weeks the way a business actually reveals them: by observation. I kept a running log of every point in a shift where I reached for a spreadsheet, sent a text, or held something in my head that should have been in a system. Each entry became a candidate requirement.
I mapped three stakeholders. Myself as manager (reporting, approvals, oversight), the four sales assistants (shift tasks and self-service HR), and the store itself (compliance, accountability, an audit trail). I grouped what I found into eight areas: rota, sales, stock, waste, team HR, performance, finance and compliance.
The insight that shaped everything came out of that log. Almost none of the pain was missing data. The data existed; it was just in the wrong format, in the wrong place, or locked in someone's memory. That gave me one design principle to build against: anything currently living in someone's head or someone's phone goes into the system.
Key decisions, and why
A build like this is really a chain of decisions, and each one had to be defensible.
Why not off-the-shelf. The systems that exist for retail at this scale are generic HR platforms that do not understand stock, generic stock systems that do not understand people, or enterprise tools at £200 to £500 a month. None produce a demand forecast, and none tie stock intelligence to the rota to the performance of the person doing the ordering. The requirement that ruled them out was simple: one team, one system, each person seeing only what they should.
Why React and Supabase. A spreadsheet with macros could handle a rota. It could not handle multi-user access with role-based data isolation, real-time shared state, a forecasting engine and a full audit trail. The moment the requirement became "the whole team uses this at once, and each person sees only their own data", the technical bar was set. Supabase gave me a real PostgreSQL database with row-level security on a free tier, so total infrastructure cost is £0.
Why Holt's, not Holt-Winters. This is the decision I would most want a reviewer to see. I had 22 weeks of weekly sales history. That is enough to estimate a level and a trend, but not a seasonal component: annual seasonality needs at least 52 observations, ideally two full cycles. Fitting Holt-Winters to 22 weeks would have modelled the noise of those particular weeks and dressed it up as a pattern. So I used Holt's linear (double) exponential smoothing, matched to the data I actually had rather than the model that sounds more impressive. It is deterministic, and I verified it by hand: the engine reproduces a specific next-week projection of £8,127.74 that I recalculated manually.
Security designed first, not bolted on. The access model came before any feature. Every table has row-level security, and the rules live in the database, not the interface, so a colleague cannot see another's salary or performance figures even with developer tools open. Hiding a button is not security; the database enforces every rule.
Delivery
I built it iteratively, one painful problem at a time: define the smallest feature that fixes it, build it, verify it, deploy it, use it in a real shift, then move to the next. There were no sprints or standups, because I am the team, but the discipline that replaced process was a firm rule: nothing ships without a successful production build and a browser test. That rule caught runtime errors a compiler never would.
The foundation, authentication, rota, takings and dashboard, went live in the first week. Team features followed: holiday and availability approvals, staff purchases, and an alerts bell that surfaces anything needing attention so the manager is not checking every screen. Then came stock intelligence on a real sales dataset, ABC analysis, reorder detection, overstock and dead-stock flags, and the Holt's forecasting engine. The platform now runs weekly forecasts across 1,201 products, built on a 22-week sales history of roughly £201,767 and 46,396 units, on 20 database tables and around 3,500 lines of code, at £0 running cost, used by five people every day.
User acceptance testing
Testing was never a phase at the end. The app was in real shifts from day seven, and the team tested every feature as it shipped. Their feedback drove real changes:
- "Availability requests disappear after I accept them" led to an accepted and rejected log with a per-colleague filter.
- "Cassie's name in the till system is Marcus" led to a name-linking layer so performance data maps to the right person.
- "The rota only shows contracted hours in history, not which days we worked" led to a day-by-day past-weeks grid.
- "The calendar shows 270 instead of 27" was traced to a rendering bug and fixed.
- "When I log a purchase I can only see my own name" opened the colleague dropdown for till-side logging.
The point is not any single fix; it is that the people using the system shaped it, and there is a record of how. When a colleague accidentally deleted a record, confirmation dialogs went in. When one of them suggested a squirrel that chased the login-screen illustrations, it got built, broke on deploy, glitched, and was cut once the team said "go back to the nuts". Not every idea survives contact with implementation, and knowing which to keep is part of the job.
When it went wrong
A portfolio that only shows the wins is weak evidence, so here is the failure I am most willing to talk about, because finding it was the analytical work.
The stock module was reporting £168,182 in sales across exactly 1,000 products. The source data held £201,767 across more. The figure that gave it away was that round "1,000": real product counts are never that tidy. I summed the first 1,000 rows of the source file by hand, and it reproduced the app's wrong total exactly, which told me this was not a calculation error, it was truncation. The root cause: Supabase returns a maximum of 1,000 rows per request by default, and the app was silently taking the first thousand and stopping.
The headline totals were the least of it. Because the data was cut off, products were missing from the reorder alerts, the overstock flags and the dead-stock lists. The tool had been quietly giving incomplete ordering advice, and nobody would have known unless they looked. I fixed it with paginated fetching that pages through the full dataset until it runs out, and the reorder list was whole again.
The lesson stuck, and it is a general one: a suspiciously round number in an output is a red flag, and you verify totals against a source you can check independently. That instinct, distrust the clean number, reconcile to source, is the same one that keeps a risk or finance analyst out of trouble.
Outcomes
The platform changed how the store actually runs, not just what it stores.
| Before | After |
|---|---|
| Rota built in Excel, 30 to 45 minutes a week, re-sent on every change | Built in-app in 10 to 15 minutes, live for the whole team instantly |
| Stock reordered on instinct | Reorder list generated from 22 weeks of sell-through, worst-first |
| Date checks with no handover | Bay-by-bay progress log, "resume from" always visible |
| Holidays requested by text | Request-and-approve flow with a full audit trail |
| Individual performance invisible | Weekly per-person figures, privately visible to each colleague |
Underneath the operational wins is a weekly analytical output the store orders against: an ABC breakdown that isolates the roughly 356 products (31% of the range) driving 80% of revenue, a reorder list flagging anything below two weeks of cover, and a per-product Holt's forecast. The forecasting and stock intelligence are not a demo; they are used to make real ordering decisions every week.
What I'd do differently, and where it's going
A few things I would do differently, because saying so is part of the discipline. I would build paginated data fetching from day one rather than adding it after the truncation bug forced it. I would make browser-testing the built app a formal step in every change, since the errors that hurt most, a blank page from a missing import and the calendar bug, passed the build tool and would have been caught instantly in a browser. And I would put experimental features behind flags so something like the squirrel could be toggled off without a deploy.
Where it is going next: per-product forecast tuning with prediction intervals rather than single point estimates; an upgrade to Holt-Winters once about 52 weeks of history exist, around January 2027, when seasonality can finally be estimated honestly; multi-tenancy with hard data isolation so the platform could serve more than one store; and turning the reorder list into an exportable order sheet ready for the supplier.