import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line } from 'recharts'; // Define image URLs for students const studentImageMap = { "2031039": "https://i.ibb.co/ynYXPCfk/ASHIKA-RAHMAN-PANNA.png", "2031048": "https://i.ibb.co/qMmspmwd/AUTHORY-BISWAS-BIDDA.png", "2031013": "https://i.ibb.co/G4778Vwd/HOSSAIN-MOHAMMAD-SHIHAB.png", "2031043": "https://i.ibb.co/XrZqTvHq/JANNATUL-MAOUA-PROME.png", "2031032": "https://i.ibb.co/BVc0jwkq/JASMIN-AKTER-MUKTA.png", "2031001": "https://i.ibb.co/q3YsvKkb/KOBIR-HOSSAIN.png", "2031007": "https://i.ibb.co/rK3fVch5/M-D-BELAL-HOSSAIN-BADHON.png", "2031018": "https://i.ibb.co/wrCRDDL1/MD-EMRAN-HOSSAIN.png", "2031011": "https://i.ibb.co/4nYNfzPH/MD-HABIBUL-HASAN.png", "2031006": "https://i.ibb.co/TD8TC105/MD-MASHUK-E-RAHMAN.png", "2031017": "https://i.ibb.co/LDHT2Hvh/MD-NAFIU-UL-HAQUE.png", "2031004": "https://i.ibb.co/XfMNw1sq/MD-NAIMUL-ISLAM.png", "2031016": "https://i.ibb.co/hJCk50F5/MD-OMAYER-HAQUE-KHAN.jpg", "2031005": "https://i.ibb.co/svp64vNR/MD-PARVEJ-HOSSAIN.jpg", "2031009": "https://i.ibb.co/CKwVXN3x/MD-RASHIDUL-ISLAM.png", "2031021": "https://i.ibb.co/BV34DZ7z/MD-SHAHRIAR-HOSSAIN.png", "2031012": "https://i.ibb.co/60X8bXwX/MD-SHANZAD-ALI.png", "2031008": "https://i.ibb.co/LXpDkcJf/MD-TORIKUL-ISLAM-HIMEL.png", "2031003": "https://i.ibb.co/R4JGhKWS/MOHD-TAKIUDDIN.png", "2031023": "https://i.ibb.co/jvsKxC9F/MOST-MAHFUJA-ANAN.png", "2031034": "https://i.ibb.co/WbV5tsr/NAFi-JA-TASNIM-ANANNA.png", "2031041": "https://i.ibb.co/jkdbV5rQ/ORPA-AHMED.png", "2031045": "https://i.ibb.co/d0B8xCH4/RUKAIYA-HOSSAIN.png", "2031030": "https://i.ibb.co/j9p1pBGf/SHAFKAT-TASEEN-SADIYA.png", "2031019": "https://i.ibb.co/h3CLWnb/TUSHAR-BISWAS.png", "2031038": "https://i.ibb.co/PG9s9K3J/UMME-SAMIHA-ISLAM.png", "2031010": "https://i.ibb.co/ynZVb2q1/WASHIM-SHAHED-SOVON.png", "2031024": "https://i.ibb.co/mCNbhMpX/MST-AFROZA-KHATUN24.png", // Specific for 2031024 "2031042": "https://i.postimg.cc/bNJkZxJB/KHADIJA-AKTER.png", // Unique image for Khadija Akter }; const sharedImageUrl = "https://i.ibb.co/BVc0jwkq/JASMIN-AKTER-MUKTA.png"; // JASMIN AKTER MUKTA's image const studentsUsingSharedImage = [ "2031042", // KHADIJA AKTER "2031046", // MAHFUZA KHATUN "2031044", // MAHMUDA JANNAT OWYSHE "2031025", // MEFTAHUL JANNAT "2031036", // MST. AFROZA KHATUN (roll 2031036) "2031040", // MST. ANANNA-AFRIN "2031037", // MST. ANIKA TABASSUM "2031028", // MST. SULTANA PARVIN SHIFA "2031027" // SADIA ISLAM ]; // Manually extracted data from the provided Excel sheet image const studentData = [ { "name": "MEFTAHUL JANNAT", "roll": "2031025", "gpa_1_1": 3.90, "grade_1_1": "A-", "gpa_1_2": 3.737, "grade_1_2": "A-", "gpa_2_1": 3.779, "grade_2_1": "A", "gpa_2_2": 3.852, "grade_2_2": "A", "gpa_3_1": 3.89, "grade_3_1": "B+", }, { "name": "MD. PARVEJ HOSSAIN", "roll": "2031005", "gpa_1_1": 3.74, "grade_1_1": "A-", "gpa_1_2": 3.612, "grade_1_2": "A-", "gpa_2_1": 3.750, "grade_2_1": "A-", "gpa_2_2": 3.886, "grade_2_2": "A", "gpa_3_1": 3.936, "grade_3_1": "A", }, { "name": "MST. AFROZA KHATUN", "roll": "2031036", "gpa_1_1": 3.67, "grade_1_1": "A-", "gpa_1_2": 3.638, "grade_1_2": "A-", "gpa_2_1": 3.605, "grade_2_1": "A-", "gpa_2_2": 3.992, "grade_2_2": "A", "gpa_3_1": 3.85, "grade_3_1": "A", }, { "name": "MOHD. TAKIUDDIN", "roll": "2031003", "gpa_1_1": 3.60, "grade_1_1": "A-", "gpa_1_2": 3.651, "grade_1_2": "A-", "gpa_2_1": 3.605, "grade_2_1": "A-", "gpa_2_2": 3.841, "grade_2_2": "A", "gpa_3_1": 3.566, "grade_3_1": "A-", }, { "name": "SHAFKAT TASEEN SADIYA", "roll": "2031030", // Corrected name here "gpa_1_1": 3.58, "grade_1_1": "A-", "gpa_1_2": 3.579, "grade_1_2": "A-", "gpa_2_1": 3.551, "grade_2_1": "A-", "gpa_2_2": 3.852, "grade_2_2": "A", "gpa_3_1": 3.82, "grade_3_1": "B+", }, { "name": "MST. AFROZA KHATUN", "roll": "2031024", "gpa_1_1": 3.52, "grade_1_1": "A-", "gpa_1_2": 3.737, "grade_1_2": "A-", "gpa_2_1": 3.587, "grade_2_1": "A-", "gpa_2_2": 3.716, "grade_2_2": "A-", "gpa_3_1": 3.92, "grade_3_1": "A", }, { "name": "MST. ANANNA-AFRIN", "roll": "2031040", "gpa_1_1": 3.44, "grade_1_1": "B+", "gpa_1_2": 3.592, "grade_1_2": "A-", "gpa_2_1": 3.640, "grade_2_1": "A-", "gpa_2_2": 3.864, "grade_2_2": "A", "gpa_3_1": 3.91, "grade_3_1": "A", }, { "name": "ORPA AHMED", "roll": "2031041", "gpa_1_1": 3.53, "grade_1_1": "A-", "gpa_1_2": 3.704, "grade_1_2": "A-", "gpa_2_1": 3.552, "grade_2_1": "A-", "gpa_2_2": 3.739, "grade_2_2": "A-", "gpa_3_1": 3.89, "grade_3_1": "A", }, { "name": "MD. SHANZAD ALI", "roll": "2031012", "gpa_1_1": 3.45, "grade_1_1": "B+", "gpa_1_2": 3.566, "grade_1_2": "A-", "gpa_2_1": 3.517, "grade_2_1": "A-", "gpa_2_2": 3.773, "grade_2_2": "A-", "gpa_3_1": 3.91, "grade_3_1": "A", }, { "name": "HOSSAIN MOHAMMAD SHIHAB", "roll": "2031013", "gpa_1_1": 3.43, "grade_1_1": "B+", "gpa_1_2": 3.579, "grade_1_2": "A-", "gpa_2_1": 3.500, "grade_2_1": "A-", "gpa_2_2": 3.795, "grade_2_2": "A-", "gpa_3_1": 3.82, "grade_3_1": "B+", }, { "name": "MD. OMAYER HAQUE KHAN", "roll": "2031016", "gpa_1_1": 3.42, "grade_1_1": "B+", "gpa_1_2": 3.559, "grade_1_2": "A-", "gpa_2_1": 3.483, "grade_2_1": "B+", "gpa_2_2": 3.739, "grade_2_2": "A-", "gpa_3_1": 3.802, "grade_3_1": "A-", }, { "name": "KHADIJA AKTER", "roll": "2031042", "gpa_1_1": 3.36, "grade_1_1": "B+", "gpa_1_2": 3.559, "grade_1_2": "A-", "gpa_2_1": 3.517, "grade_2_1": "A-", "gpa_2_2": 3.682, "grade_2_2": "A-", "gpa_3_1": 3.89, "grade_3_1": "A", }, { "name": "KOBIR HOSSAIN", "roll": "2031001", "gpa_1_1": 3.54, "grade_1_1": "A-", "gpa_1_2": 3.421, "grade_1_2": "B+", "gpa_2_1": 3.517, "grade_2_1": "A-", "gpa_2_2": 3.517, "grade_2_2": "A-", "gpa_3_1": 3.72, "grade_3_1": "A-", }, { "name": "MOST. MAHFUJA ANAN", "roll": "2031023", "gpa_1_1": 3.37, "grade_1_1": "B+", "gpa_1_2": 3.546, "grade_1_2": "A-", "gpa_2_1": 3.360, "grade_2_1": "B+", "gpa_2_2": 3.705, "grade_2_2": "A-", "gpa_3_1": 3.9, "grade_3_1": "A", }, { "name": "SADIA ISLAM", "roll": "2031027", "gpa_1_1": 3.44, "grade_1_1": "B+", "gpa_1_2": 3.382, "grade_1_2": "B+", "gpa_2_1": 3.448, "grade_2_1": "B+", "gpa_2_2": 3.682, "grade_2_2": "A-", "gpa_3_1": 3.82, "grade_3_1": "B+", }, { "name": "WASHIM SHAHED SOVON", "roll": "2031010", "gpa_1_1": 3.21, "grade_1_1": "B", "gpa_1_2": 3.487, "grade_1_2": "B+", "gpa_2_1": 3.430, "grade_2_1": "B+", "gpa_2_2": 3.784, "grade_2_2": "A-", "gpa_3_1": 3.974, "grade_3_1": "A+", }, { "name": "MD. NAFIU-UL-HAQUE", "roll": "2031017", "gpa_1_1": 3.32, "grade_1_1": "B+", "gpa_1_2": 3.289, "grade_1_2": "B+", "gpa_2_1": 3.355, "grade_2_1": "B+", "gpa_2_2": 3.733, "grade_2_2": "A-", "gpa_3_1": 3.64, "grade_3_1": "A-", }, { "name": "JASMIN AKTER MUKTA", "roll": "2031032", "gpa_1_1": 3.22, "grade_1_1": "B", "gpa_1_2": 3.553, "grade_1_2": "A-", "gpa_2_1": 3.203, "grade_2_1": "B", "gpa_2_2": 3.67, "grade_2_2": "A-", "gpa_3_1": 3.67, "grade_3_1": "A-", }, { "name": "UMME SAMIHA ISLAM", "roll": "2031038", "gpa_1_1": 3.32, "grade_1_1": "B+", "gpa_1_2": 3.428, "grade_1_2": "B+", "gpa_2_1": 3.331, "grade_2_1": "B+", "gpa_2_2": 3.506, "grade_2_2": "A-", "gpa_3_1": 3.79, "grade_3_1": "A-", }, { "name": "MD. TORIKUL ISLAM HIMEL", "roll": "2031008", "gpa_1_1": 3.03, "grade_1_1": "B", "gpa_1_2": 3.513, "grade_1_2": "A-", "gpa_2_1": 3.413, "grade_2_1": "B+", "gpa_2_2": 3.614, "grade_2_2": "A-", "gpa_3_1": 3.82, "grade_3_1": "B+", }, { "name": "ASHIKA RAHMAN PANNA", "roll": "2031039", "gpa_1_1": 3.22, "grade_1_1": "B", "gpa_1_2": 3.434, "grade_1_2": "B+", "gpa_2_1": 3.273, "grade_2_1": "B+", "gpa_2_2": 3.528, "grade_2_2": "A-", "gpa_3_1": 3.69, "grade_3_1": "A-", }, { "name": "RUKAIYA HOSSAIN", "roll": "2031045", "gpa_1_1": 3.05, "grade_1_1": "B", "gpa_1_2": 3.454, "grade_1_2": "B+", "gpa_2_1": 3.326, "grade_2_1": "B+", "gpa_2_2": 3.619, "grade_2_2": "A-", "gpa_3_1": 3.48, "grade_3_1": "B+", }, { "name": "MD. SHAHRIAR HOSSAIN", "roll": "2031021", "gpa_1_1": 3.34, "grade_1_1": "B+", "gpa_1_2": 3.368, "grade_1_2": "B+", "gpa_2_1": 3.110, "grade_2_1": "B", "gpa_2_2": 3.486, "grade_2_2": "B+", "gpa_3_1": 3.57, "grade_3_1": "A-", }, { "name": "TUSHAR BISWAS", "roll": "2031019", "gpa_1_1": 2.92, "grade_1_1": "B-", "gpa_1_2": 3.434, "grade_1_2": "B+", "gpa_2_1": 3.355, "grade_2_1": "B+", "gpa_2_2": 3.574, "grade_2_2": "A-", "gpa_3_1": 3.77, "grade_3_1": "A-", }, { "name": "MST. ANIKA TABASSUM", "roll": "2031037", "gpa_1_1": 3.17, "grade_1_1": "B", "gpa_1_2": 3.414, "grade_1_2": "B+", "gpa_2_1": 3.233, "grade_2_1": "B", "gpa_2_2": 3.369, "grade_2_2": "B+", "gpa_3_1": 3.78, "grade_3_1": "A-", }, { "name": "MD. HABIBUL HASAN", "roll": "2031011", "gpa_1_1": 3.34, "grade_1_1": "B+", "gpa_1_2": 3.382, "grade_1_2": "B+", "gpa_2_1": 3.157, "grade_2_1": "B", "gpa_2_2": 3.301, "grade_2_2": "B+", "gpa_3_1": 3.44, "grade_3_1": "B+", }, { "name": "AUTHORY BISWAS BIDDA", "roll": "2031048", "gpa_1_1": 3.25, "grade_1_1": "B", "gpa_1_2": 3.211, "grade_1_2": "B", "gpa_2_1": 3.070, "grade_2_1": "B", "gpa_2_2": 3.631, "grade_2_2": "A-", "gpa_3_1": 3.58, "grade_3_1": "A-", }, { "name": "MD. EMRAN HOSSAIN", "roll": "2031018", "gpa_1_1": 2.85, "grade_1_1": "B-", "gpa_1_2": 3.283, "grade_1_2": "B+", "gpa_2_1": 3.116, "grade_2_1": "B", "gpa_2_2": 3.653, "grade_2_2": "A-", "gpa_3_1": 3.653, "grade_3_1": "A-", }, { "name": "MAHMUDA JANNAT OWYSHE", "roll": "2031044", "gpa_1_1": 3.10, "grade_1_1": "B", "gpa_1_2": 3.289, "grade_1_2": "B+", "gpa_2_1": 2.797, "grade_2_1": "B-", "gpa_2_2": 3.483, "grade_2_2": "B+", "gpa_3_1": 3.483, "grade_3_1": "B+", }, { "name": "MD. MASHUK E RAHMAN", "roll": "2031006", "gpa_1_1": 3.16, "grade_1_1": "B", "gpa_1_2": 3.224, "grade_1_2": "B", "gpa_2_1": 2.791, "grade_2_1": "B-", "gpa_2_2": 3.472, "grade_2_2": "B+", "gpa_3_1": 3.566, "grade_3_1": "A-", }, { "name": "MAHFUZA KHATUN", "roll": "2031046", "gpa_1_1": 2.88, "grade_1_1": "B-", "gpa_1_2": 3.072, "grade_1_2": "B", "gpa_2_1": 2.953, "grade_2_1": "B-", "gpa_2_2": 3.483, "grade_2_2": "B+", "gpa_3_1": 3.49, "grade_3_1": "B+", }, { "name": "MD. RASHIDUL ISLAM", "roll": "2031009", "gpa_1_1": 2.96, "grade_1_1": "B-", "gpa_1_2": 3.184, "grade_1_2": "B", "gpa_2_1": 2.744, "grade_2_1": "C+", "gpa_2_2": 3.426, "grade_2_2": "B+", "gpa_3_1": 3.468, "grade_3_1": "B+", }, { "name": "MD. NAIMUL ISLAM", "roll": "2031004", "gpa_1_1": 2.71, "grade_1_1": "C", "gpa_1_2": 3.039, "grade_1_2": "B", "gpa_2_1": 3.047, "grade_2_1": "B", "gpa_2_2": 3.438, "grade_2_2": "B+", "gpa_3_1": 3.936, "grade_3_1": "A", }, { "name": "NAFiJA TASNIM ANANNA", "roll": "2031034", // Corrected name here "gpa_1_1": 2.78, "grade_1_1": "B-", "gpa_1_2": 3.132, "grade_1_2": "B", "gpa_2_1": 2.930, "grade_2_1": "B-", "gpa_2_2": 3.381, "grade_2_2": "B+", "gpa_3_1": 3.19, "grade_3_1": "B", }, { "name": "JANNATUL MAOUA PROME", "roll": "2031043", "gpa_1_1": 2.70, "grade_1_1": "C", "gpa_1_2": 3.039, "grade_1_2": "B", "gpa_2_1": 3.070, "grade_2_1": "B", "gpa_2_2": 3.267, "grade_2_2": "B", "gpa_3_1": 3.82, "grade_3_1": "B+", }, { "name": "M.D. BELAL HOSSAIN BADHON", "roll": "2031007", "gpa_1_1": 2.82, "grade_1_1": "B-", "gpa_1_2": 3.059, "grade_1_2": "B", "gpa_2_1": 2.895, "grade_2_1": "B-", "gpa_2_2": 3.233, "grade_2_2": "B", "gpa_3_1": 3.391, "grade_3_1": "B+", }, { "name": "MST. SULTANA PARVIN SHIFA", "roll": "2031028", "gpa_1_1": 2.76, "grade_1_1": "C", "gpa_1_2": 2.480, "grade_1_2": "C", "gpa_2_1": 1.895, "grade_2_1": "F", "gpa_2_2": 2.438, "grade_2_2": "C", "gpa_3_1": 2.953, "grade_3_1": "C", } ].map(student => { let imageUrl = studentImageMap[student.roll]; if (studentsUsingSharedImage.includes(student.roll)) { imageUrl = sharedImageUrl; } // Fallback for any students not explicitly mapped if (!imageUrl) { imageUrl = "https://placehold.co/96x96/aabbcc/ffffff?text=No+Image"; // Generic placeholder } // Calculate new CGPA as the average of the five semesters const totalGpa = (student.gpa_1_1 + student.gpa_1_2 + student.gpa_2_1 + student.gpa_2_2 + student.gpa_3_1); const newCgpa = (totalGpa / 5).toFixed(2); return { ...student, imageUrl, cgpa: parseFloat(newCgpa) }; }); // Define a custom color palette for the charts for a modern look const chartColors = ['#6366F1', '#EC4899', '#10B981', '#F59E0B', '#EF4444', '#3B82F6', '#8B5CF6']; // Custom Tooltip for CGPA Distribution const CustomTooltip = ({ active, payload, label }) => { if (active && payload && payload.length) { const data = payload[0].payload; // Collect all student rolls into a single comma-separated string const studentRolls = data.students.map(student => student.roll).join(', '); return (

