import { useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Loader2, Download } from "lucide-react"; import AiStudyCoach from "@/components/ai/AiStudyCoach"; import AiGradeExplainer from "@/components/ai/AiGradeExplainer"; import { reportsService } from "@/services/reports.service"; const LEVELS = ["all", "A1", "A2", "B1", "B2", "C1", "C2"] as const; function ScoreBadge({ score }: { score: number | null }) { if (score === null || score === undefined) { return ; } const color = score >= 7.5 ? "bg-success/10 text-success" : score >= 6.0 ? "bg-warning/10 text-warning" : "bg-destructive/10 text-destructive"; return ( {score.toFixed(1)} ); } export default function StudentPerformancePage() { const [entityId, setEntityId] = useState("all"); const [level, setLevel] = useState<(typeof LEVELS)[number]>("all"); const [search, setSearch] = useState(""); const { data: filters } = useQuery({ queryKey: ["reports-filters"], queryFn: () => reportsService.filters(), }); const { data, isLoading } = useQuery({ queryKey: ["student-performance", entityId, level, search], queryFn: () => reportsService.studentPerformance({ entity_id: entityId === "all" ? undefined : Number(entityId), level: level === "all" ? undefined : level, search: search || undefined, }), }); const rows = data?.items ?? []; const summary = useMemo(() => { if (!rows.length) return null; const overall = rows .map((r) => r.overall) .filter((v): v is number => v !== null); const avg = overall.length ? overall.reduce((a, b) => a + b, 0) / overall.length : null; return { total: rows.length, avg: avg !== null ? avg.toFixed(1) : "—", topLevel: rows[0]?.level ?? "—", }; }, [rows]); const exportCsv = () => { const header = [ "Student", "Entity", "Level", "Reading", "Listening", "Writing", "Speaking", "Overall", "Attempts", ]; const lines = rows.map((r) => [ r.student_name, r.entity_name, r.level ?? "", r.reading ?? "", r.listening ?? "", r.writing ?? "", r.speaking ?? "", r.overall ?? "", r.attempts_count, ] .map((v) => `"${String(v).replace(/"/g, '""')}"`) .join(","), ); const blob = new Blob([[header.join(","), ...lines].join("\n")], { type: "text/csv", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `student-performance-${new Date().toISOString().slice(0, 10)}.csv`; a.click(); URL.revokeObjectURL(url); }; return (

Student Performance

Track student scores across all IELTS modules.

{summary && (

Students tracked

{summary.total}

Average overall band

{summary.avg}

Top performer level

{summary.topLevel}

)}
setSearch(e.target.value)} className="w-[220px]" />
{isLoading ? (
Loading performance data...
) : rows.length === 0 ? (
No completed attempts match these filters yet.
) : ( Student Entity Level Reading Listening Writing Speaking Overall Attempts AI {rows.map((r) => ( {r.student_name} {r.entity_name || "—"} {r.level ? ( {r.level} ) : ( "—" )} {r.attempts_count} ))}
)}
); }