Add 504 / IEP plans with tiered confidentiality
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>
This commit is contained in:
@@ -620,6 +620,358 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_accommodations: {
|
||||
Row: {
|
||||
category: string
|
||||
created_at: string
|
||||
description: string
|
||||
id: string
|
||||
plan_id: string
|
||||
setting: string | null
|
||||
sort_order: number
|
||||
}
|
||||
Insert: {
|
||||
category?: string
|
||||
created_at?: string
|
||||
description: string
|
||||
id?: string
|
||||
plan_id: string
|
||||
setting?: string | null
|
||||
sort_order?: number
|
||||
}
|
||||
Update: {
|
||||
category?: string
|
||||
created_at?: string
|
||||
description?: string
|
||||
id?: string
|
||||
plan_id?: string
|
||||
setting?: string | null
|
||||
sort_order?: number
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_accommodations_plan_id_fkey"
|
||||
columns: ["plan_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "student_plans"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_details: {
|
||||
Row: {
|
||||
confidential_notes: string | null
|
||||
consent_date: string | null
|
||||
eligibility_category: string | null
|
||||
eligibility_notes: string | null
|
||||
evaluation_summary: string | null
|
||||
last_evaluation_date: string | null
|
||||
parent_concerns: string | null
|
||||
plan_id: string
|
||||
student_strengths: string | null
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
confidential_notes?: string | null
|
||||
consent_date?: string | null
|
||||
eligibility_category?: string | null
|
||||
eligibility_notes?: string | null
|
||||
evaluation_summary?: string | null
|
||||
last_evaluation_date?: string | null
|
||||
parent_concerns?: string | null
|
||||
plan_id: string
|
||||
student_strengths?: string | null
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
confidential_notes?: string | null
|
||||
consent_date?: string | null
|
||||
eligibility_category?: string | null
|
||||
eligibility_notes?: string | null
|
||||
evaluation_summary?: string | null
|
||||
last_evaluation_date?: string | null
|
||||
parent_concerns?: string | null
|
||||
plan_id?: string
|
||||
student_strengths?: string | null
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_details_plan_id_fkey"
|
||||
columns: ["plan_id"]
|
||||
isOneToOne: true
|
||||
referencedRelation: "student_plans"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_documents: {
|
||||
Row: {
|
||||
created_at: string
|
||||
file_path: string
|
||||
id: string
|
||||
plan_id: string
|
||||
title: string | null
|
||||
uploaded_by: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
file_path: string
|
||||
id?: string
|
||||
plan_id: string
|
||||
title?: string | null
|
||||
uploaded_by?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
file_path?: string
|
||||
id?: string
|
||||
plan_id?: string
|
||||
title?: string | null
|
||||
uploaded_by?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_documents_plan_id_fkey"
|
||||
columns: ["plan_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "student_plans"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_goal_progress: {
|
||||
Row: {
|
||||
created_at: string
|
||||
date: string
|
||||
goal_id: string
|
||||
id: string
|
||||
narrative: string | null
|
||||
recorded_by: string | null
|
||||
value: number | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
date?: string
|
||||
goal_id: string
|
||||
id?: string
|
||||
narrative?: string | null
|
||||
recorded_by?: string | null
|
||||
value?: number | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
date?: string
|
||||
goal_id?: string
|
||||
id?: string
|
||||
narrative?: string | null
|
||||
recorded_by?: string | null
|
||||
value?: number | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_goal_progress_goal_id_fkey"
|
||||
columns: ["goal_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "plan_goals"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_goals: {
|
||||
Row: {
|
||||
area: string | null
|
||||
baseline: string | null
|
||||
created_at: string
|
||||
id: string
|
||||
plan_id: string
|
||||
sort_order: number
|
||||
statement: string
|
||||
status: Database["public"]["Enums"]["plan_goal_status"]
|
||||
target_criteria: string | null
|
||||
target_date: string | null
|
||||
target_value: number | null
|
||||
unit: string | null
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
area?: string | null
|
||||
baseline?: string | null
|
||||
created_at?: string
|
||||
id?: string
|
||||
plan_id: string
|
||||
sort_order?: number
|
||||
statement: string
|
||||
status?: Database["public"]["Enums"]["plan_goal_status"]
|
||||
target_criteria?: string | null
|
||||
target_date?: string | null
|
||||
target_value?: number | null
|
||||
unit?: string | null
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
area?: string | null
|
||||
baseline?: string | null
|
||||
created_at?: string
|
||||
id?: string
|
||||
plan_id?: string
|
||||
sort_order?: number
|
||||
statement?: string
|
||||
status?: Database["public"]["Enums"]["plan_goal_status"]
|
||||
target_criteria?: string | null
|
||||
target_date?: string | null
|
||||
target_value?: number | null
|
||||
unit?: string | null
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_goals_plan_id_fkey"
|
||||
columns: ["plan_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "student_plans"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_meeting_attendees: {
|
||||
Row: {
|
||||
attended: boolean
|
||||
created_at: string
|
||||
id: string
|
||||
meeting_id: string
|
||||
name: string
|
||||
team_role: string | null
|
||||
user_id: string | null
|
||||
}
|
||||
Insert: {
|
||||
attended?: boolean
|
||||
created_at?: string
|
||||
id?: string
|
||||
meeting_id: string
|
||||
name: string
|
||||
team_role?: string | null
|
||||
user_id?: string | null
|
||||
}
|
||||
Update: {
|
||||
attended?: boolean
|
||||
created_at?: string
|
||||
id?: string
|
||||
meeting_id?: string
|
||||
name?: string
|
||||
team_role?: string | null
|
||||
user_id?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_meeting_attendees_meeting_id_fkey"
|
||||
columns: ["meeting_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "plan_meetings"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_meetings: {
|
||||
Row: {
|
||||
created_at: string
|
||||
created_by: string | null
|
||||
id: string
|
||||
location: string | null
|
||||
meeting_date: string
|
||||
meeting_type: Database["public"]["Enums"]["plan_meeting_type"]
|
||||
notes: string | null
|
||||
outcome: string | null
|
||||
plan_id: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
created_by?: string | null
|
||||
id?: string
|
||||
location?: string | null
|
||||
meeting_date: string
|
||||
meeting_type?: Database["public"]["Enums"]["plan_meeting_type"]
|
||||
notes?: string | null
|
||||
outcome?: string | null
|
||||
plan_id: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
created_by?: string | null
|
||||
id?: string
|
||||
location?: string | null
|
||||
meeting_date?: string
|
||||
meeting_type?: Database["public"]["Enums"]["plan_meeting_type"]
|
||||
notes?: string | null
|
||||
outcome?: string | null
|
||||
plan_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_meetings_plan_id_fkey"
|
||||
columns: ["plan_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "student_plans"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
plan_services: {
|
||||
Row: {
|
||||
created_at: string
|
||||
delivery: string | null
|
||||
end_date: string | null
|
||||
id: string
|
||||
location: string | null
|
||||
minutes_per_session: number | null
|
||||
period: string
|
||||
plan_id: string
|
||||
provider_id: string | null
|
||||
provider_name: string | null
|
||||
service_type: string
|
||||
sessions_per_period: number | null
|
||||
start_date: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
delivery?: string | null
|
||||
end_date?: string | null
|
||||
id?: string
|
||||
location?: string | null
|
||||
minutes_per_session?: number | null
|
||||
period?: string
|
||||
plan_id: string
|
||||
provider_id?: string | null
|
||||
provider_name?: string | null
|
||||
service_type: string
|
||||
sessions_per_period?: number | null
|
||||
start_date?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
delivery?: string | null
|
||||
end_date?: string | null
|
||||
id?: string
|
||||
location?: string | null
|
||||
minutes_per_session?: number | null
|
||||
period?: string
|
||||
plan_id?: string
|
||||
provider_id?: string | null
|
||||
provider_name?: string | null
|
||||
service_type?: string
|
||||
sessions_per_period?: number | null
|
||||
start_date?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "plan_services_plan_id_fkey"
|
||||
columns: ["plan_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "student_plans"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
profiles: {
|
||||
Row: {
|
||||
created_at: string
|
||||
@@ -744,6 +1096,59 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
student_plans: {
|
||||
Row: {
|
||||
case_manager_id: string | null
|
||||
created_at: string
|
||||
created_by: string | null
|
||||
effective_date: string | null
|
||||
end_date: string | null
|
||||
id: string
|
||||
next_annual_review_date: string | null
|
||||
next_reevaluation_date: string | null
|
||||
plan_type: Database["public"]["Enums"]["plan_type"]
|
||||
status: Database["public"]["Enums"]["plan_status"]
|
||||
student_id: string
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
case_manager_id?: string | null
|
||||
created_at?: string
|
||||
created_by?: string | null
|
||||
effective_date?: string | null
|
||||
end_date?: string | null
|
||||
id?: string
|
||||
next_annual_review_date?: string | null
|
||||
next_reevaluation_date?: string | null
|
||||
plan_type: Database["public"]["Enums"]["plan_type"]
|
||||
status?: Database["public"]["Enums"]["plan_status"]
|
||||
student_id: string
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
case_manager_id?: string | null
|
||||
created_at?: string
|
||||
created_by?: string | null
|
||||
effective_date?: string | null
|
||||
end_date?: string | null
|
||||
id?: string
|
||||
next_annual_review_date?: string | null
|
||||
next_reevaluation_date?: string | null
|
||||
plan_type?: Database["public"]["Enums"]["plan_type"]
|
||||
status?: Database["public"]["Enums"]["plan_status"]
|
||||
student_id?: string
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "student_plans_student_id_fkey"
|
||||
columns: ["student_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "students"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
students: {
|
||||
Row: {
|
||||
agreement_signed_by: string | null
|
||||
@@ -906,6 +1311,13 @@ export type Database = {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
can_edit_goal: { Args: { _goal: string }; Returns: boolean }
|
||||
can_edit_meeting: { Args: { _meeting: string }; Returns: boolean }
|
||||
can_edit_plan: { Args: { _plan: string }; Returns: boolean }
|
||||
can_view_goal: { Args: { _goal: string }; Returns: boolean }
|
||||
can_view_meeting: { Args: { _meeting: string }; Returns: boolean }
|
||||
can_view_plan_full: { Args: { _plan: string }; Returns: boolean }
|
||||
can_view_plan_impl: { Args: { _plan: string }; Returns: boolean }
|
||||
current_user_has_role: {
|
||||
Args: { _role: Database["public"]["Enums"]["app_role"] }
|
||||
Returns: boolean
|
||||
@@ -928,6 +1340,21 @@ export type Database = {
|
||||
attendance_status: "present" | "absent" | "late" | "excused"
|
||||
ledger_category: "tuition" | "late_pickup" | "activity" | "other"
|
||||
ledger_kind: "charge" | "payment"
|
||||
plan_goal_status:
|
||||
| "not_started"
|
||||
| "in_progress"
|
||||
| "met"
|
||||
| "not_met"
|
||||
| "discontinued"
|
||||
plan_meeting_type:
|
||||
| "initial"
|
||||
| "annual_review"
|
||||
| "reevaluation"
|
||||
| "eligibility"
|
||||
| "amendment"
|
||||
| "other"
|
||||
plan_status: "draft" | "active" | "archived"
|
||||
plan_type: "iep" | "section_504"
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
@@ -1062,6 +1489,23 @@ export const Constants = {
|
||||
attendance_status: ["present", "absent", "late", "excused"],
|
||||
ledger_category: ["tuition", "late_pickup", "activity", "other"],
|
||||
ledger_kind: ["charge", "payment"],
|
||||
plan_goal_status: [
|
||||
"not_started",
|
||||
"in_progress",
|
||||
"met",
|
||||
"not_met",
|
||||
"discontinued",
|
||||
],
|
||||
plan_meeting_type: [
|
||||
"initial",
|
||||
"annual_review",
|
||||
"reevaluation",
|
||||
"eligibility",
|
||||
"amendment",
|
||||
"other",
|
||||
],
|
||||
plan_status: ["draft", "active", "archived"],
|
||||
plan_type: ["iep", "section_504"],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
Reference in New Issue
Block a user