Teknisyen — Field App That Works Offline
Machine rooms and basements have no signal, and that is where a technician is standing when he wants to record a visit.
Role
The last client and the one I knew least going in. I set the offline rules and the shape of a record; the sync queue itself and the push wiring I listed up front as things I was learning, and the build was driven with AI coding agents.
Stack

Overview
Machine rooms and basements have no signal, and that is where a technician is standing when he wants to record a visit. So the app writes to the phone first. The screen says saved right away, the record sits in a queue, and the queue syncs when a connection comes back. Each record carries an id made on the phone, so resending after a dropped connection updates the same visit instead of creating a second one. Around that sit the building list by district, the open faults nobody has claimed yet, and the service form, which reproduces the paper form the firm has always used and comes out as a PDF the customer can be handed.
What it does
- IVisits saved to the phone first; the queue syncs when there is a connection
- IIThis month at a glance: visits done, buildings waiting, days left
- IIIFaults claimed by name, so two technicians don't drive to the same building
- IVThe paper service form on the phone, exported as a single A4 PDF
- VManager, phone and elevator record for every building, readable without signal
Screens








Technical notes
- IThe home screen was built twice. The first version was a strip of five days, today in the middle, tap a day to see its visits. It looked right and it answered a question nobody asks, because the backend has no per day schedule and the technicians don't plan by day; they work through a district. Two days later the strip became a circular list of districts: tap one, see its buildings and how many are still waiting this month.
- IIA failed sync was once a verdict. Anything that wasn't offline or a server error marked the service form as uploaded, so a failed render, a full disk or an expired token could mark a form done that never left the phone, and nothing in the app could ask for it again. Now only a genuine client error that isn't about auth may mark a form done. Everything else stays in the queue.
- IIIThe printed form carried the company's real address and both real phone numbers as string literals, and a test file asserted the exact strings. Moving the template alone would have left the numbers in git. Three environment variables now supply them, the template takes the letterhead as a parameter, and the tests use placeholders that are obviously fake.
What I learned
Offline first means the old data is the default until you delete it on purpose. Preparing the screenshots for this page against a database of invented buildings, the app opened on real buildings and real managers from its cache, before a single request had gone out. I had to wipe the simulator. It is the right behaviour for a basement and the wrong one for a screenshot, and I hadn't thought about the second case.