112 lines
5.9 KiB
Markdown
112 lines
5.9 KiB
Markdown
# Policy Documents & Acknowledgments
|
|
|
|
Workstream 11 — persistence of staff acknowledgment of policy/safety documents.
|
|
|
|
## Purpose
|
|
|
|
Campus staff must acknowledge two categories of documents — **Safety Protocols**
|
|
(official/government) and **Handbook & Policies** (internal). `director` and
|
|
`office_manager` author the documents; all four campus staff roles (`director`,
|
|
`office_manager`, `teacher`, `support_staff`) acknowledge them. Acknowledgment is
|
|
**per document version**: editing a document bumps its `version`, which requires
|
|
re-acknowledgment.
|
|
|
|
## Frontend wiring
|
|
|
|
The two existing pages consume this single store (the old generated `documents`
|
|
entity it replaced has been removed):
|
|
|
|
- **Handbook & Policies** (`business/policies`) lists `policy_documents` of
|
|
`category = handbook_policy`, mapping the handbook's sub-category to/from `tag`
|
|
(`toPolicyViewModel` / `toPolicyDocumentMutationDto`). Management is gated to
|
|
owner/superintendent/director/office_manager (`canManagePolicies`, mirroring the
|
|
backend grant). Acknowledgment is **persisted** via `policy_acknowledgments`
|
|
(`usePolicyAcknowledgments` / `useAcknowledgePolicy`), replacing the former
|
|
local-state set.
|
|
- **Safety Protocols** (`business/safety-protocols`) consumes
|
|
`category = safety_protocol`, rendering author-filled `steps` + autism
|
|
considerations with a static per-`tag` card icon (fire/shield/heart). It shares
|
|
the persistent acknowledgment hooks. Both pages are seeded from
|
|
`20260611050000-policy-documents-seed.ts` (safety protocols reuse the former
|
|
content-catalog `safetyProtocols` payload; a few handbook policies seed the
|
|
handbook page). The Safety Protocols page also has a manager-gated
|
|
**structured-authoring** flow (mirrors the F.R.A.M.E. module: header
|
|
*New Protocol* button → create form, per-card *Edit*/*Delete*) with
|
|
**dynamic** `steps` + `autismConsiderations` rows that add/remove
|
|
independently, so each protocol carries its own count
|
|
(`useSafetyProtocolsModule` + `SafetyProtocolForm` /
|
|
`SafetyDynamicListEditor`; gated by `canManageSafetyProtocols`, which reuses
|
|
the policy grant). Title/body/steps/considerations changes bump `version` and
|
|
require re-acknowledgment.
|
|
|
|
## Entities
|
|
|
|
- `policy_documents` (generic-CRUD entity): `title`, `body`, `category`
|
|
(`safety_protocol` | `handbook_policy` — selects the page), `tag` (nullable
|
|
finer **sub-category**; the Handbook page maps its
|
|
Operations/Behavior/Safety/Communication/Legal categorisation onto it, and the
|
|
Safety page uses it to pick the static card icon), `author` (display name of
|
|
the **creating user**, set server-side at creation and not changed on update),
|
|
`steps` + `autism_considerations` (JSONB string arrays — **author-filled
|
|
structured content** for safety protocols; null for handbook policies),
|
|
`version` (bumped when `title`/`body`/`steps`/`autism_considerations` change),
|
|
`active`, tenant `organizationId` + nullable `campusId`. This is the **single
|
|
unified store** for both the Safety Protocols and Handbook & Policies pages
|
|
(filter by `?category=` and optionally `?tag=`). The category **list** + icons
|
|
are static frontend config; each document's category assignment (`tag`) is DB
|
|
data. `author` is derived from the current user's name —
|
|
`${name_prefix} firstName lastName` (the honorific title from `users.name_prefix`,
|
|
e.g. "Dr. Sarah Williams"), else email.
|
|
- `policy_acknowledgments` (per-user): one row per (`userId`, `policyDocumentId`,
|
|
`version`), with `acknowledgedAt`. Unique index on those three columns;
|
|
acknowledging is idempotent for a given version.
|
|
|
|
## Routes
|
|
|
|
- `GET/POST /api/policy_documents`, `PUT/DELETE /api/policy_documents/:id`,
|
|
plus the standard generic-CRUD extras — guarded by
|
|
`checkCrudPermissions('policy_documents')` (`${METHOD}_POLICY_DOCUMENTS`).
|
|
- `GET /api/policy_acknowledgments` (the caller's own acknowledgments) and
|
|
`POST /api/policy_acknowledgments` (`{ data: { policyDocumentId } }` →
|
|
acknowledges the document's **current** version) — both guarded by
|
|
`checkPermissions('ACK_POLICY')`.
|
|
|
|
## Authorization
|
|
|
|
- `READ_POLICY_DOCUMENTS` — granted to the four campus roles (director via full
|
|
access; office_manager/teacher/support via the read-only entity grant).
|
|
`student`/`guardian` get no policy-document access.
|
|
- `CREATE/UPDATE/DELETE_POLICY_DOCUMENTS` — `director` (full access) and
|
|
`office_manager` (explicit grant in the role seeder). `teacher`/`support_staff`
|
|
are read-only.
|
|
- `ACK_POLICY` — the four campus roles (a product-feature action permission;
|
|
extendable per user via `custom_permissions`).
|
|
|
|
Tenant/campus scoping is applied in the data layer (`tenantWhere` /
|
|
`findOwnedByPk`); acknowledgment reads are additionally restricted to the
|
|
caller's own `userId`. A manager-facing acknowledgment-status report (audience
|
|
TBD) is a deferred refinement.
|
|
|
|
## Tests
|
|
|
|
- **Unit** (`backend/src/shared/constants/policy-documents.test.ts` +
|
|
`users.test.ts`, `npm test`): the pure domain rules —
|
|
`isPolicyDocumentCategory` validation, the `nextPolicyDocumentVersion`
|
|
re-acknowledgment bump, and `formatPersonName` (author rendering).
|
|
- **Frontend unit** (`vitest`): `business/policies/mappers.test.ts` (handbook;
|
|
tag↔category, author), `business/safety-protocols/mappers.test.ts` (steps +
|
|
autism considerations) and `business/safety-protocols/selectors.test.ts`
|
|
(management grant + draft validation for the authoring form).
|
|
- **Seeded e2e** (`frontend/tests/e2e/policy-acknowledgments.seeded.e2e.ts`,
|
|
`npm run test:e2e:content`): document create/persist, manage-vs-read RBAC
|
|
(director/office_manager manage; teacher reads but cannot create), idempotent
|
|
per-version acknowledgment, version-bump re-acknowledgment, and external-role
|
|
lockout.
|
|
|
|
## Open / deferred
|
|
|
|
- Acknowledgment-status reporting for managers (who-acknowledged-what) — pending
|
|
the report-audience decision.
|
|
- The acknowledgment + document-management **UI** is design-gated (see
|
|
`docs/backlog.md`).
|