Go Happy Cab is a production dispatch system for a special needs child transportation company serving Marin County. The dispatcher uses a web dashboard (Expo Router + React Native Web) to pair ~120 children with ~67 drivers daily across AM pickup and PM retrieval periods. The backend is Convex (real-time, 40 tables, ~280 exported functions). SMS communication with drivers is bilingual (English/Portuguese-BR) via Twilio, including group threading. The system is actively shipping features — stakeholder demos happening monthly, production data flowing, carrier approval for SMS pending.
graph TB
subgraph DISPATCH["Dispatch App (Expo Router · Web)"]
DAILY["Daily View
Drag-drop pairing · Day Off toggle"]
WEEKLY["Weekly View
5-column grid · Search/filter"]
CRM["CRM
Drivers · Children · Schools · Clients"]
SMS_UI["SMS
Inbox · Send · Groups · Chat Windows"]
REPORTS["Reports
Roster · Mixed Weeks · Payroll"]
end
subgraph CONVEX["Convex Backend (40 tables · ~280 functions)"]
ASSIGN["assignments.ts
Routes · Unassigned · Copy · Day Off check"]
DRIVERS["drivers.ts
CRUD · On Hold · Day Off toggle"]
SCHEMA["schema.ts
Master schema: routes · drivers · children
driverDayOffs · schools · smsMessages"]
CRON["schedulePreload.ts
Sunday night full-week cron"]
SHEET["sheetSync.ts
Google Sheets ↔ Convex"]
SMS_BE["sms*.ts
Messages · Conversations · Groups · Templates"]
end
subgraph EXTERNAL["External Services"]
TWILIO["Twilio
SMS · Conversations API · Webhooks"]
CLERK["Clerk
Auth · Driver accounts"]
GSHEETS["Google Sheets
Master driver compensation"]
end
DAILY --> ASSIGN
WEEKLY --> ASSIGN
CRM --> DRIVERS
SMS_UI --> SMS_BE
REPORTS --> ASSIGN
ASSIGN --> SCHEMA
DRIVERS --> SCHEMA
SMS_BE --> SCHEMA
CRON --> ASSIGN
SHEET --> GSHEETS
SMS_BE --> TWILIO
DRIVERS --> CLERK
style DISPATCH fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
style CONVEX fill:#ecfdf5,stroke:#059669,color:#064e3b
style EXTERNAL fill:#fef3c7,stroke:#d97706,color:#78350f
driverDayOffs table enables per-date temporary unavailability. Greyed-out card in Daily view, drag-drop blocked, Smart Copy and cron skip day-off drivers.getWeekAssignments, exports to Google Sheets with per-slot driver columns.sheetSync.ts. Wider fetch range (A1:X200) for new driver columns O-X. Skip patterns for schedule notes.populate-week-routes.cjs: comprehensive fuzzy-matching script for importing sheet assignments. Removed carpool max limit of 3 — drivers can serve unlimited children per slot.strategy field.linkClerkIdById mutation. Cleaned up gitignore for .claude/, .expo/, videos/.driverDayOffs table with by_driver_date + by_date indexes. Avoids polluting the drivers table with date-keyed arrays. Each record is one driver + one date. Toggle pattern: insert or delete. No "reason" field required initially but schema supports it as optional.One child per date+period slot (by_child_date_period index). This is the core scheduling constraint — everything else flows from it.
Driver availability is 3-tier: On Hold (persistent roster removal) → Weekly Availability (recurring day/period toggles) → Day Off (one-off per-date). Each checked in order during getUnassignedDrivers.
assignments.ts is the gravity well: 1,631 lines, touched by Daily view, Weekly view, cron, Smart Copy, and reports. Changes here ripple widely.
Schema changes require npm run sync:types to copy convex/_generated to dispatch-app/convex/_generated. Forgetting this causes TypeScript errors in the frontend.
Never run npx convex dev from subdirectories. Must be project root. See CONVEX_DEV_WORKFLOW.md.
Phone numbers must be E.164 format (+1XXXXXXXXXX). The normalizePhone() helper in drivers.ts handles this for new entries.
Webhooks use internalMutation, not public mutations. Clerk webhooks, Twilio inbound/status — all internal.
The codebase uses .web.tsx suffixes for platform-specific web implementations (e.g., DraggableCard.web.tsx vs DraggableCard.native.tsx).
AssignmentScreen.tsx — 2,602 lines. The largest component in the app. Contains drag-drop logic, carpool state, rate modals, inline editing, sort/filter, add-child modal, and now Day Off toggle. Consider extracting drag-drop handler and carpool state into a custom hook.
assignments.ts — 1,631 lines, 20 exports. Handles route CRUD, queries, Smart Copy, scheduling intelligence, and reminders. The copyFromDate mutation alone is ~80 lines with 3 different skip conditions (duplicates, day-offs, holidays). Splitting into assignments-queries.ts and assignments-mutations.ts would help.
3 unmerged stale branches — all-container-theme-wrap, feat/sms-group-deduplication, fix/sms-groups-viewport-layout. These may contain useful work or may be abandoned. Review and prune.
No test suite. Zero automated tests across the entire codebase. The safety net is manual browser verification and Convex's type system. As the codebase grows (40 tables, ~280 functions), critical paths like copyFromDate and getUnassignedDrivers should have unit coverage.
11 files with TODO/FIXME markers in convex/ — including schema.ts, publish.ts, googleSheets.ts, drivers.ts. Audit and resolve or document as intentional.
Chat window z-index unification — documented in docs/TODO-chat-window-zindex-unification.md. Multiple chat windows spawning in the same render cycle all get maxZ + 1. Low priority but causes initial lag with simultaneous inbound SMS.