import { useState, useEffect, useRef } from "react"; import { LayoutDashboard, Users, BookOpen, CalendarDays, Grid3X3, Building2, FileText, LogOut, Bell, ChevronDown, Plus, Search, Download, Edit, Trash2, Eye, AlertCircle, CheckCircle, Clock, ChevronRight, X, Menu, Filter, BarChart3, GraduationCap, BookMarked, Layers, Settings } from "lucide-react"; // ─── SEED DATA ─────────────────────────────────────────────────────────────── const BRANCHES = [ { id: 1, code: "CE", name: "Civil Engineering", lateral: false }, { id: 2, code: "CE-LE", name: "Civil Engineering (Lateral Entry)", lateral: true }, { id: 3, code: "PCE", name: "Petro Chemical Engineering", lateral: false }, { id: 4, code: "PT", name: "Paint Technology", lateral: false }, ]; const SEMESTERS = [ { id: 1, no: 1, type: "ODD", year: "2025-26" }, { id: 2, no: 2, type: "EVEN", year: "2025-26" }, { id: 3, no: 3, type: "ODD", year: "2025-26" }, { id: 4, no: 4, type: "EVEN", year: "2025-26" }, { id: 5, no: 5, type: "ODD", year: "2025-26" }, { id: 6, no: 6, type: "EVEN", year: "2025-26" }, ]; const FACULTY = [ { id: 1, name: "Dr. R. K. Sharma", designation: "Principal", dept: "Admin", email: "rksharma@poly.edu", maxPeriodsWeek: 10, active: true }, { id: 2, name: "Prof. A. Mehta", designation: "H.O.D", dept: "Civil Engineering", email: "amehta@poly.edu", maxPeriodsWeek: 20, active: true }, { id: 3, name: "Mr. S. Patel", designation: "Lecturer", dept: "Civil Engineering", email: "spatel@poly.edu", maxPeriodsWeek: 30, active: true }, { id: 4, name: "Ms. P. Gupta", designation: "Lecturer", dept: "Civil Engineering", email: "pgupta@poly.edu", maxPeriodsWeek: 30, active: true }, { id: 5, name: "Mr. K. Verma", designation: "Lecturer", dept: "Petro Chemical", email: "kverma@poly.edu", maxPeriodsWeek: 30, active: true }, { id: 6, name: "Ms. N. Singh", designation: "Lecturer", dept: "Petro Chemical", email: "nsingh@poly.edu", maxPeriodsWeek: 30, active: true }, { id: 7, name: "Mr. D. Rao", designation: "Lecturer", dept: "Paint Technology", email: "drao@poly.edu", maxPeriodsWeek: 30, active: true }, { id: 8, name: "Ms. R. Joshi", designation: "Instructor", dept: "Civil Engineering", email: "rjoshi@poly.edu", maxPeriodsWeek: 28, active: true }, { id: 9, name: "Mr. T. Kumar", designation: "Workshop Superintendent", dept: "Workshop", email: "tkumar@poly.edu", maxPeriodsWeek: 25, active: true }, { id: 10, name: "Ms. S. Agarwal", designation: "Lecturer", dept: "Paint Technology", email: "sagarwal@poly.edu", maxPeriodsWeek: 30, active: true }, ]; const SUBJECTS = [ { id: 1, code: "CE301", name: "Structural Engineering", type: "Theory", credits: 4, periodsWeek: 4, branch: 1, sem: 3 }, { id: 2, code: "CE302", name: "Fluid Mechanics", type: "Theory", credits: 3, periodsWeek: 3, branch: 1, sem: 3 }, { id: 3, code: "CE303", name: "Surveying Lab", type: "Practical", credits: 2, periodsWeek: 4, branch: 1, sem: 3 }, { id: 4, code: "CE304", name: "Engineering Drawing", type: "Practical", credits: 2, periodsWeek: 4, branch: 1, sem: 3 }, { id: 5, code: "CE305", name: "Concrete Technology", type: "Theory", credits: 3, periodsWeek: 3, branch: 1, sem: 3 }, { id: 6, code: "PCE301", name: "Chemical Process Calc", type: "Theory", credits: 4, periodsWeek: 4, branch: 3, sem: 3 }, { id: 7, code: "PCE302", name: "Petroleum Refining", type: "Theory", credits: 3, periodsWeek: 3, branch: 3, sem: 3 }, { id: 8, code: "PCE303", name: "Chemistry Lab", type: "Lab", credits: 2, periodsWeek: 4, branch: 3, sem: 3 }, { id: 9, code: "PT301", name: "Pigments & Dyes", type: "Theory", credits: 3, periodsWeek: 3, branch: 4, sem: 3 }, { id: 10, code: "PT302", name: "Paint Formulation Lab", type: "Practical", credits: 2, periodsWeek: 4, branch: 4, sem: 3 }, ]; const ROOMS = [ { id: 1, number: "LH-101", type: "Lecture", capacity: 60, active: true }, { id: 2, number: "LH-102", type: "Lecture", capacity: 60, active: true }, { id: 3, number: "LAB-201", type: "Lab", capacity: 30, active: true }, { id: 4, number: "LAB-202", type: "Lab", capacity: 30, active: true }, { id: 5, number: "WS-01", type: "Workshop", capacity: 40, active: true }, { id: 6, number: "DH-01", type: "Drawing Hall", capacity: 50, active: true }, ]; const TIME_SLOTS = [ { id: 1, label: "Period 1", start: "10:00", end: "11:00", isBreak: false }, { id: 2, label: "Period 2", start: "11:00", end: "12:00", isBreak: false }, { id: 3, label: "Period 3", start: "12:00", end: "13:00", isBreak: false }, { id: 4, label: "Lunch", start: "13:00", end: "14:00", isBreak: true }, { id: 5, label: "Period 4", start: "14:00", end: "15:00", isBreak: false }, { id: 6, label: "Period 5", start: "15:00", end: "16:00", isBreak: false }, { id: 7, label: "Period 6", start: "16:00", end: "17:00", isBreak: false }, ]; const DAYS = ["MON", "TUE", "WED", "THU", "FRI", "SAT"]; const INITIAL_TIMETABLE = [ { id:1, branch:1, sem:3, subject:1, faculty:3, room:1, slot:1, day:"MON", year:"2025-26" }, { id:2, branch:1, sem:3, subject:2, faculty:4, room:1, slot:2, day:"MON", year:"2025-26" }, { id:3, branch:1, sem:3, subject:5, faculty:3, room:1, slot:3, day:"MON", year:"2025-26" }, { id:4, branch:1, sem:3, subject:3, faculty:8, room:3, slot:5, day:"MON", year:"2025-26" }, { id:5, branch:1, sem:3, subject:1, faculty:3, room:1, slot:1, day:"TUE", year:"2025-26" }, { id:6, branch:1, sem:3, subject:4, faculty:4, room:6, slot:2, day:"TUE", year:"2025-26" }, { id:7, branch:1, sem:3, subject:2, faculty:4, room:1, slot:5, day:"WED", year:"2025-26" }, { id:8, branch:1, sem:3, subject:5, faculty:3, room:1, slot:6, day:"WED", year:"2025-26" }, { id:9, branch:1, sem:3, subject:3, faculty:8, room:3, slot:1, day:"THU", year:"2025-26" }, { id:10, branch:1, sem:3, subject:1, faculty:3, room:1, slot:3, day:"FRI", year:"2025-26" }, ]; const EXAMS = [ { id:1, name:"First Sessional", branch:1, sem:3, subject:1, date:"2026-03-10", start:"10:00", end:"12:00", room:1 }, { id:2, name:"First Sessional", branch:1, sem:3, subject:2, date:"2026-03-11", start:"10:00", end:"12:00", room:1 }, { id:3, name:"Second Sessional", branch:1, sem:3, subject:1, date:"2026-04-15", start:"10:00", end:"12:00", room:2 }, { id:4, name:"Third Sessional", branch:1, sem:3, subject:1, date:"2026-05-20", start:"10:00", end:"12:00", room:2 }, ]; const USERS_DB = [ { username: "admin", password: "admin123", role: "super_admin", name: "Super Admin", facultyId: null }, { username: "faculty", password: "faculty123", role: "faculty", name: "Mr. S. Patel", facultyId: 3 }, { username: "hod", password: "hod123", role: "dept_admin", name: "Prof. A. Mehta", facultyId: 2 }, ]; // ─── HELPERS ───────────────────────────────────────────────────────────────── const typeColor = { Theory: "#2563eb", Practical: "#16a34a", Lab: "#16a34a", Workshop: "#ea580c" }; const typeBg = { Theory: "#dbeafe", Practical: "#dcfce7", Lab: "#dcfce7", Workshop: "#ffedd5" }; function getSubject(id) { return SUBJECTS.find(s => s.id === id); } function getFaculty(id) { return FACULTY.find(f => f.id === id); } function getRoom(id) { return ROOMS.find(r => r.id === id); } // ─── COMPONENTS ────────────────────────────────────────────────────────────── function Badge({ children, color = "#2563eb", bg = "#dbeafe" }) { return ( {children} ); } function StatCard({ icon: Icon, label, value, sub, accent }) { return (
{value}
{label}
{sub &&
{sub}
}
); } // ─── LOGIN ─────────────────────────────────────────────────────────────────── function LoginPage({ onLogin }) { const [u, setU] = useState("admin"); const [p, setP] = useState("admin123"); const [err, setErr] = useState(""); const [loading, setLoading] = useState(false); const handle = () => { setLoading(true); setErr(""); setTimeout(() => { const user = USERS_DB.find(x => x.username === u && x.password === p); if (user) onLogin(user); else { setErr("Invalid credentials. Try admin/admin123"); setLoading(false); } }, 800); }; return (
{/* BG pattern */}
Timetable ERP
Classroom Management Portal
setU(e.target.value)} style={{ width: "100%", marginTop: 6, padding: "11px 14px", border: "1.5px solid #e2e8f0", borderRadius: 10, fontSize: 14, outline: "none", background: "#f8fafc", boxSizing: "border-box", transition: "border 0.2s" }} onFocus={e => e.target.style.borderColor = "#0ea5e9"} onBlur={e => e.target.style.borderColor = "#e2e8f0"} />
setP(e.target.value)} onKeyDown={e => e.key === "Enter" && handle()} style={{ width: "100%", marginTop: 6, padding: "11px 14px", border: "1.5px solid #e2e8f0", borderRadius: 10, fontSize: 14, outline: "none", background: "#f8fafc", boxSizing: "border-box" }} onFocus={e => e.target.style.borderColor = "#0ea5e9"} onBlur={e => e.target.style.borderColor = "#e2e8f0"} />
{err &&
{err}
}
Demo: admin/admin123 · faculty/faculty123 · hod/hod123
); } // ─── DASHBOARD ─────────────────────────────────────────────────────────────── function Dashboard({ timetable, user }) { const conflicts = []; const today = new Date(); const upcoming = EXAMS.filter(e => new Date(e.date) > today).slice(0, 3); const activeEntries = timetable.length; const activeFaculty = [...new Set(timetable.map(t => t.faculty))].length; return (

