Case study / prototype
KykloC
A field-service app for small trade businesses: customers, appointments, jobs, payments and repeat work in one place, in Greek and English.
- Responsibility
- Product direction and development. The project was scaffolded with Lovable and developed from there.
- Stack
- React / TanStack Start / Supabase / PostgreSQL / Vitest



01 / The problem
What needed to change.
Small trade businesses such as cleaners and electricians often run on a phone, a paper diary and memory. KykloC started from how one cleaning business, AZ Clean, books and follows up work: a customer calls, an appointment is set, the job is done, payment is recorded, and repeat work is due months later.
02 / Constraints
The boundaries of the work.
- Several businesses share one database, so one business must never see another's records.
- Work happens on site, where mobile signal is unreliable.
- The owners work in Greek, and the app also needs English.
- Someone evaluating it should be able to try it without creating an account.
03 / Technical decisions
Choices and tradeoffs.
Enforce tenant separation in PostgreSQL with row-level security policies.
A missed filter in one query would leak another business's data. A database policy applies to every query, including ones written later.
Alternative considered: Filter by business only in application code.
Queue failed writes in a persistent outbox in local storage and retry them in order when the browser reports it is back online.
On site the user may not notice a failed save until the data is needed. Keeping writes in order avoids replaying them out of sequence.
Alternative considered: Show an error and ask the user to try again.
Offer an on-device trial with sample data instead of a shared demo account.
A shared login would expose whatever visitors type to everyone else and to the real database. Local data keeps visitors separate and the backend untouched.
Alternative considered: A public demo login on the real backend.
04 / Verification
What the evidence supports.
The trial was used in a clean browser for this page, and the screenshots come from it. The database checks and unit tests were run offline at commit 095778d against an in-memory Postgres, not the production database.
Anyone can try it without an account: the trial runs in the browser with sample data that stays on the device.
Opened the public URL in a clean browser, chose English and 'Try on this device'. Every request observed went to the app's own host. Source review of continueDemo in the store confirms trial data is kept in localStorage and Supabase sync is skipped.
executed / Checked 2026-10-28
Live trial ↗Each business's records are separated by PostgreSQL row-level security, checked by a database test that switches between users.
scripts/verify-database.mjs at commit 095778d applies every migration to an in-memory Postgres (PGlite) and runs 124 assertions as different authenticated roles: all passed. This is a test environment, not the production database.
executed / Checked 2026-10-28
Saves that fail while offline are kept in a persistent outbox and retried when the connection returns.
outbox.ts and its 7 Vitest tests (reload survival, order after a failed write, full storage) at commit 095778d. Not observed against a real network outage.
source reviewed / Checked 2026-10-28
The automated test suite runs offline.
npx vitest run at commit 095778d: 110 passed, 1 failed (an address-search quota test expecting 503 that received 429).
executed / Checked 2026-10-28
05 / Limitations
Where the work stands.
- Prototype. There are no card payments or subscription billing: payments are recorded, not charged.
- One unit test currently fails (address-search quota handling).
- The finance fields shown to staff accounts are still being restricted, so that is not claimed here.
- Row-level security and the outbox are verified by automated tests, not by production use.
- The deployed commit was not determined.