Terminal
This commit is contained in:
@@ -1,97 +0,0 @@
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";
|
||||
|
||||
export type IntakePayload = {
|
||||
student: {
|
||||
dob: string; gender: string; grade_level: string; preferred_start_date: string;
|
||||
allergies: string; chronic_conditions: string; primary_physician: string; physician_phone: string;
|
||||
home_ed_eval_due: string; previous_schools: string; special_needs: string; portfolio_keeper: string;
|
||||
disciplinary_history: string; custody_agreement: string;
|
||||
dismissal_methods: string[]; dismissal_other: string; backup_transport_plan: string;
|
||||
interests: string; other_info: string; agreement_signed_by: string; agreement_signed_date: string;
|
||||
};
|
||||
guardians: { is_primary: boolean; full_name: string; relationship: string; phone: string; address: string; city: string; state: string; zip: string; email: string; employer: string }[];
|
||||
contacts: { name: string; relationship: string; phone: string; alt_phone: string }[];
|
||||
pickups: { name: string; relationship: string; phone: string; notes: string }[];
|
||||
logins: { website: string; student_login: string; student_password: string; parent_account: string; parent_password: string }[];
|
||||
};
|
||||
|
||||
const orNull = (v: string) => (v && v.trim() ? v.trim() : null);
|
||||
|
||||
// ── Admin: create a one-time intake link for a student ────────────────────────
|
||||
export const createIntakeToken = createServerFn({ method: "POST" })
|
||||
.middleware([requireSupabaseAuth])
|
||||
.validator((d: { studentId: 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 create intake links.");
|
||||
|
||||
const token = crypto.randomUUID().replace(/-/g, "") + crypto.randomUUID().replace(/-/g, "");
|
||||
const expires_at = new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const { error } = await supabaseAdmin.from("intake_tokens").insert({ token, student_id: data.studentId, created_by: callerId, expires_at });
|
||||
if (error) throw new Error(error.message);
|
||||
return { token };
|
||||
});
|
||||
|
||||
// ── Public: validate a token, return the student's name for the form ──────────
|
||||
export const getIntakeToken = createServerFn({ method: "POST" })
|
||||
.validator((d: { token: string }) => d)
|
||||
.handler(async ({ data }) => {
|
||||
const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
|
||||
const { data: row } = await supabaseAdmin.from("intake_tokens").select("student_id, expires_at, used_at").eq("token", data.token).maybeSingle();
|
||||
if (!row) return { status: "invalid" as const };
|
||||
if (row.used_at) return { status: "used" as const };
|
||||
if (new Date(row.expires_at) < new Date()) return { status: "expired" as const };
|
||||
const { data: student } = await supabaseAdmin.from("students").select("first_name, last_name").eq("id", row.student_id).maybeSingle();
|
||||
return { status: "ok" as const, firstName: student?.first_name ?? "", lastName: student?.last_name ?? "" };
|
||||
});
|
||||
|
||||
// ── Public: submit the intake, write to the student's record, mark used ───────
|
||||
export const submitIntake = createServerFn({ method: "POST" })
|
||||
.validator((d: { token: string; payload: IntakePayload }) => d)
|
||||
.handler(async ({ data }) => {
|
||||
const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
|
||||
const { data: row } = await supabaseAdmin.from("intake_tokens").select("id, student_id, expires_at, used_at").eq("token", data.token).maybeSingle();
|
||||
if (!row) throw new Error("This link is not valid.");
|
||||
if (row.used_at) throw new Error("This link has already been used.");
|
||||
if (new Date(row.expires_at) < new Date()) throw new Error("This link has expired.");
|
||||
|
||||
const sid = row.student_id;
|
||||
const s = data.payload.student;
|
||||
const { error: e1 } = await supabaseAdmin.from("students").update({
|
||||
dob: orNull(s.dob), gender: orNull(s.gender), grade_level: orNull(s.grade_level), preferred_start_date: orNull(s.preferred_start_date),
|
||||
allergies: orNull(s.allergies), chronic_conditions: orNull(s.chronic_conditions), primary_physician: orNull(s.primary_physician), physician_phone: orNull(s.physician_phone),
|
||||
home_ed_eval_due: orNull(s.home_ed_eval_due), previous_schools: orNull(s.previous_schools), special_needs: orNull(s.special_needs), portfolio_keeper: orNull(s.portfolio_keeper),
|
||||
disciplinary_history: orNull(s.disciplinary_history), custody_agreement: orNull(s.custody_agreement),
|
||||
dismissal_methods: s.dismissal_methods ?? [], dismissal_other: orNull(s.dismissal_other), backup_transport_plan: orNull(s.backup_transport_plan),
|
||||
interests: orNull(s.interests), other_info: orNull(s.other_info), agreement_signed_by: orNull(s.agreement_signed_by), agreement_signed_date: orNull(s.agreement_signed_date),
|
||||
}).eq("id", sid);
|
||||
if (e1) throw new Error(e1.message);
|
||||
|
||||
// Replace collections with what was submitted (initial intake is the source of truth here).
|
||||
await supabaseAdmin.from("student_guardians").delete().eq("student_id", sid);
|
||||
await supabaseAdmin.from("authorized_pickups").delete().eq("student_id", sid);
|
||||
await supabaseAdmin.from("student_curriculum_logins").delete().eq("student_id", sid);
|
||||
|
||||
const guardianRows = data.payload.guardians.filter((g) => g.full_name.trim()).map((g) => ({
|
||||
student_id: sid, is_primary: g.is_primary, full_name: g.full_name.trim(), relationship: orNull(g.relationship), phone: orNull(g.phone),
|
||||
address: orNull(g.address), city: orNull(g.city), state: orNull(g.state), zip: orNull(g.zip), email: orNull(g.email), employer: orNull(g.employer),
|
||||
}));
|
||||
if (guardianRows.length) { const { error } = await supabaseAdmin.from("student_guardians").insert(guardianRows); if (error) throw new Error(error.message); }
|
||||
|
||||
const pickupRows = [
|
||||
...data.payload.pickups.filter((p) => p.name.trim()).map((p, i) => ({ student_id: sid, kind: "pickup", sort_order: i, name: p.name.trim(), relationship: orNull(p.relationship), phone: orNull(p.phone), alt_phone: null as string | null, notes: orNull(p.notes) })),
|
||||
...data.payload.contacts.filter((c) => c.name.trim()).map((c, i) => ({ student_id: sid, kind: "emergency", sort_order: i, name: c.name.trim(), relationship: orNull(c.relationship), phone: orNull(c.phone), alt_phone: orNull(c.alt_phone), notes: null as string | null })),
|
||||
];
|
||||
if (pickupRows.length) { const { error } = await supabaseAdmin.from("authorized_pickups").insert(pickupRows); if (error) throw new Error(error.message); }
|
||||
|
||||
const loginRows = data.payload.logins.filter((l) => Object.values(l).some((v) => v.trim())).map((l) => ({
|
||||
student_id: sid, website: orNull(l.website), student_login: orNull(l.student_login), student_password: orNull(l.student_password), parent_account: orNull(l.parent_account), parent_password: orNull(l.parent_password),
|
||||
}));
|
||||
if (loginRows.length) { const { error } = await supabaseAdmin.from("student_curriculum_logins").insert(loginRows); if (error) throw new Error(error.message); }
|
||||
|
||||
await supabaseAdmin.from("intake_tokens").update({ used_at: new Date().toISOString() }).eq("id", row.id);
|
||||
return { ok: true };
|
||||
});
|
||||
Reference in New Issue
Block a user