Dashboard

Academic Year 2025–26 | ODD Semester

{/* Upcoming Exams */}

Upcoming Exams

{upcoming.map(ex => { const sub = getSubject(ex.subject); const br = BRANCHES.find(b => b.id === ex.branch); return (
{new Date(ex.date).getDate()} {new Date(ex.date).toLocaleString("default", { month: "short" }).toUpperCase()}
{sub?.name}
{ex.name} · {br?.code} Sem-3
{ex.start}
); })}
{/* Branch Overview */}

Branch Status

{BRANCHES.map(br => { const entries = timetable.filter(t => t.branch === br.id).length; const pct = Math.min(100, Math.round((entries / 18) * 100)); return (
{br.code} {entries} slots
); })}
{/* Faculty Workload */}

Faculty Workload (Periods/Week)

{FACULTY.slice(0, 10).map(f => { const periods = timetable.filter(t => t.faculty === f.id).length; const pct = Math.min(100, Math.round((periods / f.maxPeriodsWeek) * 100)); const color = pct > 80 ? "#ef4444" : pct > 60 ? "#f59e0b" : "#10b981"; return (
{f.name.split(" ").slice(-1)[0]}
{periods}p
); })}
); } // ─── TIMETABLE GRID ────────────────────────────────────────────────────────── function TimetableGrid({ timetable, setTimetable, user }) { const [selBranch, setSelBranch] = useState(1); const [selSem, setSelSem] = useState(3); const [dragEntry, setDragEntry] = useState(null); const [hovCell, setHovCell] = useState(null); const [modal, setModal] = useState(null); // { day, slot } const [form, setForm] = useState({ subject: "", faculty: "", room: "" }); const canEdit = ["super_admin", "admin", "dept_admin"].includes(user.role); const filtered = timetable.filter(t => t.branch === selBranch && t.sem === selSem); const getCell = (day, slotId) => filtered.find(t => t.day === day && t.slot === slotId); const openAdd = (day, slotId) => { if (!canEdit) return; setModal({ day, slot: slotId }); setForm({ subject: "", faculty: "", room: "" }); }; const saveEntry = () => { if (!form.subject || !form.faculty) return; // Conflict check const facConflict = timetable.find(t => t.faculty === +form.faculty && t.slot === modal.slot && t.day === modal.day && !(t.branch === selBranch && t.sem === selSem)); const roomConflict = form.room ? timetable.find(t => t.room === +form.room && t.slot === modal.slot && t.day === modal.day && !(t.branch === selBranch && t.sem === selSem)) : false; if (facConflict) { alert("⚠️ Faculty clash detected! This faculty is already scheduled at this time."); return; } if (roomConflict) { alert("⚠️ Room clash detected! This room is already booked at this time."); return; } const newEntry = { id: Date.now(), branch: selBranch, sem: selSem, subject: +form.subject, faculty: +form.faculty, room: +form.room || null, slot: modal.slot, day: modal.day, year: "2025-26" }; setTimetable(prev => [...prev, newEntry]); setModal(null); }; const removeEntry = (id) => { setTimetable(prev => prev.filter(t => t.id !== id)); }; const handleDrop = (day, slotId) => { if (!dragEntry || !canEdit) return; const slot = TIME_SLOTS.find(s => s.id === slotId); if (slot?.isBreak) return; setTimetable(prev => prev.map(t => t.id === dragEntry.id ? { ...t, day, slot: slotId } : t)); setDragEntry(null); setHovCell(null); }; const semList = SEMESTERS.filter(s => { if (BRANCHES.find(b => b.id === selBranch)?.lateral) return s.no >= 3; return true; }); return (

