4.0 KiB
4.0 KiB
Daily Zone Check-in Integration
Purpose
Staff log a daily self-regulation "Emotional Zone" (blue/green/yellow/red).
The same state drives the dashboard check-in card, the /zones-of-regulation
overview cards, the profile unified results table, leader dashboard
completion/risk sections, and a notification-dropdown nudge when an eligible
user has not checked in today.
Backend contract
/api/zone_checkins requires explicit ZONE_CHECKIN for personal reads/writes.
This personal workflow permission is not implied by globalAccess. The client
never computes the date; "today" is computed server-side from the user's campus
timezone, falling back to UTC for organization/school-scope staff without a
campus.
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 }GET /completion→ leader/report scoped staff completion{ summary, rows }withREAD_ZONE_CHECKIN_REPORTS.
Frontend Structure
- API/types:
shared/api/zoneCheckins.ts,shared/types/zoneCheckins.ts - Business:
business/zone-checkin/hooks.ts(useTodayZoneCheckIn,useZoneCheckInHistory,useZoneCheckInCompletion),business/zone-checkin/selectors.ts(canZoneCheckIn,shouldNudgeZoneCheckIn) - Components:
components/zone-checkin/ZoneCheckInCard.tsx(shared dashboard card),ZoneCheckInReminder.tsx(banner),ZoneCheckInSection.tsx(composed section), and the/zones-of-regulationoverview cards.
Behavior
- Eligibility/nudge gating requires the effective
ZONE_CHECKINpermission. Seed data grants it to staff users expected to complete the daily workflow across organization, school, campus, and class scopes; custom permissions can extend or remove it per user. - Dashboard: the card is wired through
useDashboardPage(which exposesshowZoneCheckIn+needsZoneCheckIn) with an optimistic shell value for snappy selection. - Zones page: the main zone overview cards call
useTodayZoneCheckInthroughuseZonesOfRegulationPage; selecting blue/green/yellow/red both opens the zone detail panel and saves the eligible user's daily check-in. Parent drill-down keeps the selection local and does not persist personal state; the backend also rejects personal zone mutations in child scopes. - 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. useTodayZoneCheckInis disabled for users withoutZONE_CHECKINor when the user is drilled into a child scope. Disabled callers receive empty local state instead of stale cached check-in data. Itserrorsurfaces only save/clear failures; non-eligible users should not trigger the/todayrequest.- Leader dashboards:
useDirectorDashboardPageloadsuseZoneCheckInCompletionwith the effective tenant in its query key and appends Daily Zone Check-In rows to the unified completion table. Non-green completed zones are shown as medium risk in Risk Areas. - Profile: the unified quiz/results table includes today's Daily Zone Check-In for eligible users.
- Content: zone cards and page copy are backend-owned content catalog presets
(
regulation-zones,zones-of-regulation-page-content) for new organizations.
Tests
business/zone-checkin/selectors.test.ts(eligibility + nudge),business/top-bar/selectors.test.ts(notification builder + zoneshref),business/director-dashboard/selectors.test.ts,business/profile/selectors.test.ts, andshared/api/zoneCheckins.test.ts.- 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.