68 lines
2.8 KiB
Markdown
68 lines
2.8 KiB
Markdown
# 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: <FrameEntryInput> }`.
|
|
|
|
## 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).
|