Mathics

ENTRY
LOGGED

A planner and shift-scheduling system for the people in the tower. Air-traffic control runs 24/7 across multiple positions, each requiring specific certifications, with rest-period rules that aren’t optional and a workforce that — like everyone else — also wants weekends off and predictable trades. Doing that with a spreadsheet is exactly as miserable as it sounds.

The shape of the problem

Building a schedule for an ATC unit isn’t a generic shift planner. A few things make it specific:

  • Position-bound — controllers aren’t interchangeable. Each rated position needs someone currently certified on it, with currency hours kept up to date.
  • Hard rest rules — minimum rest between shifts, maximum hours per week, recency requirements. These are regulatory, not preferences.
  • Time zone honesty — operations cross UTC and local timezones, and a schedule shown in the wrong zone is worse than no schedule at all.
  • Trades and leave — controllers swap shifts and request leave constantly. Every change has to re-validate against the same constraints in real time.
  • High density — months of shifts × dozens of controllers × multiple positions is a lot of cells, and they all need to be readable, scrollable, and editable without the UI choking.

The product is built around a timeline-grid view that surfaces all of that and lets a planner build, adjust, and validate a roster without leaving the page.

Stack

The product is a full-stack React app on Cloudflare, built around the TanStack ecosystem and a Radix-based design system.

  • TanStack Start — the full-stack React framework. SSR for fast first paint on a heavy schedule view, with server functions for data work that shouldn’t ship to the client.
  • TanStack Router — type-safe routing with searchparam-as-state for the schedule view (date range, filters, selected position, etc. all live in the URL and survive refresh / share).
  • TanStack Query — server-state cache. The schedule view reads from many endpoints in parallel; React Query handles the dedup, invalidation, and optimistic updates when a planner drops a shift onto a cell.
  • TanStack Table + react-window — the timeline-grid is a virtualised table. Months of shifts × dozens of controllers don’t fit in a naive DOM tree.
  • TanStack Form + Zod — leave requests, trade proposals, and the small forms scattered across the app. Zod schemas are the single source of truth for validation, shared with the API contract.
  • Radix UI primitives + a Shadcn-style design system — accessible, headless components (dialog, popover, dropdown, tooltip, scroll-area, etc.) styled with Tailwind. class-variance-authority + tailwind-merge keep variants tidy.
  • Tailwind CSS v4 — the styling layer.
  • Motion — micro-animations for state changes (shift drops, trade resolution, conflict-banner reveals).
  • Recharts — utilization, fatigue, and coverage analytics overlaid on the schedule.
  • date-fns + date-fns-tz — every date in the system is timezone-aware; nothing is rendered without an explicit zone.
  • Orval — generates the typed API client + React Query hooks + Zod schemas from the backend’s OpenAPI spec, so the client never drifts from the contract.
  • MSW — request mocking for local development and Storybook stories, so the UI can be built and tested against realistic responses without the backend.
  • Sonner — toasts for trade approvals, sync errors, etc.
  • Tabler Icons + Lucide — icon system.
  • Sentry — error tracking for both the React app and the SSR runtime.
  • Cloudflare Workers + Wrangler — deploys to the edge, close to where planners actually sit.
  • Paraglide — i18n with type-safe message keys.

Quality and tooling

  • Storybook with the a11y and Vitest addons — every interactive component has stories, and the Vitest addon runs them as tests in CI.
  • Playwright for end-to-end flows that span multiple pages (publish a roster, controller accepts a trade, planner reviews).
  • Vitest in browser mode for the units that touch DOM and need real layout/interaction.
  • ESLint v9 + Prettier with sorted imports and Tailwind class sort.
  • Husky + lint-staged + commitlint so the messy stuff gets caught before it lands.

Outcome

In production with the people who actually have to plan towers. The architecture is built to keep being extended — new constraint types, new analytics overlays, new integrations with the underlying staffing systems — without rewriting the surface.