Mathics

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-mergekeep 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.