Timetable Grid

Click empty cells to add · Drag entries to move · Delete with ×

{DAYS.map(d => )} {TIME_SLOTS.map((slot, si) => ( {DAYS.map(day => { const entry = getCell(day, slot.id); const isHov = hovCell?.day === day && hovCell?.slot === slot.id; if (slot.isBreak) return ( ); const sub = entry ? getSubject(entry.subject) : null; const fac = entry ? getFaculty(entry.faculty) : null; return ( ); })} ))}
TIME{d}
{slot.label}
{slot.start}–{slot.end}
Lunch Break { e.preventDefault(); setHovCell({ day, slot: slot.id }); }} onDrop={() => handleDrop(day, slot.id)} onDragLeave={() => setHovCell(null)} onClick={() => !entry && openAdd(day, slot.id)} > {entry ? (
setDragEntry(entry)} style={{ background: typeBg[sub?.type] || "#f0f9ff", border: `1.5px solid ${typeColor[sub?.type] || "#0ea5e9"}`, borderRadius: 8, padding: "7px 8px", cursor: canEdit ? "grab" : "default", position: "relative", opacity: dragEntry?.id === entry.id ? 0.5 : 1 }}>
{sub?.name}
{fac?.name.split(" ").slice(-2).join(" ")}
{sub?.code}
{canEdit && ( )}
) : (
)}
{/* Legend */}
{Object.entries(typeColor).map(([type, color]) => (
{type}
))}
{/* Add Entry Modal */} {modal && (

Add Class Entry

{modal.day} · {TIME_SLOTS.find(s => s.id === modal.slot)?.label}

)}
); } // ─── FACULTY MGMT ───────────────────────────────────────────────────────────── function FacultyManagement({ timetable, user }) { const [search, setSearch] = useState(""); const [faculty, setFaculty] = useState(FACULTY); const [modal, setModal] = useState(null); const [form, setForm] = useState({ name: "", designation: "Lecturer", dept: "", email: "", maxPeriodsWeek: 30 }); const canEdit = ["super_admin", "admin"].includes(user.role); const filtered = faculty.filter(f => f.name.toLowerCase().includes(search.toLowerCase()) || f.dept.toLowerCase().includes(search.toLowerCase())); const openAdd = () => { setForm({ name: "", designation: "Lecturer", dept: "", email: "", maxPeriodsWeek: 30 }); setModal("add"); }; const save = () => { if (modal === "add") setFaculty(prev => [...prev, { ...form, id: Date.now(), active: true, maxPeriodsWeek: +form.maxPeriodsWeek }]); setModal(null); }; return (

