Skip to content
All projects
MobileIn active development

ShiftPay

Clock in, see what you are owed, and get a PDF at the end of the month.

Overview

Personal shift & pay tracker PWA — clock in/out with real timestamps, hourly-rate history with auto-calculated pay, mark-paid balance, and PDF reports. Next.js 16 · Drizzle · Turso · 6-digit PIN.

The problem

Tracking shifts and pay by hand puts hours in one place, rates in another and payments in a third, which makes the one number that matters — what you are actually owed — a manual reconciliation every fortnight. The first version solved that with a server, a hosted database and an account, and in doing so put one person's pay log on someone else's infrastructure to answer a question that never left their phone.

The approach

Rebuilt it local-first. The records are a single JSON document in IndexedDB rather than a database: a shift row serialises to 290 bytes, so ten years of one person's working life is under a megabyte, and every screen already loaded a whole range and computed over it. That document is the same type as the backup file, so an export is a copy and a restore is an assignment. Three guarantees SQLite used to enforce — one shift per date, one rate per effective-from, at most one open shift — became plain code, each held down by a test that fails when the check is removed. The cost is stated plainly in the app: with nobody else holding the data there is no recovery link, so a forgotten lock means reset and restore from a backup.

Outcome

  • No database to provision and no migrations — install and run is the whole setup
  • Shipped through twelve phases, from PIN auth to Drive sync with per-row merge
  • All money and time maths lives in a pure domain layer with no database or React imports, in integer cents and minutes, unit-tested
  • CI runs lint, unit tests, a production build and the end-to-end suite on every push, with no environment at all
  • The one optional variable is read per request rather than inlined at build, so a deployment is reconfigured without rebuilding it

What it does

  • Clock in, clock out, correct it later

    The real instants are stamped and never change. Effective times stay editable, and a difference between the two renders an “adjusted” marker rather than hiding the correction.

  • Rate history that does not rewrite the past

    A shift is priced by the rate with the latest effective-from on or before its own date, so changing your rate adds history instead of silently re-pricing days you were already paid for.

  • Payments are receipts, not calculations

    Marking paid stores a snapshot of what those days came to — the only stored money in the app. A later rate correction re-prices the log while the receipt still says what was actually handed over.

  • A lock, not a login

    A passkey where the device can make one, a PBKDF2-hashed PIN otherwise, and a lockout ladder that runs before the check so a lock already counting down cannot be guessed through.

  • Sync to your own Drive

    A second copy goes to the user's own Google Drive, merged per row under two rules: no row either side knows about is dropped, and nothing either side deleted comes back.

  • An install gate on iOS, not a banner

    Safari deletes script-writable storage after seven days of not opening a site, which for the only record of someone's hours is catastrophic, silent and on a timer. Home-screen apps are exempt, so in an iOS tab the app shows the install steps and nothing else.

Ask about ShiftPay

Want something like this, or curious how a piece of it works? Send a note — it reaches me tagged with this project, so I have the context before I reply.

Replies within 24h on business days.