From 07a53798d9de14d32f7d266d266f99bb67ddf3f9 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 7 Aug 2026 04:15:33 +0000 Subject: [PATCH] Modified by www.SourceFiles.app --- src/routes/_authenticated/dashboard.tsx | 126 ++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/routes/_authenticated/dashboard.tsx 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 ( +
+

Welcome back

+

Signed in as {role}

+ +
+ + + +
+ + {planAlerts && planAlerts.length > 0 && ( +
+

+ 504 / IEP compliance needing attention +

+
    + {planAlerts.slice(0, 8).map((a) => ( +
  • + + {complianceState(a.date) === "overdue" && } + {a.name} + · {a.kind} + + {formatDate(a.date)} +
  • + ))} +
+ {planAlerts.length > 8 && ( + + View all {planAlerts.length} alerts + + )} +
+ )} + +
+

Upcoming on the calendar

+ {upcoming && upcoming.length > 0 ? ( +
    + {upcoming.map((e) => ( +
  • + {e.title} + {new Date(e.date).toLocaleDateString()} +
  • + ))} +
+ ) : ( +

No upcoming events.

+ )} +
+
+ ); +} + +function Stat({ icon: Icon, label, value }: { icon: typeof Users; label: string; value: number | string }) { + return ( +
+ +
{value}
+
{label}
+
+ ); +}