2.8 KiB
2.8 KiB
Daily Zone Check-in Integration
Purpose
Campus staff log a daily self-regulation "Emotional Zone" (blue/green/yellow/red).
The same state drives three surfaces: the dashboard check-in card, the
/zones-of-regulation page (reminder banner + card), and a notification-dropdown
nudge when an eligible user has not checked in today.
Backend contract
/api/zone_checkins (requires ZONE_CHECKIN — the four campus staff roles). The
client never computes the date; "today" is the campus-local date computed
server-side from campuses.timezone.
GET /today→{ date, zone, isCheckedInToday }POST /{ data: { zone } }→ record today's zone (upsert)DELETE /today→ clear today's zoneGET /?from=&to=→ history{ rows: [{ date, zone }], count }
Frontend Structure
- API/types:
shared/api/zoneCheckins.ts,shared/types/zoneCheckins.ts - Business:
business/zone-checkin/hooks.ts(useTodayZoneCheckIn,useZoneCheckInHistory),business/zone-checkin/selectors.ts(canZoneCheckIn,shouldNudgeZoneCheckIn) - Components:
components/zone-checkin/ZoneCheckInCard.tsx(shared card),ZoneCheckInReminder.tsx(banner),ZoneCheckInSection.tsx(page section)
Behavior
- Eligibility/nudge gating is role-based (
canZoneCheckIn— the four campus staff roles), mirroring the backend grant. The dashboard card and the zones-page section render only for eligible roles; the nudge (red "Not checked in" badge, reminder banner, and notification) shows when an eligible user hasn't checked in today. - Dashboard: the card is wired through
useDashboardPage(which exposesshowZoneCheckIn+needsZoneCheckIn) with an optimistic shell value for snappy selection. - Zones page:
ZoneCheckInSectionis self-contained (useTodayZoneCheckIn) and renders above the regulation content. - Notifications:
business/top-barderives a single unread notification fromshouldNudgeZoneCheckIn(buildTopBarNotifications) — there is no backend notifications store. The notification carries anhref(APP_ROUTE_PATHS.zones); clicking it navigates to/zones-of-regulation(a react-routerLink) and closes the dropdown. - The
useTodayZoneCheckInerrorsurfaces only save/clear failures; the today-load query can 403 for an ineligible caller and must not render as an error in the widget. React Query dedupes the/todayfetch across all three surfaces.
Tests
business/zone-checkin/selectors.test.ts(eligibility + nudge),business/top-bar/selectors.test.ts(notification builder + zoneshref).- Seeded e2e:
frontend/tests/e2e/zone-checkins.seeded.e2e.ts(record / read-back / clear today, invalid-zone rejection, external-role lockout).
Verification
npm run typecheck,npm run lint,npm run testpass.