3.0 KiB
Daily Zone Check-in
Workstream 16 — campus staff log a daily self-regulation "zone" (Zones of
Regulation: blue/green/yellow/red). History is retained per day, and an eligible
user who has not checked in today is nudged on the dashboard, the
/zones-of-regulation page, and in the notification dropdown.
"Today" is server-computed in the campus timezone
The check-in date is not decided by the client. Each campus carries a
required IANA timezone (campuses.timezone); the service computes the
campus-local date (localDateInTimezone, native Intl, DST-correct) so "today"
is independent of the caller's device clock/zone and correct across
organizations, campuses, and timezones.
Storage (reuses user_progress)
No new table. A check-in is a user_progress row with
progress_type = zone_checkin and item_id = the campus-local date
(YYYY-MM-DD), value = the zone color. Because the per-user upsert key is
(userId, progress_type, item_id), this yields one row per user per day, and
the set of rows is the history (item_id sorts chronologically). The thin
services/zone-checkin.ts wraps UserProgressService and owns the timezone/date
logic, keeping the generic user_progress endpoint generic.
Routes (/api/zone_checkins)
All require ZONE_CHECKIN (the four campus staff roles).
GET /today→{ date, zone, isCheckedInToday }(campus-local date).POST /→ record today's zone. Body{ data: { zone } }(blue|green|yellow|red).DELETE /today→ clear today's check-in.GET /?from=&to=→ the caller's history ({ rows: [{ date, zone }], count }).
Authorization
ZONE_CHECKIN—director(full access),office_manager(via...MODULE_ACTIONS),teacher,support_staff(explicit grants). Other roles (owner/superintendent/student/guardian/system) are not granted it; the frontend also gates the nudge to the four campus roles (canZoneCheckIn). Reads/writes are scoped to the caller's ownuserIdbyUserProgressService.- A user with no campus has no campus-local "today" — the service rejects with a validation error (only campus staff reach these routes).
Tests
- Unit (
npm test):shared/constants/timezone.test.ts(campus-local date incl. Phoenix no-DST + a DST zone;isValidIanaTimezone) andshared/constants/zone-checkin.test.ts(isZoneCheckinColor). - Frontend unit (
vitest):business/zone-checkin/selectors.test.ts(eligibility + nudge) and the top-bar notification builder (incl. the zoneshref). - Seeded e2e (
frontend/tests/e2e/zone-checkins.seeded.e2e.ts,npm run test:e2e:content): a campus-staff record/read-back/clear of today's zone, invalid-zone rejection, and external-role lockout.
Open / deferred
- A manager-facing aggregate (campus self-regulation trends across staff) would
need a cross-user report endpoint (
user_progressis self-scoped) — deferred. - Editing a campus
timezoneis part of the (design-gated) campus admin UI; for now it is seeded and validated at the API.