diff --git a/src/routes/_authenticated/dashboard.tsx b/src/routes/_authenticated/dashboard.tsx new file mode 100644 index 0000000..38cb1f5 --- /dev/null +++ b/src/routes/_authenticated/dashboard.tsx @@ -0,0 +1,126 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; +import { useAuth, highestRole } from "@/hooks/use-auth"; +import { useQuery } from "@tanstack/react-query"; +import { supabase } from "@/integrations/supabase/client"; +import { Users, ClipboardCheck, Receipt, CalendarDays, ClipboardList, AlertTriangle } from "lucide-react"; +import { COMPLIANCE_CLASS, complianceState, formatDate } from "@/lib/plans"; + +export const Route = createFileRoute("/_authenticated/dashboard")({ + head: () => ({ meta: [{ title: "Dashboard — School Portal" }] }), + component: Dashboard, +}); + +function Dashboard() { + const { user, roles } = useAuth(); + const role = highestRole(roles); + + const { data: studentCount } = useQuery({ + queryKey: ["students-count"], + queryFn: async () => { + const { count } = await supabase.from("students").select("*", { count: "exact", head: true }); + return count ?? 0; + }, + }); + + const { data: todayAttendance } = useQuery({ + queryKey: ["attendance-today"], + queryFn: async () => { + const today = new Date().toISOString().slice(0, 10); + const { count } = await supabase.from("attendance").select("*", { count: "exact", head: true }).eq("date", today); + return count ?? 0; + }, + }); + + const { data: upcoming } = useQuery({ + queryKey: ["upcoming-events"], + queryFn: async () => { + const today = new Date().toISOString().slice(0, 10); + const { data } = await supabase.from("calendar_events").select("*").gte("date", today).order("date").limit(5); + return data ?? []; + }, + }); + + // RLS scopes this to plans the viewer may see, so it is empty for most users. + const { data: planAlerts } = useQuery({ + queryKey: ["plan-alerts"], + queryFn: async () => { + const { data } = await supabase + .from("student_plans") + .select("id, student_id, next_annual_review_date, next_reevaluation_date, students(first_name, last_name)") + .neq("status", "archived"); + return (data ?? []).flatMap((p) => { + const rows: { key: string; student_id: string; name: string; kind: string; date: string }[] = []; + const name = `${p.students?.first_name ?? ""} ${p.students?.last_name ?? ""}`.trim(); + if (p.next_annual_review_date) rows.push({ key: `${p.id}-ar`, student_id: p.student_id, name, kind: "Annual review", date: p.next_annual_review_date }); + if (p.next_reevaluation_date) rows.push({ key: `${p.id}-re`, student_id: p.student_id, name, kind: "Re-evaluation", date: p.next_reevaluation_date }); + return rows; + }) + .filter((r) => complianceState(r.date) === "overdue" || complianceState(r.date) === "due_soon") + .sort((a, b) => (a.date < b.date ? -1 : 1)); + }, + }); + + return ( +
Signed in as {role}
+ +No upcoming events.
+ )} +