Work / Web app for field-service crews
RouteTimecard
A timecard, route and job-tracking web app for lawn-care and field-service crews. In development, in early access.
Open routetimecard.comHow it works
- Role
- Product design, development and hosting
- Status
- In development. Early access.
- Platform
- Web, phone first. Installs to the home screen.
- Stack
- Node 20, MariaDB, a service worker, Leaflet and OpenStreetMap, IIS

The problem
People who work a route are often paid by commission, by day rate or by a mix of both. They find out what a week was worth when the paycheck arrives, and by then it is hard to check.
RouteTimecard gives a crew one place to log the day stop by stop and see estimated pay as it builds up. It gives the office one place to build routes, move stops and review the day afterwards.
What I built
- Route days. Start, end and reopen a day. Mark each stop done or skipped with a reason, or rain out the whole day.
- Pay estimates. Per stop, per day and per pay period, for hourly, salary, day rate, commission and mixed pay. A recorded paycheck can be compared with the estimate.
- Routes for the office. A route builder, recurring schedules and moving stops between days.
- Customers. CSV import with a preview before anything is saved, and merging of duplicates.
- Maps. Customers as pins with lasso select, and directions handed to the phone’s maps app.
- On the stop. “On my way” texts that open the phone’s own messaging app, photos, notes and a tank-mix calculator.
- For owners. Team roles, quotes, an activity log and exports.
It works in a phone browser and installs to the home screen.
How it works
The phone holds enough to finish a day without signal. The server decides who may see what, and every row belongs to exactly one company.
Phone
Installed web appA service worker caches the app shell. The last route day is kept for 7 days and unsent edits for 30Server
Node 20, no frameworkIts own router, body parser and static file handler. Two npm dependencies in totalAccess
Sessions and rolesPasswords hashed with scrypt, session IDs stored only as hashes, a CSRF token on every change. Owner, admin and tech rolesData
MariaDB33 tables. Every row carries its company ID, and nothing is authorized by row ID alonePhotosResized in the browser, stored on disk, served only to signed-in members of the same companyOutside
Geocoding and mapsUS Census geocoder for addresses, OpenStreetMap tiles through Leaflet
The same storage interface has an in-memory version, which is what the tests and local development run on.
Hard problems and the decisions made
Pay rules change, history must not
Problem If last month’s work is recalculated with this month’s rule, every old estimate quietly changes.
Decision Pay rules have effective dates and are copied onto the work when it is recorded.
Result A later rule change never rewrites an earlier estimate.
Skipped stops, partial jobs and missing prices
Problem Guessing in any of these cases produces a number nobody can trust.
Decision A skipped stop adds nothing to commission by default. A partial job counts only an amount someone entered. A missing price is flagged, not guessed.
Result Every figure is labelled an estimate. Overtime and tax are out of scope.
Working a route with no signal
Problem A day’s edits have to survive a dead zone, a closed tab and a phone with the wrong time.
Decision Cache the day and queue the edits on the phone, scoped to one company and one user, with expiry dates. Tolerate five minutes of clock skew and distrust timestamps further ahead than that.
Result A closed day is locked for the tech. An admin can still correct it, and the correction is logged.
Importing a customer list safely
Problem A CSV from another system can be large, messy and imported twice.
Decision Show a dry-run preview first, commit in batches of 250 rows, and make retries repeat-safe with stable customer numbers.
Result Importing a new file never removes customers that are missing from it.
Closed pay periods
Problem Once a period is paid, a late import or edit would change numbers that were already settled.
Decision A closed period rejects imports and tech edits.
Deleting data for real
Problem Rows can be deleted on request. Backups cannot be edited afterwards.
Decision A written retention policy: deletion removes rows in dependency order, and backups expire on a schedule.
The stack, and what each part is for
- Node 20, no framework
- The HTTP layer is in the repository. The only npm dependencies are the database driver and one API library.
- MariaDB
- The system of record, behind a storage interface that also has in-memory and file versions.
- Service worker
- Makes the app installable and keeps the route day usable offline.
- Leaflet, OpenStreetMap, US Census geocoder
- Maps and address lookup.
- Phone handoffs
- “On my way” opens the phone’s own messaging app with the text filled in. The app does not send texts itself.
- Scanning paper job sheets
- Optional and off by default, with a daily cap.
- Tests
- 121 test files, run directly with Node against fictional records.
Screens




Screens from a fictional demo company. Every name, address, phone number and amount in them is made up.
Timeline and what is next
- Oct 25, 2026First commit
- Nov 1, 2026Quotes and recurring schedules
- Nov 2, 2026Wide-screen layout for the office
- Nov 3, 2026Techs can reorder stops while working the day
- NextFinishing the app for its opening
- NextiPhone and Android apps
- NextRoute optimisation and invoices are on the roadmap
Status and links
In development. Early access.