Mathics

Et planlægnings- og vagtplanssystem til folkene i tårnet. Flyvekontrol kører 24/7 på tværs af flere positioner, der hver kræver specifikke certificeringer, med hvileregler, der ikke er valgfri, og en arbejdsstyrke, der — ligesom alle andre — også gerne vil have weekender fri og forudsigelige vagtbytter. At gøre det i et regneark er præcis så elendigt, som det lyder.
Problemets form
At bygge en vagtplan for en ATC-enhed er ikke en generisk shift-planner. Et par ting gør det specifikt:
- Positionsbundet — controllere er ikke ombyttelige. Hver godkendt position kræver en, der i øjeblikket er certificeret til den, med opdaterede currency-timer.
- Hårde hvileregler — minimumshvile mellem vagter, maksimum timer pr. uge, recency-krav. De er regulative, ikke præferencer.
- Tidszone-ærlighed — operationer krydser UTC og lokal tid, og en vagtplan vist i den forkerte zone er værre end ingen plan overhovedet.
- Bytter og fravær — controllere bytter vagter og søger fravær konstant. Hver ændring skal revalideres mod de samme constraints i realtid.
- Høj tæthed — måneders vagter × dusinvis af controllere × flere positioner er mange celler, og de skal alle være læsbare, scrollbare og redigerbare uden at UI’et kvæles.
Produktet er bygget op om et timeline-grid-view, der overflader alt det, og lader en planlægger bygge, justere og validere en plan uden at forlade siden.
Stack
Produktet er en full-stack React-app på Cloudflare, bygget op om TanStack-økosystemet og et Radix-baseret designsystem.
- TanStack Start — full-stack React-framework. SSR for hurtigt first paint på et tungt vagtplansview, med server-funktioner til datalogik, der ikke skal sendes til klienten.
- TanStack Router — typesikker routing med searchparam-as-state til vagtplansviewet (datointerval, filtre, valgt position osv. lever alle i URL’en og overlever refresh / deling).
- TanStack Query — server-state cache. Vagtplansviewet læser fra mange endpoints parallelt; React Query håndterer dedup, invalidering og optimistiske opdateringer, når en planlægger dropper en vagt på en celle.
- TanStack Table + react-window — timeline-griddet er en virtualiseret tabel. Måneders vagter × dusinvis af controllere passer ikke i et naivt DOM-træ.
- TanStack Form + Zod — fraværsanmodninger, byttetilbud og de små forms spredt rundt i appen. Zod-skemaer er den eneste sandhedskilde for validering og deles med API-kontrakten.
- Radix UI primitives + et Shadcn-style designsystem — tilgængelige, headless komponenter (dialog, popover, dropdown, tooltip, scroll-area osv.) styled med Tailwind.
class-variance-authority+tailwind-mergeholder varianter ryddelige. - Tailwind CSS v4 — stylinglaget.
- Motion — mikroanimationer ved tilstandsændringer (vagt-drop, byt-afgørelse, konflikt-banner-reveals).
- Recharts — udnyttelses-, træthed- og dækningsanalyser lagt oven på vagtplanen.
- date-fns + date-fns-tz — hver dato i systemet er tidszone-bevidst; intet renderes uden en eksplicit zone.
- Orval — genererer den typede API-klient + React Query-hooks + Zod-skemaer ud fra backendens OpenAPI-spec, så klienten aldrig driver fra kontrakten.
- MSW — request-mocking til lokal udvikling og Storybook-stories, så UI’et kan bygges og testes mod realistiske svar uden backend.
- Sonner — toasts ved byt-godkendelser, sync-fejl osv.
- Tabler Icons + Lucide — ikonsystem.
- Sentry — fejlsporing for både React-appen og SSR-runtimen.
- Cloudflare Workers + Wrangler — deployer til edge, tæt på hvor planlæggerne faktisk sidder.
- Paraglide — i18n med typesikre message-nøgler.
Kvalitet og tooling
- Storybook med a11y- og Vitest-addons — hver interaktiv komponent har stories, og Vitest-addon kører dem som tests i CI.
- Playwright til end-to-end-flows, der spænder over flere sider (publicer en plan, controller accepterer et byt, planlægger gennemgår).
- Vitest i browser-mode til de units, der rører DOM og kræver reel layout/interaktion.
- ESLint v9 + Prettier med sorterede imports og Tailwind-klasse-sortering.
- Husky + lint-staged + commitlint så det rodede fanges, før det lander.
Resultat
I produktion hos dem, der faktisk skal planlægge tårne. Arkitekturen er bygget til løbende at blive udvidet — nye constraint-typer, nye analyselag, nye integrationer med de underliggende bemandingssystemer — uden at omskrive overfladen.