diff --git a/src/app/admin/students/page.tsx b/src/app/admin/students/page.tsx
index b68e6dc..20d714c 100644
--- a/src/app/admin/students/page.tsx
+++ b/src/app/admin/students/page.tsx
@@ -4,7 +4,7 @@ import { fetchWithAuth } from "@/lib/apiClient";
import { useEffect, useState } from "react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
-import { Search, Download, Flame, RefreshCw, Upload } from "lucide-react"
+import { Search, Download, Flame, RefreshCw, Upload, Trash2 } from "lucide-react"
import { useRouter } from "next/navigation"
import { useRef } from "react"
@@ -25,6 +25,8 @@ export default function StudentsPage() {
const [students, setStudents] = useState([])
const [loading, setLoading] = useState(true)
const [search, setSearch] = useState("")
+ const [selectedIds, setSelectedIds] = useState([])
+ const [deleting, setDeleting] = useState(false)
const fileInputRef = useRef(null)
@@ -45,17 +47,20 @@ export default function StudentsPage() {
const results = await rawRes.json()
alert(`Successfully registered ${results.length} students!`)
- // Download CSV with passwords
- let csvContent = "data:text/csv;charset=utf-8,name,email,password\\n"
+ // Download CSV with passwords using Blob to fix formatting on Windows
+ const csvRows = ["name,email,password"];
results.forEach((row: any) => {
- csvContent += `${row.name},${row.email},${row.password}\\n`
- })
- const encodedUri = encodeURI(csvContent)
- const link = document.createElement("a")
- link.setAttribute("href", encodedUri)
- link.setAttribute("download", "bulk_registered_passwords.csv")
- document.body.appendChild(link)
- link.click()
+ csvRows.push(`${row.name},${row.email},${row.password}`);
+ });
+ const csvContent = csvRows.join("\r\n");
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.setAttribute("href", url);
+ link.setAttribute("download", "bulk_registered_passwords.csv");
+ document.body.appendChild(link);
+ link.click();
+ URL.revokeObjectURL(url);
// Refresh students
const fresh = await fetchWithAuth("/api/admin/students").then(r => r.json())
@@ -68,6 +73,29 @@ export default function StudentsPage() {
}
}
+
+ const handleDeleteSelected = async () => {
+ if (!selectedIds.length) return
+ if (!confirm(`Are you sure you want to delete ${selectedIds.length} students? This cannot be undone.`)) return
+
+ setDeleting(true)
+ try {
+ const res = await fetchWithAuth("/api/admin/students/bulk", {
+ method: "DELETE",
+ body: JSON.stringify({ user_ids: selectedIds })
+ })
+ if (!res.ok) throw new Error("Delete failed")
+
+ setSelectedIds([])
+ const fresh = await fetchWithAuth("/api/admin/students").then(r => r.json())
+ setStudents(fresh)
+ } catch (err: any) {
+ alert("Error deleting students: " + err.message)
+ } finally {
+ setDeleting(false)
+ }
+ }
+
const downloadTemplate = () => {
const template = "data:text/csv;charset=utf-8,name,email,phone,target_exam,tier\\nJohn Doe,john@example.com,9876543210,JEE_Main,Free\\nJane Smith,jane@example.com,9876543211,NEET,Pro"
const encodedUri = encodeURI(template)
@@ -107,14 +135,19 @@ export default function StudentsPage() {
+ {selectedIds.length > 0 && (
+
+ )}
-
@@ -143,6 +176,17 @@ export default function StudentsPage() {