# FRAME Entries Backend ## Purpose `frame_entries` stores weekly F.R.A.M.E. focus entries per organization. The backend is the source of truth for persisted FRAME data; the frontend never substitutes static samples for it. ## Slice Files (by layer) - Route: `src/routes/frame_entries.ts` (thin wiring; `GET /`, `POST /`, `PUT /:id`). - Controller: `src/api/controllers/frame_entries.controller.ts` (custom — not the CRUD factory). - Service (BLL): `src/services/frame_entries.ts`. - Repository (DAL): queries run through `db.frame_entries` inside the service (no separate `db/api/frame_entries.ts`). - Model: `src/db/models/frame_entries.ts`. - Shared used: `db/with-transaction.ts` (`withTransaction`), `services/shared/access.ts` (`getOrganizationIdOrGlobal`, `hasRoleAccess`), `shared/constants/pagination.ts` (`resolvePagination`), `shared/constants/frame.ts` (`FRAME_EDITOR_ROLE_NAMES`), `shared/errors/*` (`ForbiddenError`, `ValidationError`). ## API All routes require JWT authentication. - `GET /api/frame_entries` -> `200` `{ rows, count }` for the current user's organization (paginated via `resolvePagination`). - `POST /api/frame_entries` -> `201` the created entry DTO. - `PUT /api/frame_entries/:id` -> `200` the updated entry DTO (scoped to the org). Request body for create/update is wrapped as `{ data: }`. ## Access Rules - Read: any authenticated user in the organization, or any user with `globalAccess` (sees all organizations). - Edit (create/update): restricted to roles in `FRAME_EDITOR_ROLE_NAMES` (director/superintendent capabilities) — `Super Administrator`, `Administrator`, `Platform Owner`, `Tenant Director`, `Campus Manager`. Enforced by `assertCanEdit` via `hasRoleAccess`; a non-editor gets `ForbiddenError`. Frontend may hide editing controls, but the backend check is authoritative. ## Tenant Scope - Organization is resolved via `getOrganizationIdOrGlobal`: users with `globalAccess` bypass the org filter and see/create entries across all organizations; regular users are bound to their organization. - `campusId` is optional; when omitted it defaults to the current staff profile's campus (`currentUser.staff_user[0].campusId`) when available, else `null`. ## Data Contract Required request fields (`REQUIRED_FIELDS`): `week_of`, `posted_date`, `formal`, `recognition`, `application`, `management`, `emotional`, `author`. Optional: `campusId`. Missing/invalid input raises `ValidationError`. ## Behavior / Notes - Create/update run inside `withTransaction`. - List is paginated with the shared defaults (`resolvePagination`). ## Tests None yet (no `frame_entries` unit/e2e test in `src/`). ## Related - Frontend: `frontend/docs/frame-integration.md`. - Related slices: `user-progress.md` (dashboard zone check-ins), `staff` (campus resolution).