Case study · Client project · Sep 2026
Repair By Engineer: booking, tracking and a workshop console
A live web system for an earbud and headphone repair shop in Dhaka. Customers book and track repairs without an account; the shop's staff run every job, payment and printed document from one console.
- Role
- Freelance developer: requirements with the shop, data model, build, deployment and data migration
- Stack
- Next.js 16 (App Router), React, Auth.js v5, PostgreSQL (postgres.js), Tailwind CSS, Vercel
- Links
- Live site · code is private because it belongs to the client
Problem
The shop's first version stored its entire dataset as one JSON value. Every save rewrote everything, so two people saving at once could overwrite each other, and there was no sign-in. Customers had no online way to book a repair or check its progress.
Who uses it
- Customers book a repair and track it with their phone number and reference. No account needed.
- Engineers take in devices, update the status of their own jobs, record payment on delivery and print memos. Their console shows their assigned jobs and those customers.
- Admins approve bookings, assign engineers, manage customers and staff logins, and see income, costs and profit.
Architecture
Key decisions
- The mobile number is the customer's identity. Bangladeshi numbers are normalised to one 11-digit form and a unique index enforces it in the database, not in application code. A returning customer is recognised under any name.
- Each job keeps its own contact snapshot, so a one-off delivery address never overwrites the saved customer.
- Job numbers such as REP-2026-00001 come from an atomic counter (a single upsert inside the job-creation transaction), so two staff creating jobs at once never collide.
- The database sets itself up. Tables and columns are created idempotently on first request, and one-time migrations are claimed atomically. They imported the shop's existing customers, repairs and payments from the old JSON row and left it untouched as a backup. The shop only has to set one environment variable.
- Money never disappears. Amounts are stored as NUMERIC, customers are soft-deleted, and permanent deletion refuses to cascade into payment history. Invoices print amounts in words using lakh and crore.
- Phone-first console. Engineers update jobs from their phones, so tables become cards, navigation moves to the bottom, and tap targets are at least 44px.
Security
- Every private route checks the session and role on the server; hiding a button in the UI is never the only protection.
- Engineers' job edits and status changes are checked against their assignment. The same response is returned whether a job is missing or belongs to someone else.
- Public tracking needs both the phone number and the reference, gives one uniform "not found" reply, so a miss does not reveal which part was wrong. Attempts are rate-limited per phone and per IP.
- The first-run setup page works only while no account exists, and closes itself afterwards.
- I reviewed access control route by route and scoped every customer query to the engineer's assigned jobs; booking requests are admin-only.
Result
The system is live at repair-by-engineer-inky.vercel.app with public booking and tracking, and the shop's data was migrated into the new schema. It was built in 32 commits between 10 and 20 September 2026.
What I would do next
- Move rate limiting from memory to the database, because serverless instances do not share memory, and tighten the remaining staff-permission edges (customer lookup, payments, request approval).
- Add route-by-role integration tests and CI before adding more features.
- Send WhatsApp or SMS updates when a job is ready, with a per-job tracking link.
What I learned
- Authorization belongs in every query, not in the interface.
- I used an AI coding assistant (Claude Code) as a pair programmer. Reviewing its output line by line, especially access control, stayed my job.