Pratham200Rajbhar
first commit
78013c4
Raw
History Blame Contribute Delete
3.4 kB
"use client";
import React, { useEffect, useState, useCallback } from "react";
import { useRouter } from "next/navigation";
import { Plus, Eye, Pencil } from "lucide-react";
import Link from "next/link";
import api from "@/lib/api";
import GlassPageHeader from "@/components/ui/GlassPageHeader";
import GlassBreadcrumb from "@/components/ui/GlassBreadcrumb";
import GlassTable, { type TableColumn } from "@/components/ui/GlassTable";
import GlassSearch from "@/components/ui/GlassSearch";
import GlassButton from "@/components/ui/GlassButton";
import GlassLoader from "@/components/ui/GlassLoader";
import type { ClassResponse } from "@/types";
export default function ClassesPage(): React.ReactElement {
const router = useRouter();
const [classes, setClasses] = useState<ClassResponse[]>([]);
const [filtered, setFiltered] = useState<ClassResponse[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetch(): Promise<void> {
try { const { data } = await api.get<ClassResponse[]>("/admin/classes"); setClasses(data); setFiltered(data); }
catch { setClasses([]); setFiltered([]); }
finally { setLoading(false); }
}
fetch();
}, []);
const handleSearch = useCallback((q: string) => {
if (!q.trim()) { setFiltered(classes); return; }
const lq = q.toLowerCase();
setFiltered(classes.filter((c) =>
c.name.toLowerCase().includes(lq) ||
c.subject_name.toLowerCase().includes(lq) ||
c.subject_code.toLowerCase().includes(lq)
));
}, [classes]);
const columns: TableColumn<ClassResponse & Record<string, unknown>>[] = [
{ key: "name", header: "Class Name", sortable: true },
{ key: "subject_name", header: "Subject", sortable: true, render: (r) => <span>{String(r.subject_name)}<span className="ml-1 text-xs text-slate-500">({String(r.subject_code)})</span></span> },
{ key: "enrolled_count", header: "Enrolled", render: (r) => <span className="font-mono text-sm">{String(r.enrolled_count)}{r.max_students ? ` / ${String(r.max_students)}` : ""}</span> },
{ key: "classroom_name", header: "Classroom", render: (r) => <span className="text-slate-400 text-sm">{String(r.classroom_name || "—")}</span> },
{
key: "actions", header: "Actions",
render: (row) => (
<div className="flex items-center gap-2">
<Link href={`/admin/classes/${row.id}`} className="glass-btn glass-btn-ghost glass-btn-sm"><Eye size={14} /> View</Link>
<Link href={`/admin/classes/${row.id}/edit`} className="glass-btn glass-btn-ghost glass-btn-sm"><Pencil size={14} /> Edit</Link>
</div>
),
},
];
if (loading) return <GlassLoader text="Loading classes..." />;
return (
<div className="animate-fade-in-up">
<GlassBreadcrumb items={[{ label: "Admin", href: "/admin/dashboard" }, { label: "Classes" }]} />
<GlassPageHeader title="Class Directory" description={`${classes.length} classes registered`}
actions={<GlassButton variant="primary" icon={<Plus size={16} />} onClick={() => router.push("/admin/classes/create")}>Create Class</GlassButton>} />
<div className="mb-6"><GlassSearch placeholder="Search classes..." onSearch={handleSearch} /></div>
<GlassTable columns={columns} data={filtered as (ClassResponse & Record<string, unknown>)[]} emptyMessage="No classes found" />
</div>
);
}