Skip to content

Audit firm time tracking

Project and staff time tracking for an audit firm · Built from scratch, solo

Updated

Before

Clients, projects and hours lived in one place where everyone in the firm could see and change everything.

After

87

permission boxes proven against the database before every deploy; the 30 that did nothing were hidden.

How it works
  1. 1Person:Owner

    Builds roles with checkboxes per area (clients, projects, time, reports) for view, create, edit and delete.

  2. 2Person:Staff member

    Logs hours against a client, project and task, or books time off.

    Software:Web app

    Reminds anyone whose timesheet is still empty before the deadline.

  3. 3Person:Project admin

    Approves or rejects the week's timesheet for their own project team.

  4. 4Software:Database

    Shows each person only what their role allows: staff see their own hours, project admins their team, owners every client.

  5. 5Person:Owner

    Reads hours by client, project and person on the dashboards and reports.

Always on

  • Before, everyone in the firm could open every client and everyone's hours. Now the database refuses anything a role does not allow, even if a screen asks for it.
  • Before each push, every role is tested against an isolated copy of the database.

Problem

An audit firm ran its clients, projects and staff hours in a tool where every person could open every client and everyone's time.

Diagnosis

Access control fails quietly when it lives in the screen, so every rule had to live in the database, proven by tests.

Options I weighed

  1. Each role sees different data. Where do the access rules live?

    Chosen

    In the database itself, tested for every role before each deploy

    The screen cannot leak what the database refuses. Cost: a test run on an isolated copy before each push.

    • Rejected

      Only in the screens

      A person could see or change data the screen meant to hide.

  2. A record is no longer needed. Delete it or keep it?

    Chosen

    Archive and anonymise it

    Timesheet history stays intact.

    • Rejected

      Delete them

      Deleting cascades away timesheet history.

Solution

A single-page web app on managed Postgres. Every access rule lives in the database and is proven by an automated role matrix before each deploy.

App
  • React
  • Vite
  • TanStack Router
  • TanStack Query
  • shadcn/ui
  • Chart.js
Data and security
  • Supabase
  • PostgreSQL row-level security
  • Edge Functions (Deno)
Hosting
  • Cloudflare
Quality
  • GitHub Actions
  • Role-matrix tests
  • Vitest
  • Property-based tests
  • Accessibility tests

Figures: Automated role check, 2026-09-25