EMS
An all-in-one business system: employees, field work, products and stock, and accounting in one place.
- Client
- Own product
- My role
- Designer and full-stack developer
- When
- Sep 2026 – present

The problem
Small businesses that send staff to sites run on phone calls, paper and three or four separate apps: one for attendance, one for stock, one for bills. Owners cannot see who is where, what was used, or what is owed.
What I built
- Located tickets: the owner assigns a job, and the employee can only check in from within 100 metres of the site.
- A six-state job lifecycle from pending to completed, with reminders when nobody has checked in by the start time.
- Attendance, schedules and overtime for employees.
- Products, inventory and installations, with estimates and quotes the customer can review online.
- Accounting: bills, expenses, payments, a day book and customer ledgers, with reports.
- Separate workspaces for each business, each with its own public website on its own subdomain.
How it holds up
Faster
- TanStack Query keeps lists in sync without reloading the page.
- Images are served through a CDN in front of a private S3 bucket.
Safer
- Every response carries a Content Security Policy with a fresh nonce.
- API changes are only accepted from the app's own origin, which blocks cross-site requests.
- Public pages and signed-in actions are rate limited.
- Each workspace's data is separated by tenant. A business's public site has no API at all.
More reliable
- The accounting core (ledgers, day book, estimates, settlements and allocations) has unit tests on Node's built-in test runner.
- Check-ins wait in an offline queue when the phone loses signal, and send when it comes back.
Outcome
Live as a working product. The source is public on GitHub.
Stack
- Next.js
- React
- TypeScript
- MongoDB
- Auth.js
- AWS S3
- TanStack Query
- React Hook Form
- Zod
- Leaflet
- Three.js
- Tailwind CSS