40227-vm/backend/docs/walkthrough-checkins.md
2026-06-12 06:55:35 +02:00

99 lines
5.4 KiB
Markdown

# Walk-Through Check-Ins Backend
## Purpose
`walkthrough_checkins` stores structured classroom-observation ("walk-through") records for
director-level staff. Each record captures the observed teacher, classroom, observing director,
date/time, seven rated categories with optional per-category comments, and overall notes. The
backend owns tenant scope, campus scope, creator ownership, validation, and role-gated access.
## Slice Files (by layer)
- Route: `src/routes/walkthrough_checkins.ts` (thin wiring; `GET /`, `POST /`, `DELETE /:id`).
- Controller: `src/api/controllers/walkthrough_checkins.controller.ts` (custom — not the CRUD
factory; uses `paramStr` for the id param).
- Service (BLL): `src/services/walkthrough_checkins.ts` (+ `walkthrough_checkins.types.ts` for
`WalkthroughInput` / `WalkthroughFilter`).
- Repository (DAL): queries run through `db.walkthrough_checkins` inside the service (no separate
`db/api/walkthrough_checkins.ts`).
- Model: `src/db/models/walkthrough_checkins.ts`.
- Shared used: `services/shared/validate.ts` (`nullableString`); `db/with-transaction.ts`
(`withTransaction`); `shared/constants/pagination.ts` (`resolvePagination`);
`shared/constants/walkthrough.ts` (`WALKTHROUGH_MANAGER_ROLE_NAMES`,
`WALKTHROUGH_TENANT_WIDE_ROLE_NAMES`); `services/shared/access.ts` (`getOrganizationIdOrGlobal`,
`hasGlobalAccess`, `hasRoleAccess`, `requireUserId`); `shared/errors/*` (`ForbiddenError`,
`ValidationError`). Note: the service defines a module-local `getCampusId` and `campusScope`
(staff-profile campus only), not the shared access helpers.
## API
All routes require JWT authentication. Base path mounted at `/api/walkthrough_checkins`.
- `GET /api/walkthrough_checkins` -> `200` `{ rows, count }`. Optional query `teacher_name`, plus
`limit` / `page` (paginated via `resolvePagination`). Returns check-ins visible to the current
manager, ordered by `check_in_date` desc then `createdAt` desc.
- `POST /api/walkthrough_checkins` -> `201`. Request body wrapped as `{ data: <WalkthroughInput> }`.
Returns the created check-in DTO.
- `DELETE /api/walkthrough_checkins/:id` -> `200` `{ deletedCount }`. Deletes one check-in visible
to the current manager.
## Access Rules
- All operations require a role in `WALKTHROUGH_MANAGER_ROLE_NAMES` (`super_admin`,
`system_admin`, `owner`, `superintendent`, `director`) or `globalAccess`,
enforced by `assertCanManage` (which also requires an authenticated user); otherwise
`ForbiddenError`. Users with `globalAccess` are always allowed.
- Campus visibility: roles in `WALKTHROUGH_TENANT_WIDE_ROLE_NAMES` (`super_admin`,
`system_admin`, `owner`, `superintendent`) or `globalAccess` see all org records;
other managers (e.g. `director`) are restricted to their own staff campus on `list` and
`delete` via `campusScope`.
## Tenant Scope
- Organization is resolved via `getOrganizationIdOrGlobal`: global access users bypass the org
filter and see check-ins across all organizations; regular users are bound to their org.
- `campusId` is resolved from the module-local `getCampusId` — the current staff profile's campus
only (`currentUser.staff_user[0].campusId`), else `null`; it never falls back to the user's own
`campusId`.
- On create, `createdById` is required from the current user (`requireUserId`); `updatedById` from
the current user.
## Data Contract
- Required mutation fields: `teacher_name`, `classroom`, `director_name`, `check_in_date`,
`check_in_time` (non-empty strings); and the seven rating fields `attitude_rating`,
`classroom_management_rating`, `cleanliness_rating`, `vibes_rating`, `team_dynamics_rating`,
`emergency_exit_rating`, `lesson_plan_rating` (finite numbers). Invalid input raises
`ValidationError`.
- Optional mutation fields: the matching per-category comments (`attitude_comment`,
`classroom_management_comment`, `cleanliness_comment`, `vibes_comment`, `team_dynamics_comment`,
`emergency_exit_comment`, `lesson_plan_comment`) and `overall_notes`. Comments are normalized via
`nullableString` (trimmed, blank -> `null`).
- DTO fields: `id`, the five required strings, the seven `*_rating` and matching `*_comment` fields,
`overall_notes`, `organizationId`, `campusId`, `createdById`, `updatedById`, `createdAt`,
`updatedAt`.
- Model columns: the five required strings are TEXT not null except `check_in_date` (DATEONLY) and
`check_in_time` (TIME); the seven `*_rating` are INTEGER not null; the seven `*_comment` and
`overall_notes` are TEXT nullable; `importHash` (unique). Tenant/audit columns: `organizationId`
(not null), `createdById` (not null), `campusId` (nullable), `updatedById` (nullable). The model
is `paranoid` (soft delete via `deletedAt`) and uses `freezeTableName`.
- Associations: `belongsTo` organizations (`organization`), campuses (`campus`), users
(`createdBy`, `updatedBy`). Note: there is no `user` association on this model.
## Behavior / Notes
- `create` runs inside `withTransaction`; required string fields are trimmed before persistence.
- `list` is paginated with shared defaults (`resolvePagination`).
- `remove` is a hard `destroy` scoped by `id` + organization + `campusScope`, returning the number
of rows deleted (no transaction wrapper).
## Tests
None yet (no `walkthrough_checkins` unit/e2e test in `src/`).
## Related
- Frontend: `frontend/docs/walkthrough-integration.md`.
- Related slices: `frame-entries.md` (similar editor-role-gated, campus-resolved director
workflow), `safety-quiz-results.md`, `personality-quiz-results.md`.