- Migration: student role, students.user_id, grade_categories/grade_scale (school defaults + per-class override), assignments, grades + RLS - Classes area (nav): list/create classes, class page with Gradebook / Roster / Grading setup tabs; teachers see their classes, admins all - Weighted-category grade calc -> letter scale (shared helper) - Roster assignment (admin), assignment + grade entry (teacher/admin) - Student profile: view-only Grades tab (parents/students) - Portal access: create parent OR student logins linked to the student Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
2.4 KiB
TypeScript
53 lines
2.4 KiB
TypeScript
import { createServerFn } from "@tanstack/react-start";
|
|
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";
|
|
|
|
type Role = "admin" | "teacher" | "parent" | "student";
|
|
|
|
// Admin-only: create a user account (service-role, server-side), set its role,
|
|
// and optionally link it to a student (for parent hand-off).
|
|
export const createUserFn = createServerFn({ method: "POST" })
|
|
.middleware([requireSupabaseAuth])
|
|
.validator((d: { fullName: string; email: string; password: string; role: Role; linkStudentId?: string }) => d)
|
|
.handler(async ({ data, context }) => {
|
|
const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
|
|
const callerId = (context as { userId: string }).userId;
|
|
|
|
const { data: adminRow } = await supabaseAdmin
|
|
.from("user_roles").select("role").eq("user_id", callerId).eq("role", "admin").maybeSingle();
|
|
if (!adminRow) throw new Error("Only admins can add users.");
|
|
|
|
const email = data.email.trim().toLowerCase();
|
|
if (!email || !data.fullName.trim()) throw new Error("Name and email are required.");
|
|
|
|
const { data: created, error } = await supabaseAdmin.auth.admin.createUser({
|
|
email,
|
|
password: data.password,
|
|
email_confirm: true,
|
|
user_metadata: { full_name: data.fullName.trim() },
|
|
});
|
|
if (error) throw new Error(error.message);
|
|
const uid = created.user?.id;
|
|
if (!uid) throw new Error("User creation failed.");
|
|
|
|
// The on_auth_user_created trigger assigns 'parent' by default; adjust to the chosen role.
|
|
if (data.role !== "parent") {
|
|
await supabaseAdmin.from("user_roles").delete().eq("user_id", uid).eq("role", "parent");
|
|
}
|
|
const { error: rErr } = await supabaseAdmin
|
|
.from("user_roles").upsert({ user_id: uid, role: data.role }, { onConflict: "user_id,role" });
|
|
if (rErr) throw new Error(rErr.message);
|
|
|
|
if (data.linkStudentId && data.role === "parent") {
|
|
const { error: lErr } = await supabaseAdmin
|
|
.from("parent_students").upsert({ parent_id: uid, student_id: data.linkStudentId }, { onConflict: "parent_id,student_id" });
|
|
if (lErr) throw new Error(lErr.message);
|
|
}
|
|
if (data.linkStudentId && data.role === "student") {
|
|
const { error: sErr } = await supabaseAdmin
|
|
.from("students").update({ user_id: uid }).eq("id", data.linkStudentId);
|
|
if (sErr) throw new Error(sErr.message);
|
|
}
|
|
|
|
return { id: uid, email };
|
|
});
|