Faculty Management

{faculty.length} faculty members registered

setSearch(e.target.value)} placeholder="Search faculty..." style={{ ...selStyle, paddingLeft: 36, width: 220 }} />
{canEdit && }
{["Name", "Designation", "Department", "Email", "Max Periods/Wk", "Assigned", "Status", ""].map(h => ( ))} {filtered.map((f, i) => { const assigned = timetable.filter(t => t.faculty === f.id).length; const overload = assigned > f.maxPeriodsWeek; return ( e.currentTarget.style.background = "#fafffe"} onMouseLeave={e => e.currentTarget.style.background = ""}> ); })}
{h}
{f.name.split(" ").map(w => w[0]).join("").slice(0, 2)}
{f.name}
{f.designation} {f.dept} {f.email} {f.maxPeriodsWeek} {assigned}
{f.active ? "Active" : "Inactive"}
{canEdit && }
{modal && (

{modal === "add" ? "Add Faculty" : "Edit Faculty"}

{[["Name","name","text"],["Department","dept","text"],["Email","email","email"]].map(([l, k, t]) => (
setForm(f => ({ ...f, [k]: e.target.value }))} style={{ ...selStyle, width: "100%", marginTop: 6, boxSizing: "border-box" }} />
))}
setForm(f => ({ ...f, maxPeriodsWeek: e.target.value }))} style={{ ...selStyle, width: "100%", marginTop: 6, boxSizing: "border-box" }} />
)}
); } // ─── SUBJECTS ──────────────────────────────────────────────────────────────── function SubjectsPage({ user }) { const [selBranch, setSelBranch] = useState(1); const [selSem, setSelSem] = useState(3); const filtered = SUBJECTS.filter(s => s.branch === selBranch && s.sem === selSem); return (

