Case study

Clox

A time tracking product I built for small trades crews. Workers clock in from their phones, managers review and approve hours, and approved time can be exported to payroll. I run the company and built the product end to end.

Client
Clox (my own company)
My role
Founder: product, design, front end and marketing
Year
2026 to present
Scale
481 TypeScript files, 19 app routes, 55 API routes, 365 tests
Screens
The live app and the crew app, signed in to the demo organization
The Clox Today screen: the current time on a paper background, a project field, a note field and a large Clock in button.
The main clock-in screen. The current time, project, note and Clock in action are all visible without scrolling.
  • Works offlinePunches are stored on the device and synced when the connection returns
  • 365 tests39 test suites covering overtime, pay periods, timezones, geofences, imports and payroll exports
  • 19 app routes, 55 API routesPlus Stripe billing, four payroll integrations and four scheduled jobs

About the project

Clox is a time tracking product for small trades crews. Workers clock in from their phones, managers review hours and approve the week, and approved time can be exported to payroll. I founded the company and built the product end to end.

The main technical constraint is that workers are often using the app on a job site with a weak connection. At the same time, the hours being recorded feed into payroll. The interface therefore has to handle offline use without losing or duplicating punches.

When the phone is offline, punches are stored in an IndexedDB queue. Each one gets a UUID on the device, which is also used as the server-side idempotency key, so a punch that replays after a successful sync is recognized and ignored. When the connection returns, the queue uses the same server action as the normal online path. Clock in, clock out, breaks and project changes all use the same system.

Failed punches are handled according to the type of failure. Temporary errors are retried, invalid punches can be discarded, and anything that needs a human decision is held for review. Unknown responses are held for review as well, so no punch is dropped silently.

As with Keyset, the business logic lives in plain modules, separate from the framework. The 365 tests cover overtime, pay periods, timezone boundaries, geofences, CSV imports and payroll exports, with most of the logic tested without a browser.

Section by section

The application, screen by screen. These are the live app, signed in to the demo organization.

  1. The Clox Today screen, not clocked in: the time, a project picker, a note field and a Clock in button, with the week total below.
    Clocking inThe primary action is the Clock in button. Workers can optionally select a project and add a note before starting.
  2. The Clox Today screen while clocked in: the same layout inverted to dark, a running timer and a coral Clock out button.
    The same screen, on shiftAfter clocking in, the interface switches to a dark state and the primary action becomes Clock out. The timer counts from the server-recorded punch time, so reopening the page does not reset it.
  3. The Clox timesheet: a week of days with hours, and a list of shifts with projects, break deductions and pay.
    The weekThe timesheet shows daily hours and individual shifts, including projects, break deductions and pay. Forgotten clock-outs are flagged so they can be corrected before approval.
  4. The Clox schedule board: eight employees down the side, seven days across the top, and shift chips showing times and project names.
    Building the schedulePeople are listed down the side and days run across the top. Shifts can be dragged between people and days, clicked to edit, or added to an empty cell. Dragging is built on pointer events, so touch behaves the same as a mouse, and the same moves work from the keyboard. Moving a shift preserves its duration across overnight and daylight saving changes.
  5. The Clox Team page: nobody on shift, 338 hours this week, and a pending approvals panel with time off and shift groups.
    The approvals queueThe team screen shows who is working, weekly hours and items waiting for approval. Shifts can be approved individually or in bulk.
  6. The Clox map view: 44 located punches for one week, a day-grouped list on the left and a map of New York with clustered markers.
    Where the punches happenedThe map shows where clock-ins happened during the week. Clox records location at the moment of the punch and does not track workers between punches. Punches outside an assigned worksite are refused.
  7. The Clox reports page: 338 hours 49 minutes, $10,905 estimated payroll, and per-employee bars with overtime in a second color.
    ReportsReports show total hours, estimated payroll and overtime by employee. Hours can also be grouped by project, client and task, then exported to ADP, Paychex, Gusto or QuickBooks.
  8. The Clox import page: a column specification table with example values, a CSV template download and a paste box.
    Getting last year inThe import tool accepts pasted spreadsheet data or CSV files. Existing people and projects are matched during the import, and nothing is written until the user confirms the preview.
  9. The Clox audit log: 140 entries with kiosk events and shift edits showing their before and after values.
    The audit logThe audit log records changes to rates, approvals, roles, worksites and shifts, including the values before and after each change. It can be filtered and exported to CSV.

On a phone

The crew app is built with Expo and React Native and shares the same Supabase project as the web app.

  • The Clox app clock screen, not clocked in, with a large Clock in button and the line All punches synced below it.
    The clock screen when a worker is not on shift. The status below the button confirms that all offline punches have synced.
  • The same screen with a project and a task chosen before clocking in.
    Workers can select a project and task before clocking in. Organizations can make this required.
  • The Clox app schedule board on a phone, with the instruction to hold a shift then drag it onto a day to move it.
    The schedule board also works on the phone. Hold a shift and drag it onto another day to move it.
  • The Clox app map of punches for a month, showing 124 located punches over New York.
    Punch locations for the month. Clox records location at each punch and does not track workers between punches.
  • The Clox app approvals screen listing 45 timesheets, each with edit, reject and approve.
    Timesheets waiting for review, with edit, reject and approve actions.

What went into it

Design

  • The palette is paper, ink and one clay accent. Color is reserved for primary actions and overtime.
  • The app switches to a dark state while a worker is on shift, making the current state easy to recognize at a glance.
  • Navigation is based on eleven capabilities, with defaults by role and overrides for individual people.
  • Kiosk mode supports shared tablets on site. Workers use a PIN to identify themselves and a manager PIN to exit.
  • Hours use tabular numerals so columns line up, and screens identify the organization's timezone.

Build

  • Next.js 16 App Router, React 19 and TypeScript in strict mode, with Tailwind and shadcn/ui. Server components handle reads and server actions handle writes across 35 action modules.
  • Supabase Auth uses the SSR client with cookies refreshed in middleware. Authentication resolves once per render with React cache, using getUser, because getSession reads the cookie locally and can come back empty in the moment after sign-in.
  • Postgres runs on Supabase through Drizzle, with database migrations stored in the repository.
  • The offline queue uses IndexedDB directly. Client timestamps are validated so they cannot be in the future or more than one day old.
  • 365 tests across 39 Vitest suites, with most business logic tested independently of the framework and browser.
  • Payroll exports support ADP, Paychex, Gusto and QuickBooks, along with CSV and signed PDF. Spreadsheet formula characters are neutralized in exported text fields, so a value like a display name cannot execute when the file is opened.
  • Stripe for billing, Upstash for rate limiting, Sentry for errors, PostHog for product analytics, Resend for transactional email, and Leaflet for maps.
  • Four scheduled jobs handle abandoned shifts, Stripe reconciliation, trial reminders and weekly summaries.
  • The crew apps use Expo and React Native and share the same Supabase project, with biometric unlock, camera, location, push notifications and over-the-air updates.

Marketing

  • Twenty trade pages, five comparison pages, documentation and a blog, all built from the same components as the homepage and informed by search data.
  • App Store listings, directory profiles and a Product Hunt launch.
  • I also handle positioning, pricing, onboarding, SEO and support.

Tools used

  • Next.js
  • React
  • TypeScript
  • Supabase
  • Postgres
  • Drizzle
  • Tailwind
  • Stripe
  • Expo
  • Vitest