40227-vm/backend/docs/policy-documents.md

7.2 KiB

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; users with explicit ACK_POLICY 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 affordances are gated by effective policy-document permissions. 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 effective policy-document permissions). Title/body/steps/considerations changes bump version and require re-acknowledgment.
  • Acknowledgments (business/policies, pages/modules/AcknowledgmentsPage) renders the manager report from GET /api/policy_acknowledgments/report. The Director Dashboard also shows the report summary as an overview card.

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, or returns null as a no-op when the caller is acting through a drilled child scope that is not their own scope) — both guarded by checkPermissions('ACK_POLICY').
  • GET /api/policy_acknowledgments/report — manager-facing acknowledgment report for the current tenant scope. Returns summary totals, per-document completion rows, and per-staff document statuses.

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_DOCUMENTSdirector (full access) and office_manager (explicit grant in the role seeder). teacher/support_staff are read-only.
  • ACK_POLICY — seeded for director, office_manager, teacher, and support_staff. It is a personal workflow permission, is not implied by globalAccess, and can be extended or removed per user via effective permissions. Acknowledgments persist only when the active scope is the user's own scope; parent users drilled into a child school/campus/classroom do not see personal acknowledgment badges or acknowledgment actions there, and the backend no-ops any attempted write so no reportable rows are created for the child scope.

Tenant/campus scoping is applied in the data layer (tenantWhere / findOwnedByPk); acknowledgment reads are additionally restricted to the caller's own userId. The manager report is scoped to the current tenant: organization for owner/superintendent, school for principal/registrar, campus for director, and the active drilled scope for platform admins. Report access requires READ_POLICY_ACKNOWLEDGMENT_REPORTS; owner, superintendent, principal, registrar, and director receive it through seeded baseline permissions. super_admin/system_admin can read it only while drilled into a tenant. custom_permissions can grant the report permission to tenant users and custom_permissions_filter can remove it. The report population is active staff accounts in the current scope holding one of director/office_manager/teacher/support_staff roles.

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).
  • Backend service (backend/src/services/policy_acknowledgments.test.ts): acknowledgment listing plus the drilled-child no-op rule for parent users.
  • 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

None.