Subject Management

Curriculum configuration per branch & semester

{filtered.length ? filtered.map(s => (
{s.code}
{s.name}
{s.type}
{s.credits}
Credits
{s.periodsWeek}
Periods/Wk
)) : (
No subjects configured for this combination
)}
); } // ─── EXAM SCHEDULE ─────────────────────────────────────────────────────────── function ExamSchedule() { const [exams, setExams] = useState(EXAMS); const [modal, setModal] = useState(false); const [form, setForm] = useState({ name: "First Sessional", branch: 1, subject: 1, date: "", start: "10:00", end: "12:00", room: 1 }); const save = () => { setExams(prev => [...prev, { ...form, id: Date.now(), branch: +form.branch, subject: +form.subject, room: +form.room }]); setModal(false); }; return (

Examination Schedule

Sessional exam management & calendar

{["First Sessional", "Second Sessional", "Third Sessional"].map(sessional => { const sessExams = exams.filter(e => e.name === sessional); return (

{sessional}

{sessExams.length} exams
{sessExams.length ? (
{sessExams.map(ex => { const sub = getSubject(ex.subject); const br = BRANCHES.find(b => b.id === ex.branch); const room = getRoom(ex.room); const d = new Date(ex.date); return (
{d.getDate()} {d.toLocaleString("default",{month:"short"}).toUpperCase()}
{sub?.name}
{br?.code} · {room?.number}
{ex.start} – {ex.end}
); })}
) :
No exams scheduled
}
); })} {modal && (

Schedule Exam

setForm(f=>({...f,date:e.target.value}))} style={{...selStyle,width:"100%",marginTop:6,boxSizing:"border-box"}} />
setForm(f=>({...f,start:e.target.value}))} style={{...selStyle,width:"100%",marginTop:6,boxSizing:"border-box"}} />
setForm(f=>({...f,end:e.target.value}))} style={{...selStyle,width:"100%",marginTop:6,boxSizing:"border-box"}} />
)}
); } // ─── CLASSROOMS ────────────────────────────────────────────────────────────── function ClassroomsPage() { const typeColors = { Lecture: ["#0369a1","#e0f2fe"], Lab: ["#166534","#dcfce7"], Workshop: ["#c2410c","#ffedd5"], "Drawing Hall": ["#6d28d9","#ede9fe"] }; return (

