import { useCallback, useState } from "react"; import { Upload, FileSpreadsheet, CheckCircle2, AlertCircle, AlertTriangle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { useBulkCreate, useSendCredentials, useValidateCSV } from "@/hooks/queries/useEntityOnboarding"; import type { BulkCreateResult, CSVValidationReport, EntityStudent } from "@/types"; import { cn } from "@/lib/utils"; import { toast } from "sonner"; export default function BulkStudentUpload() { const [step, setStep] = useState<1 | 2 | 3>(1); const [report, setReport] = useState(null); const [created, setCreated] = useState(null); const [dragOver, setDragOver] = useState(false); const validateMut = useValidateCSV(); const bulkMut = useBulkCreate(); const sendMut = useSendCredentials(); const onFile = useCallback( (file: File | null) => { if (!file || !file.name.toLowerCase().endsWith(".csv")) { toast.error("Please upload a CSV file."); return; } validateMut.mutate(file, { onSuccess: (data) => { setReport(data); setStep(2); }, onError: (e) => toast.error(e instanceof Error ? e.message : "Validation failed"), }); }, [validateMut], ); const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); const f = e.dataTransfer.files[0]; onFile(f ?? null); }; const summary = report ? `${report.valid_count} valid, ${report.error_count} error${report.error_count === 1 ? "" : "s"}, ${report.warning_count} warning${report.warning_count === 1 ? "" : "s"}` : ""; const confirmCreate = () => { bulkMut.mutate( { validate_session_id: report?.validate_session_id }, { onSuccess: (data) => { setCreated(data); setStep(3); toast.success("Accounts created."); }, onError: (e) => toast.error(e instanceof Error ? e.message : "Bulk create failed"), }, ); }; const sendEmails = () => { const ids = created?.accounts.map((a) => a.id) ?? []; if (!ids.length) return; sendMut.mutate(ids, { onSuccess: () => toast.success("Credential emails queued."), onError: (e) => toast.error(e instanceof Error ? e.message : "Send failed"), }); }; const statusIcon = (s: string) => { if (s === "valid") return ; if (s === "error") return ; return ; }; return (

Bulk student upload

CSV import for your entity

{step === 1 && ( Step 1 — Upload CSV Drag and drop a file or click to browse. CSV only.
{ e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={handleDrop} >

Drop your CSV here

)} {step === 2 && report && ( Step 2 — Validation report Review each row before creating accounts.
{summary}
Row# Name Email Status Issue {report.rows.map((r) => ( {r.row_number} {r.student_name} {r.email}
{statusIcon(r.status)} {r.status}
{r.issue ?? "—"}
))}
)} {step === 3 && created && ( Step 3 — Accounts created {created.created_count} new accounts are ready.
Name Email Status {created.accounts.map((a: EntityStudent) => ( {a.name} {a.email} {a.account_status} ))}
)}
); }