{`CGPA ≥ ${data.threshold}`}

{`Number of Students: ${data.count}`}

Student Rolls:

{studentRolls.length > 0 ? ( {studentRolls} ) : ( 'No students in this range.' )}
); } return null; }; function App() { const [searchTerm, setSearchTerm] = useState(''); const [filteredStudents, setSearchStudents] = useState([]); const [selectedStudent, setSelectedStudent] = useState(null); const [sortConfig, setSortConfig] = useState({ key: null, direction: 'ascending' }); const [isMobile, setIsMobile] = useState(false); const [chartError, setChartError] = useState(false); useEffect(() => { const handleResize = () => { setIsMobile(window.innerWidth < 768); // Tailwind's 'md' breakpoint is 768px }; window.addEventListener('resize', handleResize); handleResize(); // Set initial value return () => window.removeEventListener('resize', handleResize); }, []); // Reset chart error when student changes useEffect(() => { setChartError(false); }, [selectedStudent]); // FIXED PRINT FUNCTION const handlePrintResult = useCallback(() => { if (!selectedStudent) return; // Helper to get grade or 'N/A' const getGrade = (gpaKey, gradeKey) => { const gpaValue = selectedStudent[gpaKey]; if (gpaValue !== undefined && gpaValue !== null) { const gradeValue = selectedStudent[gradeKey] || 'N/A'; return `${gpaValue.toFixed(3)} (${gradeValue})`; } return 'N/A'; }; const printContent = ` Student Result Slip `; const printWindow = window.open('', '', 'height=800,width=600'); printWindow.document.write(printContent); printWindow.document.close(); // Wait for the document to load before printing printWindow.onload = function() { printWindow.print(); // Do not close the window - let the user close it when they're done }; }, [selectedStudent]); // Effect to filter students based on search term useEffect(() => { if (searchTerm.trim() === '') { setSearchStudents([]); setSelectedStudent(null); // Clear selected student when search is empty return; } const lowerCaseSearchTerm = searchTerm.toLowerCase(); const results = studentData.filter(student => student.name.toLowerCase().includes(lowerCaseSearchTerm) || student.roll.includes(lowerCaseSearchTerm) ); setSearchStudents(results); // If only one result, automatically select it if (results.length === 1) { setSelectedStudent(results[0]); } else { setSelectedStudent(null); } }, [searchTerm]); // Function to handle sorting const sortedStudentData = useMemo(() => { let sortableItems = [...studentData]; if (sortConfig.key !== null) { sortableItems.sort((a, b) => { let aValue = a[sortConfig.key]; let bValue = b[sortConfig.key]; // Handle GPA/CGPA values as numbers for proper sorting if (sortConfig.key.includes('gpa') || sortConfig.key === 'cgpa') { aValue = parseFloat(aValue) || 0; bValue = parseFloat(bValue) || 0; } else if (sortConfig.key === 'roll') { // Treat roll as number for sorting aValue = parseInt(aValue, 10) || 0; bValue = parseInt(bValue, 10) || 0; } if (aValue < bValue) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (aValue > bValue) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0; }); } return sortableItems; }, [studentData, sortConfig]); const requestSort = (key) => { let direction = 'ascending'; if (sortConfig.key === key && sortConfig.direction === 'ascending') { direction = 'descending'; } setSortConfig({ key, direction }); }; const getSortIndicator = (key) => { if (sortConfig.key === key) { return sortConfig.direction === 'ascending' ? ' ▲' : ' ▼'; } return ''; }; // Prepare data for Grade Distribution Bar Chart const gradeDistributionData = useMemo(() => { const gradeCounts = {}; const grades = ['A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D', 'F']; // Define order of grades studentData.forEach(student => { // Collect all grades from all semesters ['grade_1_1', 'grade_1_2', 'grade_2_1', 'grade_2_2', 'grade_3_1'].forEach(gradeKey => { const grade = student[gradeKey]; if (grade) { gradeCounts[grade] = (gradeCounts[grade] || 0) + 1; } }); }); // Convert to array of objects for recharts, maintaining order return grades .filter(grade => gradeCounts[grade] > 0) // Only include grades that exist in data .map(grade => ({ grade: grade, count: gradeCounts[grade] || 0 })); }, []); // Prepare data for CGPA Trend Line Chart (Overall Average) const cgpaTrendData = useMemo(() => { const semesterData = {}; // { '1-1': { totalGpa: 0, count: 0 }, ... } studentData.forEach(student => { // Aggregate GPA for each semester ['1_1', '1_2', '2_1', '2_2', '3_1'].forEach(sem => { const gpaKey = `gpa_${sem}`; const semesterName = sem.replace('_', '-'); // Format for display if (student[gpaKey] !== undefined) { if (!semesterData[semesterName]) { semesterData[semesterName] = { totalGpa: 0, count: 0 }; } semesterData[semesterName].totalGpa += student[gpaKey]; semesterData[semesterName].count += 1; } }); }); // Calculate average GPA for each semester const trend = Object.keys(semesterData).map(semester => ({ semester: semester, averageGpa: (semesterData[semester].totalGpa / semesterData[semester].count).toFixed(2) })); // Map semester names for display const semesterDisplayNames = { '1-1': 'First Semester', '1-2': 'Second Semester', '2-1': 'Third Semester', '2-2': 'Fourth Semester', '3-1': 'Fifth Semester', }; // Sort semesters for proper line chart display and apply display names const semesterOrder = ['1-1', '1-2', '2-1', '2-2', '3-1']; return trend.sort((a, b) => semesterOrder.indexOf(a.semester) - semesterOrder.indexOf(b.semester)) .map(item => ({ ...item, semester: semesterDisplayNames[item.semester] || item.semester })); }, []); // Prepare data for Selected Student's GPA Trend Line Chart const selectedStudentGpaTrendData = useMemo(() => { if (!selectedStudent) return []; const trend = []; const semesterOrder = ['1_1', '1_2', '2_1', '2_2', '3_1']; // Process each semester in order for (const sem of semesterOrder) { const gpaKey = `gpa_${sem}`; const semesterName = sem.replace('_', '-'); let gpaValue = selectedStudent[gpaKey]; // Ensure gpaValue is a valid number if (typeof gpaValue === 'string') { gpaValue = parseFloat(gpaValue); } // Only include valid GPA values if (typeof gpaValue === 'number' && !isNaN(gpaValue) && gpaValue >= 0 && gpaValue <= 4.0) { trend.push({ semester: semesterName, gpa: gpaValue }); } } // Map semester names for display const semesterDisplayNames = { '1-1': 'First Semester', '1-2': 'Second Semester', '2-1': 'Third Semester', '2-2': 'Fourth Semester', '3-1': 'Fifth Semester', }; // Sort by semester order return trend.sort((a, b) => { const orderA = semesterOrder.indexOf(`gpa_${a.semester.replace('-', '_')}`); const orderB = semesterOrder.indexOf(`gpa_${b.semester.replace('-', '_')}`); return orderA - orderB; }).map(item => ({ ...item, semester: semesterDisplayNames[item.semester] || item.semester })); }, [selectedStudent]); // Dynamic Y-axis domain for individual student GPA trend const selectedStudentGpaDomain = useMemo(() => { if (!selectedStudentGpaTrendData.length) return [1.0, 4.0]; try { const gpas = selectedStudentGpaTrendData.map(d => d.gpa); const minGpa = Math.min(...gpas); const maxGpa = Math.max(...gpas); // Ensure min and max are valid numbers if (isNaN(minGpa) || isNaN(maxGpa)) { return [1.0, 4.0]; } // Add a small buffer for better visualization. // Ensure the domain is at least a small range if min and max are the same. const padding = 0.1; const finalMin = Math.max(0, minGpa - padding); const finalMax = Math.min(4.0, maxGpa + padding); // If min and max are the same, expand the domain slightly. if (finalMin === finalMax) { return [Math.max(0, finalMin - 0.5), Math.min(4.0, finalMax + 0.5)]; } return [finalMin, finalMax]; } catch (error) { console.error("Error calculating GPA domain:", error); return [1.0, 4.0]; } }, [selectedStudentGpaTrendData]); // Dynamic Y-axis domain for overall average GPA trend const overallGpaDomain = useMemo(() => { if (!cgpaTrendData.length) return [1.0, 4.0]; try { const gpas = cgpaTrendData.map(d => parseFloat(d.averageGpa)); const minGpa = Math.min(...gpas); const maxGpa = Math.max(...gpas); // Ensure min and max are valid numbers if (isNaN(minGpa) || isNaN(maxGpa)) { return [1.0, 4.0]; } // Add a small buffer to min/max for better visualization, ensuring it doesn't go below 0 or above 4.0 return [ Math.max(0, minGpa - 0.1), Math.min(4.0, maxGpa + 0.1) ]; } catch (error) { console.error("Error calculating overall GPA domain:", error); return [1.0, 4.0]; } }, [cgpaTrendData]); // Cumulative CGPA Distribution const cgpaDistributionData = useMemo(() => { const thresholds = [2.5, 3.0, 3.25, 3.5, 3.75, 3.8, 3.9]; return thresholds.map(threshold => { const studentsAboveThreshold = studentData.filter(student => student.cgpa >= threshold); return { threshold: threshold, count: studentsAboveThreshold.length, students: studentsAboveThreshold.map(s => ({ name: s.name, roll: s.roll })) }; }); }, []); // Calculate overall statistics const overallStats = useMemo(() => { const cgpAs = studentData.map(s => s.cgpa); const maxCGPA = Math.max(...cgpAs); const minCGPA = Math.min(...cgpAs); const averageCGPA = (cgpAs.reduce((sum, val) => sum + val, 0) / cgpAs.length).toFixed(2); return { maxCGPA, minCGPA, averageCGPA }; }, []); // Error-safe chart renderer const renderStudentGpaChart = useCallback(() => { if (chartError) { return (

Error displaying GPA chart. Please try another student.

); } if (!selectedStudentGpaTrendData.length) { return (
No GPA trend data available for this student.
); } try { return ( index === 0 ? '' : value.toFixed(2)} /> ); } catch (error) { console.error("Chart rendering error:", error); setChartError(true); return (
Error displaying GPA chart. Please try another student.
); } }, [selectedStudentGpaTrendData, selectedStudentGpaDomain, chartError]); return (
{/* Header */}

ESG 20-21 Result Dashboard

Islamic University Bangladesh

{/* Search Section - Made Sticky */}
setSearchTerm(e.target.value)} />
{/* Search Results / Suggestions */} {searchTerm.trim() !== '' && filteredStudents.length > 0 && (

Matching Students:

    {filteredStudents.map(student => (
  • setSelectedStudent(student)} > {student.name} ({student.roll})
  • ))}
)} {searchTerm.trim() !== '' && filteredStudents.length === 0 && (
No students found matching "${searchTerm}". Please try a different name or roll.
)}
{/* Dashboard Content */}
{/* Conditional rendering based on selectedStudent */} {selectedStudent ? ( // Layout when a student is selected <>

Selected Student Details

{/* Student Image */} {`${selectedStudent.name} { e.target.onerror = null; e.target.src="https://placehold.co/96x96/aabbcc/ffffff?text=No+Image" }} />

Name: {selectedStudent.name}

Roll: {selectedStudent.roll}

CGPA: {selectedStudent.cgpa}

Semester-wise Grades:

  • First Semester: {selectedStudent.gpa_1_1 !== undefined && selectedStudent.gpa_1_1 !== null ? `${selectedStudent.gpa_1_1.toFixed(3)} (${selectedStudent.grade_1_1 || 'N/A'})` : 'N/A'}
  • Second Semester: {selectedStudent.gpa_1_2 !== undefined && selectedStudent.gpa_1_2 !== null ? `${selectedStudent.gpa_1_2.toFixed(3)} (${selectedStudent.grade_1_2 || 'N/A'})` : 'N/A'}
  • Third Semester: {selectedStudent.gpa_2_1 !== undefined && selectedStudent.gpa_2_1 !== null ? `${selectedStudent.gpa_2_1.toFixed(3)} (${selectedStudent.grade_2_1 || 'N/A'})` : 'N/A'}
  • Fourth Semester: {selectedStudent.gpa_2_2 !== undefined && selectedStudent.gpa_2_2 !== null ? `${selectedStudent.gpa_2_2.toFixed(3)} (${selectedStudent.grade_2_2 || 'N/A'})` : 'N/A'}
  • Fifth Semester: {selectedStudent.gpa_3_1 !== undefined && selectedStudent.gpa_3_1 !== null ? `${selectedStudent.gpa_3_1.toFixed(3)} (${selectedStudent.grade_3_1 || 'N/A'})` : 'N/A'}
{/* Print Result button moved here */}

{selectedStudent.name ? `${selectedStudent.name}'s GPA Trend` : "Student's GPA Trend"}

{renderStudentGpaChart()} {selectedStudent && (

Developed by MD. PARVEJ HOSSAIN

)}
) : ( // Layout when no student is selected (Overall Dashboard) <> {/* Overall Statistics (inline) */}

Overall Statistics:

Total Students: {studentData.length}

Highest CGPA: {overallStats.maxCGPA}

Lowest CGPA: {overallStats.minCGPA}

Average CGPA: {overallStats.averageCGPA}

{/* Overall Charts - Now 50/50 split */}
{/* Overall Average GPA Trend by Semester */}

Overall Average GPA Trend by Semester

index === 0 ? '' : value.toFixed(2)} tickCount={5} // Show only 5 ticks for cleaner UI tickMargin={10} tick={{ fontSize: 12, fill: '#666' }} axisLine={{ stroke: '#ccc' }} tickLine={{ stroke: '#ccc' }} />
{/* Cumulative CGPA Distribution */}

Cumulative CGPA Distribution

} cursor={{ fill: 'rgba(0,0,0,0.05)' }} position={{ x: 40, y: -20 }} // Position tooltip at top right of cursor />
)} {/* Total Students Result Table - Only show if no student selected */} {!selectedStudent && (

All Student Results

{ sortedStudentData.map((student) => ( )) }
requestSort('name')} > Name {getSortIndicator('name')} requestSort('roll')} > Roll {getSortIndicator('roll')} requestSort('gpa_1_1')} > First Semester {getSortIndicator('gpa_1_1')} requestSort('gpa_1_2')} > Second Semester {getSortIndicator('gpa_1_2')} requestSort('gpa_2_1')} > Third Semester {getSortIndicator('gpa_2_1')} requestSort('gpa_2_2')} > Fourth Semester {getSortIndicator('gpa_2_2')} requestSort('gpa_3_1')} > Fifth Semester {getSortIndicator('gpa_3_1')} requestSort('cgpa')} > CGPA {getSortIndicator('cgpa')}
{student.name} {student.roll} {student.gpa_1_1 !== undefined ? `${student.gpa_1_1.toFixed(3)} (${student.grade_1_1 || 'N/A'})` : 'N/A'} {student.gpa_1_2 !== undefined ? `${student.gpa_1_2.toFixed(3)} (${student.grade_1_2 || 'N/A'})` : 'N/A'} {student.gpa_2_1 !== undefined ? `${student.gpa_2_1.toFixed(3)} (${student.grade_2_1 || 'N/A'})` : 'N/A'} {student.gpa_2_2 !== undefined ? `${student.gpa_2_2.toFixed(3)} (${student.grade_2_2 || 'N/A'})` : 'N/A'} {student.gpa_3_1 !== undefined ? `${student.gpa_3_1.toFixed(3)} (${student.grade_3_1 || 'N/A'})` : 'N/A'} {student.cgpa}
)}
); } export default App;