Classroom & Resource Management

{ROOMS.filter(r=>r.active).length} active rooms registered

{ROOMS.map(r => { const [tc, bc] = typeColors[r.type] || ["#374151","#f1f5f9"]; return (
{r.active ? "Active" : "Inactive"}
{r.number}
{r.type}
Capacity: {r.capacity}
); })}
); } // ─── REPORTS ───────────────────────────────────────────────────────────────── function ReportsPage({ timetable }) { const downloadCSV = (name, rows, headers) => { const csv = [headers.join(","), ...rows].join("\n"); const b = new Blob([csv],{type:"text/csv"}); const a = document.createElement("a"); a.href=URL.createObjectURL(b); a.download=`${name}.csv`; a.click(); }; const reports = [ { title: "Branch Timetable", icon: Grid3X3, color: "#0ea5e9", bg: "#e0f2fe", desc: "Complete timetable per branch and semester", action: () => { const rows = timetable.map(t => [ BRANCHES.find(b=>b.id===t.branch)?.code, `Sem ${t.sem}`, t.day, TIME_SLOTS.find(s=>s.id===t.slot)?.label, getSubject(t.subject)?.name, getFaculty(t.faculty)?.name, getRoom(t.room)?.number||"N/A" ].join(",")); downloadCSV("branch_timetable", rows, ["Branch","Semester","Day","Slot","Subject","Faculty","Room"]); } }, { title: "Faculty Workload", icon: BarChart3, color: "#8b5cf6", bg: "#ede9fe", desc: "Periods assigned per faculty per week", action: () => { const rows = FACULTY.map(f => { const periods = timetable.filter(t=>t.faculty===f.id).length; return [f.name, f.designation, f.dept, periods, f.maxPeriodsWeek, periods > f.maxPeriodsWeek ? "OVERLOAD" : "OK"].join(","); }); downloadCSV("faculty_workload", rows, ["Name","Designation","Dept","Assigned Periods","Max Periods","Status"]); } }, { title: "Exam Schedule", icon: CalendarDays, color: "#f59e0b", bg: "#fef3c7", desc: "All sessional examination dates and rooms", action: () => { const rows = EXAMS.map(e => [e.name, BRANCHES.find(b=>b.id===e.branch)?.code, getSubject(e.subject)?.name, e.date, e.start, e.end, getRoom(e.room)?.number].join(",")); downloadCSV("exam_schedule", rows, ["Sessional","Branch","Subject","Date","Start","End","Room"]); } }, { title: "Subject Coverage", icon: BookMarked, color: "#10b981", bg: "#dcfce7", desc: "Branch-wise subject allocation report", action: () => { const rows = SUBJECTS.map(s => [BRANCHES.find(b=>b.id===s.branch)?.code, `Sem ${s.sem}`, s.code, s.name, s.type, s.credits, s.periodsWeek].join(",")); downloadCSV("subject_coverage", rows, ["Branch","Semester","Code","Subject","Type","Credits","Periods/Wk"]); } }, { title: "Room Utilization", icon: Building2, color: "#ef4444", bg: "#fee2e2", desc: "Classroom usage statistics", action: () => { const rows = ROOMS.map(r => { const used = timetable.filter(t=>t.room===r.id).length; const total = DAYS.length * TIME_SLOTS.filter(s=>!s.isBreak).length; return [r.number, r.type, r.capacity, used, total, `${Math.round(used/total*100)}%`].join(","); }); downloadCSV("room_utilization", rows, ["Room","Type","Capacity","Slots Used","Total Slots","Utilization"]); } }, { title: "Full Timetable Export", icon: FileText, color: "#64748b", bg: "#f1f5f9", desc: "Complete raw data export (all entries)", action: () => { const rows = timetable.map(t=>[t.id,BRANCHES.find(b=>b.id===t.branch)?.code,t.sem,t.day,TIME_SLOTS.find(s=>s.id===t.slot)?.label,getSubject(t.subject)?.name||t.subject,getFaculty(t.faculty)?.name||t.faculty,getRoom(t.room)?.number||""].join(",")); downloadCSV("full_timetable", rows, ["ID","Branch","Sem","Day","Slot","Subject","Faculty","Room"]); } } ]; return (

