Add full-page student intake form matching Bayside paper forms
- Migration: student intake fields (health, academic, dismissal, agreement), student_guardians + student_curriculum_logins tables, extend authorized_pickups (alt_phone, notes, kind) with RLS mirroring existing policies - New /students/new full-page multi-section form (replaces the add dialog) - Students list links to the full page; regenerate Supabase types Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,9 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Plus, ChevronRight } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const Route = createFileRoute("/_authenticated/students")({
|
||||
head: () => ({ meta: [{ title: "Students — School Portal" }] }),
|
||||
@@ -19,7 +13,6 @@ export const Route = createFileRoute("/_authenticated/students")({
|
||||
function StudentsPage() {
|
||||
const { roles } = useAuth();
|
||||
const isAdmin = roles.includes("admin");
|
||||
const qc = useQueryClient();
|
||||
|
||||
const { data: students } = useQuery({
|
||||
queryKey: ["students"],
|
||||
@@ -33,34 +26,6 @@ function StudentsPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const { data: classes } = useQuery({
|
||||
queryKey: ["classes"],
|
||||
queryFn: async () => (await supabase.from("classes").select("*").order("name")).data ?? [],
|
||||
});
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [form, setForm] = useState({ first_name: "", last_name: "", dob: "", class_id: "", allergies: "" });
|
||||
|
||||
const createStudent = useMutation({
|
||||
mutationFn: async () => {
|
||||
const { error } = await supabase.from("students").insert({
|
||||
first_name: form.first_name,
|
||||
last_name: form.last_name,
|
||||
dob: form.dob || null,
|
||||
class_id: form.class_id || null,
|
||||
allergies: form.allergies || null,
|
||||
});
|
||||
if (error) throw error;
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success("Student added");
|
||||
setOpen(false);
|
||||
setForm({ first_name: "", last_name: "", dob: "", class_id: "", allergies: "" });
|
||||
qc.invalidateQueries({ queryKey: ["students"] });
|
||||
},
|
||||
onError: (e: Error) => toast.error(e.message),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-8 max-w-6xl">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
@@ -69,28 +34,9 @@ function StudentsPage() {
|
||||
<p className="text-muted-foreground text-sm">{students?.length ?? 0} students visible to you</p>
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild><Button><Plus className="h-4 w-4 mr-1" /> Add student</Button></DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader><DialogTitle>New student</DialogTitle></DialogHeader>
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div><Label>First name</Label><Input value={form.first_name} onChange={(e) => setForm({ ...form, first_name: e.target.value })} /></div>
|
||||
<div><Label>Last name</Label><Input value={form.last_name} onChange={(e) => setForm({ ...form, last_name: e.target.value })} /></div>
|
||||
</div>
|
||||
<div><Label>Date of birth</Label><Input type="date" value={form.dob} onChange={(e) => setForm({ ...form, dob: e.target.value })} /></div>
|
||||
<div>
|
||||
<Label>Class</Label>
|
||||
<Select value={form.class_id} onValueChange={(v) => setForm({ ...form, class_id: v })}>
|
||||
<SelectTrigger><SelectValue placeholder="Choose class" /></SelectTrigger>
|
||||
<SelectContent>{(classes ?? []).map((c) => <SelectItem key={c.id} value={c.id}>{c.name}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div><Label>Allergies</Label><Input value={form.allergies} onChange={(e) => setForm({ ...form, allergies: e.target.value })} placeholder="None" /></div>
|
||||
<Button className="w-full" disabled={!form.first_name || !form.last_name || createStudent.isPending} onClick={() => createStudent.mutate()}>Create student</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Link to="/students/new">
|
||||
<Button><Plus className="h-4 w-4 mr-1" /> Add student</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user