Skip to main
omar.nagy
Harmonia Salon POS — Arabic-first login screen with bilingual hero, mint accent, "14d" key metric.
Paid pilot·case study·published 2026-04-29

Harmonia Salon POS

A men’s luxury salon in El-Arish was running on WhatsApp messages, a notebook, and the owner’s memory. I built him a real system in 14 days: POS, booking, payroll commission settlement, full audit log, branded receipt, RTL Arabic, PWA. Two waves shipped end-to-end. The interesting engineering decision wasn’t the stack — it was choosing to build it single-tenant, against every instinct trained by SaaS culture.

14d
Calendar days end-to-end
2
Waves shipped
300
Demo orders seeded
PWA
Install + offline shell

The problem

Egyptian salon owners get two kinds of POS pitches: enterprise multi-branch SaaS at franchise prices, or Excel-with-a-receipt-printer that breaks if a stylist takes a personal day. Neither speaks Arabic right. Neither thinks about a salon’s actual cash-handling reality (cash drawer + tip math + commission split + end-of-day owner walk-out with the till).

Harmonia had three concrete pains:

  • Double-bookings on Saturdays. The owner kept appointments in WhatsApp; staff kept walk-ins on a clipboard. Two stylists, one chair, two clients, one angry phone call.
  • Commission settlement was a Sunday-morning argument. Stylists earn a percentage per service. With no system tracking who-served-whom, the weekly settlement was litigated from memory. Staff churn followed.
  • The owner couldn’t leave. No system meant his physical presence was the audit log. He hadn’t taken a full day off in 18 months.

The architectural decision: single-tenant on purpose

The default SaaS reflex is multi-tenant: build it once, sell it ten times, amortise the engineering. I went the opposite way. Harmonia is a single-tenant codebase. One repo, one Supabase project, one customer. Here’s why that was right:

  • The hard parts of multi-tenant SaaS are friction here, not value. RLS gymnastics, tenant context middleware, per-tenant rate limits, tenant-scoped feature flags — all of that overhead exists to support buyers who haven’t bought yet. Harmonia has bought. They want their POS to work, not their POS-platform to scale.
  • The real complexity moved to the domain. When I’m not spending architecture-budget on tenancy, I can spend it on Arabic RTL on touch tablets, commission rules that match the cash-handling reality of an Egyptian salon, and an audit log the owner can show the staff at end-of-month.
  • I can ship in days, not months. 14 calendar days, two waves. A multi-tenant version of the same scope is a quarter of work, minimum.
  • Single-tenant is a sales tool. The owner doesn’t share a database with strangers. His takings, his stylists’ commissions, his client list — his. That’s an honest pitch in a country where data-trust is hard-earned.

Architecture

The stack had three constraints: cheap to run (single client, thin margins), tablet-touch first (front desk + stylist devices are 10-inch Android tablets), and Arabic RTL as a first-class citizen (not an afterthought direction-flip).

Front-desk tabletPWA · RTLStylist tabletPWA · role-awareOwner phone/finances · /auditNext.js 15 appserver actionspermissions.tssingle source of truthSupabaseeu-central-1Audit logappend-onlyTelegram botend-of-day digest
Fig 1 · Harmonia data flow · permissions.ts gates every action · audit log is append-only

A few calls that mattered more than the stack choice:

  • permissions.ts as the single source of truth. Every server action and every client guard imports from one file: can(role, action, resource). The same module runs on server (authoritative) and client (UI affordances). No two-source-of-truth drift.
  • RTL is a layout primitive, not a flip-the-CSS hack. The whole UI is built with logical CSS properties (margin-inline-start, padding-inline-end) so an English fallback flips correctly without a parallel CSS file.
  • Append-only audit log, written from server actions only. Every state-changing action emits an audit row before returning. Owner sees who did what at 17:42 yesterday. Staff disputes evaporate when the log says it for them.
  • Cash-close is its own state machine. Open drawer → sales accumulate → tips registered → commissions calculated → settlement reviewed → close drawer. Each transition is a server action with a permission check.
  • Telegram end-of-day digest. Owner doesn’t want to log in to see how his Saturday went. He wants a Telegram message at 22:00 with: total revenue, stylist-by-stylist breakdown, walk-in vs booking ratio, top three services.

What I got wrong

Multi-tenant temptation in week 1

I caught myself adding a tenant_id column to every table on day three. Single-tenant or not, the muscle memory wanted it there “in case.” I deleted it the same day. If the next customer arrives, the right answer is a fork-and-rename, not a tenant column waiting silently in production for two years.

Demo data with masculine names — almost

I seeded demo orders with random Arabic names from a list. Half were female. The salon is men-only — that’s hard-coded into the brand voice (the bundle name is باقة العريس, “the groom’s package”). Caught before pilot install. Hard-coded the brand reality into the seeder.

Built the audit log as a UI feature, not a discipline

First version: each action wrote to a separate events table. Some actions wrote and some didn’t. Second version: every server action goes through recordAction(actor, verb, resource, details). There is no other way to mutate state. The audit log is now structurally complete, not optimistically complete.

The lesson

The thing that retains Harmonia isn’t the POS — it’s that the owner can leave the salon at 14:00 and the cash math still adds up at 22:00. The features I sold were “POS + booking + payroll.” The features that retain are: the audit log that resolves a dispute without me on a call, the cash-close state machine that won’t let you skip a step, and the Telegram digest.

The single-tenant call is the one I’d defend hardest. Multi-tenant SaaS is a sales-stage architecture; single-tenant is an operations-stage architecture. Harmonia is in operations now. If salon #2 calls, I’ll fork.

What’s next

On-site owner onboarding in El-Arish. The build is done; the human handover closes the pilot. Plan: link Telegram via /settings, walk through one real sale on the floor, agree on cash-close ownership, and review the first three days of audit log together. The system is ready. The next risk is human, not technical.

// stack

  • Next.js 15
  • Supabase eu-central-1
  • TypeScript
  • PWA
  • RTL

// next case study

Want this kind of build for your business? Book the Audit Sprint — $1,500 or email omar@neurascale.org.