Reports & Export

Download reports in CSV format

{reports.map(r => (
{r.title}
{r.desc}
))}
); } // ─── SHARED STYLES ──────────────────────────────────────────────────────────── const selStyle = { padding: "8px 14px", border: "1.5px solid #e2e8f0", borderRadius: 8, fontSize: 13, outline: "none", background: "#fff", color: "#374151", cursor: "pointer" }; const lblStyle = { fontSize: 11, fontWeight: 700, color: "#374151", letterSpacing: 0.6, textTransform: "uppercase" }; // ─── SIDEBAR ────────────────────────────────────────────────────────────────── const NAV = [ { id: "dashboard", label: "Dashboard", icon: LayoutDashboard }, { id: "timetable", label: "Timetable Grid", icon: Grid3X3 }, { id: "faculty", label: "Faculty", icon: Users }, { id: "subjects", label: "Subjects", icon: BookOpen }, { id: "exams", label: "Exam Schedule", icon: CalendarDays }, { id: "rooms", label: "Classrooms", icon: Building2 }, { id: "reports", label: "Reports", icon: FileText }, ]; // ─── MAIN APP ───────────────────────────────────────────────────────────────── export default function App() { const [user, setUser] = useState(null); const [page, setPage] = useState("dashboard"); const [timetable, setTimetable] = useState(INITIAL_TIMETABLE); const [sideCollapsed, setSideCollapsed] = useState(false); const [notifOpen, setNotifOpen] = useState(false); if (!user) return ; const roleLabel = { super_admin: "Super Admin", admin: "Admin", dept_admin: "Dept. Admin", faculty: "Faculty", student: "Student" }; return (
{/* SIDEBAR */}
{!sideCollapsed &&
TimetableERP
Classroom Management
}
{!sideCollapsed && (
{user.name.split(" ").map(w=>w[0]).join("").slice(0,2)}
{user.name}
{roleLabel[user.role]}
)}
{/* MAIN AREA */}
{/* TOPBAR */}
AY 2025–26 · ODD Semester
{notifOpen && (
Notifications
{[ { msg: "First Sessional starts March 10", type: "warn" }, { msg: "CE Sem-3 timetable updated", type: "info" }, { msg: "Faculty S. Patel approaching workload limit", type: "warn" }, ].map((n, i) => (
{n.msg}
))}
)}
{/* PAGE CONTENT */}
{page === "dashboard" && } {page === "timetable" && } {page === "faculty" && } {page === "subjects" && } {page === "exams" && } {page === "rooms" && } {page === "reports" && }
{/* Click outside notif */} {notifOpen &&
setNotifOpen(false)} />}
); }