Three documents, all print-styled rather than generated server-side: no
PDF dependency is added, and "Save as PDF" in the browser produces the
file. @media print in styles.css strips the app chrome (.no-print) and
breaks each student onto its own sheet (.print-page).
- Report card: weighted category breakdown, overall percent and letter
from the existing gradebook config, plus an attendance summary.
- Academic progress report: the same, plus assignment-level detail —
a mid-term report is only actionable with the underlying work listed.
- 504 / IEP progress report: each goal's baseline, target, status and
progress entries within the period, for the quarterly report IDEA
requires. RLS keeps it to the case manager, admins and parents.
- Invoice, reachable per student from the tuition ledger: opening
balance carried forward, itemised activity, and balance due.
Grade maths is reused from lib/grades and the class grading config from
useEffectiveConfig, so report cards cannot drift from the gradebook.
The "Email to parents" button opens a prefilled mailto: draft — the same
mechanism the intake-link share already uses. It gathers addresses from
intake guardians and linked parent accounts. mailto cannot attach a file,
so the itemisation goes in the body as text; sending a real attachment
would need a transactional email provider and an API key.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plans, accommodations, related services, annual goals with progress
monitoring, meetings/team, and signed documents — plus a compliance
list and dashboard alerts for annual-review and triennial re-evaluation
dates (overdue in red, due-within-30-days in amber).
Access is tiered because special-education records are need-to-know
under FERPA:
FULL admin, the plan's case manager, the student's parents
IMPL the above, plus any teacher of the student — accommodations
and services only, never eligibility or meeting notes
RLS is row-level and every app role is the same Postgres role
(`authenticated`), so column grants cannot separate the tiers. The
split is therefore physical: confidential fields live in plan_details,
plan_goals, plan_meetings and plan_documents rather than as columns on
student_plans.
The UI asks the database which tier applies via the same predicates the
policies use (can_view_plan_full / can_edit_plan) instead of re-deriving
the rules client-side.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- intake_tokens table (server-only via service role)
- Server functions: createIntakeToken (admin), getIntakeToken (public validate),
submitIntake (public write + mark used, 14-day one-time tokens)
- Public /intake/$token full intake form (no login) with valid/used/expired states
- Student profile (admin): generate link, copy, and email-to-parent (mailto)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Admin server function (service-role) to create accounts, set role, and
optionally link a parent to a student; wired via env_file (.env.secret)
- Admin > Users: "Add a user" form (teacher/parent/admin) with one-time temp password
- Student profile > Family: "Parent portal access" — create + link a parent login
- Parents can now edit their own child's profile (RLS-scoped); internal notes stay admin-only
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>