Sajjad Mahessar Good Merch Group Free audit
~/sajjad $ cat projects/white-label-staff-app ← cd ../projects Client work

White-label staff app, second group

App Store · Jul 2026Now v1.7 · three venues

The staff platform rebuilt and shipped for a different pub group — two venues, its own database, its own App Store listing. Proof the first one was a product rather than a one-off: 41 Swift files, 19 migrations, approved by Apple as an unlisted app. It has since grown to a third venue, a members’ club system and a web portal, so staff without an iPhone are covered too.

Type
iOS app + web
Category
Client work
Built with
Swift / SwiftUI · Supabase · PostgreSQL + RLS · CoreLocation
Project
23 of 37
ONE APP · TWO VENUES · THREE ACCESS LEVELS STAFFONE VENUE ONLY ADMINONE VENUE + MGMT GROUP ADMINSWITCHER · BOTH VENUE APIZZA CHECKLISTS VENUE BNINE DRAUGHT LINES TABS APPEAR AND DISAPPEAR PER VENUE, FROM THE DATA A THIRD PUB IS A SQL TEMPLATE · NO CODE, NO RELEASE
Tabs appear per venue from the data — a third pub needs no code change

What I built

  • 41 Swift files, around 15,000 lines, 19 numbered migrations, across two venues with separate checklists, draught lines and staff.
  • Geofenced clock-in with the live distance shown on screen, breaks recorded separately, and a rota builder with draft-then-publish so staff only see finished weeks.
  • Holiday accrued live at the UK statutory 12.07% of hours worked for hourly staff, calculated from completed timesheets so it is never stale, on a 1 April holiday year.
  • Peer-to-peer shift swaps with an admin gate — only swaps both parties have agreed reach the inbox, and approving reassigns the shifts in the database rather than leaving someone to edit the rota by hand.
  • End-of-day sheets, income by category and cash-ups, with variance shown green, amber under £5 and red at £5 or more.
  • Documents in private storage served through signed URLs valid for one hour.

The cellar ordering model

Every draught line carries a par level — what should be on site immediately after the weekly delivery. Count the cellar and the order is simply par minus counted.

Par is derived from sales rather than guessed: a week of average demand plus a safety margin of 1.65 standard deviations, which sets a 95% service level — the line runs dry no more than once in twenty weeks. Sellable pints allow around 7% loss to line cleaning and ullage.

The app stores the average and the standard deviation per line, recomputes what par should be, and flags any line where the stored figure has drifted — so the quarterly review surfaces itself. Each submitted count snapshots the par used, so recalculating later never rewrites an old count sheet.

Since launch: v1.3 to v1.7

  • A mobile web portal on the same database, so Android staff get rota, checklists, closing, stock counts, temperature logs and a nightly cash count that syncs with the app. Cash totals add themselves up, and any till discrepancy is recorded but kept out of the total.
  • A members’ club system: 189 existing members imported from a spreadsheet, random member codes that can’t be guessed, QR cards scanned at the door, a printable card export, and function room bookings.
  • Push notifications for shift reminders and new bookings, the one feature missing at the first handover.
  • Toilet-check grids with manager sign-off, weekly compliance reporting, and managers able to edit shifts and timesheets from the portal, with a mandatory reason and a full audit log on every change.

The security pass

It started with two findings. An admin PIN was still the default, and the server role had never been granted table privileges, so every server function had been failing silently for months.

PINs are now encrypted and the readable column retired. Login has lockout ladders by device and IP, plus session tokens. Every write goes through an authenticated server function instead of the app’s public key, and that includes adding staff, resetting PINs and moving people between venues. Errors now show the real cause, the lesson from an “Invalid PIN” message that was really a permissions failure.

What went wrong

  • A full outage during the build, caused by one missing clause. Row-level security policies created without an explicit role clause don’t apply to the role the app actually uses, so every query failed with a permission error — which the login screen reported to staff as “Invalid PIN”. The symptom pointed nowhere near the cause.
  • Daily checklists weren’t resetting overnight. The view was merging today’s completions and this week’s into one lookup, so a daily item ticked on Monday still read as done on Tuesday. The data was never wrong — only the display.
  • Two Supabase projects with near-identical names in the same dashboard; running SQL against the wrong one fails silently with “relation does not exist”. Documented in the handover as the single most likely mistake for whoever comes next.

Handed over honestly

  • One venue’s GPS coordinates are an estimate that was never verified on site — written down, with the on-screen distance reading given as the way to check it.
  • One account still on a placeholder PIN, and a possible duplicate staff record, both flagged rather than left to be discovered.
  • Push notifications scoped and not delivered — the only substantial feature promised and not built. CSV export, reports and photo attachments were considered and deliberately dropped.
  • Security rests on PIN-gating in the app rather than per-user policies. Stated as a deliberate trade-off for a small team, with the cost of changing it spelled out rather than presented as a finished security model.
The handover covers adding a third pub end to end: a SQL template, a collision check, and two in-app steps. No code changes, no release.
iOS app

Cleaning company operations app

Sites, staff and joining for a cleaning firm

Swift / SwiftUISupabasePostgreSQL + RLSStorage
No charge

I’ll find the money before you pay me anything.

Send me your software subscriptions and I’ll come back with what’s replaceable and what it’s worth — free, and yours to keep either way. How it works →

Contact

Want the same done to your cost base?

Open to operations, automation and technical operations roles — and to talking with anyone running a multi-site business who suspects they're paying for software they could own instead.