diff --git a/src/App.jsx b/src/App.jsx
index 5bda430..a0d4c37 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo, useCallback } from 'react';
-import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, LineChart, Line } from 'recharts';
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line } from 'recharts';
// Define image URLs for students
const studentImageMap = {
@@ -43,233 +43,233 @@ const studentsUsingSharedImage = [
"2031040", // MST. ANANNA-AFRIN
"2031037", // MST. ANIKA TABASSUM
"2031028", // MST. SULTANA PARVIN SHIFA
- "2031027" // SADIA ISLAM
+ "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",
- "cgpa": 3.82
- },
- {
- "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",
- "cgpa": 3.76
- },
- {
- "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",
- "cgpa": 3.71
- },
- {
- "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",
- "cgpa": 3.67
- },
- {
- "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",
- "cgpa": 3.67
- },
- {
- "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-",
- "cgpa": 3.64
- },
- {
- "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",
- "cgpa": 3.63
- },
- {
- "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-",
- "cgpa": 3.63
- },
- {
- "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-",
- "cgpa": 3.58
- },
- {
- "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-",
- "cgpa": 3.58
- },
- {
- "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-",
- "cgpa": 3.55
- },
- {
- "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-",
- "cgpa": 3.53
- },
- {
- "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-",
- "cgpa": 3.50
- },
- {
- "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-",
- "cgpa": 3.50
- },
- {
- "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-",
- "cgpa": 3.49
- },
- {
- "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-",
- "cgpa": 3.48
- },
- {
- "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-",
- "cgpa": 3.42
- },
- {
- "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-",
- "cgpa": 3.41
- },
- {
- "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-",
- "cgpa": 3.40
- },
- {
- "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-",
- "cgpa": 3.39
- },
- {
- "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-",
- "cgpa": 3.36
- },
- {
- "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-",
- "cgpa": 3.36
- },
- {
- "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+",
- "cgpa": 3.32
- },
- {
- "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-",
- "cgpa": 3.32
- },
- {
- "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+",
- "cgpa": 3.30
- },
- {
- "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+",
- "cgpa": 3.30
- },
- {
- "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-",
- "cgpa": 3.29
- },
- {
- "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-",
- "cgpa": 3.23
- },
- {
- "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+",
- "cgpa": 3.17
- },
- {
- "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+",
- "cgpa": 3.16
- },
- {
- "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+",
- "cgpa": 3.10
- },
- {
- "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+",
- "cgpa": 3.08
- },
- {
- "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+",
- "cgpa": 3.06
- },
- {
- "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+",
- "cgpa": 3.06
- },
- {
- "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",
- "cgpa": 3.02
- },
- {
- "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",
- "cgpa": 3.00
- },
- {
- "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",
- "cgpa": 2.39
- }
+ {
+ "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",
+ "cgpa": 3.82
+ },
+ {
+ "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",
+ "cgpa": 3.76
+ },
+ {
+ "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",
+ "cgpa": 3.71
+ },
+ {
+ "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",
+ "cgpa": 3.67
+ },
+ {
+ "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",
+ "cgpa": 3.67
+ },
+ {
+ "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-",
+ "cgpa": 3.64
+ },
+ {
+ "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",
+ "cgpa": 3.63
+ },
+ {
+ "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-",
+ "cgpa": 3.63
+ },
+ {
+ "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-",
+ "cgpa": 3.58
+ },
+ {
+ "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-",
+ "cgpa": 3.58
+ },
+ {
+ "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-",
+ "cgpa": 3.55
+ },
+ {
+ "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-",
+ "cgpa": 3.53
+ },
+ {
+ "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-",
+ "cgpa": 3.50
+ },
+ {
+ "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-",
+ "cgpa": 3.50
+ },
+ {
+ "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-",
+ "cgpa": 3.49
+ },
+ {
+ "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-",
+ "cgpa": 3.48
+ },
+ {
+ "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-",
+ "cgpa": 3.42
+ },
+ {
+ "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-",
+ "cgpa": 3.41
+ },
+ {
+ "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-",
+ "cgpa": 3.40
+ },
+ {
+ "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-",
+ "cgpa": 3.39
+ },
+ {
+ "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-",
+ "cgpa": 3.36
+ },
+ {
+ "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-",
+ "cgpa": 3.36
+ },
+ {
+ "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+",
+ "cgpa": 3.32
+ },
+ {
+ "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-",
+ "cgpa": 3.32
+ },
+ {
+ "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+",
+ "cgpa": 3.30
+ },
+ {
+ "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+",
+ "cgpa": 3.30
+ },
+ {
+ "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-",
+ "cgpa": 3.29
+ },
+ {
+ "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-",
+ "cgpa": 3.23
+ },
+ {
+ "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+",
+ "cgpa": 3.17
+ },
+ {
+ "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+",
+ "cgpa": 3.16
+ },
+ {
+ "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+",
+ "cgpa": 3.10
+ },
+ {
+ "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+",
+ "cgpa": 3.08
+ },
+ {
+ "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+",
+ "cgpa": 3.06
+ },
+ {
+ "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+",
+ "cgpa": 3.06
+ },
+ {
+ "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",
+ "cgpa": 3.02
+ },
+ {
+ "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",
+ "cgpa": 3.00
+ },
+ {
+ "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",
+ "cgpa": 2.39
+ }
].map(student => {
let imageUrl = studentImageMap[student.roll];
if (studentsUsingSharedImage.includes(student.roll)) {
@@ -285,925 +285,906 @@ const studentData = [
// 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
+// Custom Tooltip for CGPA Distribution (Updated with a scrollable container for rolls)
const CustomTooltip = ({ active, payload, label }) => {
- if (active && payload && payload.length) {
- const data = payload[0].payload;
- return (
-
-
{`CGPA ${data.rangeLabel}`}
-
{`Number of Students: ${data.count}`}
-
Students:
-
- {data.students.length > 0 ? (
- data.students.map(student => student.roll).join(', ')
- ) : (
- 'No students in this range.'
- )}
-
-
- );
- }
- return null;
+ 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);
+ 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);
- }, []);
+ 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]);
+ // 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
-
-
-
-
-
-
-
-
Name: ${selectedStudent.name}
-
Roll: ${selectedStudent.roll}
-
CGPA: ${selectedStudent.cgpa}
-
-
-
-
- | Semester |
- GPA (Grade) |
-
-
-
-
- | First Semester |
- ${getGrade('gpa_1_1', 'grade_1_1')} |
-
-
- | Second Semester |
- ${getGrade('gpa_1_2', 'grade_1_2')} |
-
-
- | Third Semester |
- ${getGrade('gpa_2_1', 'grade_2_1')} |
-
-
- | Fourth Semester |
- ${getGrade('gpa_2_2', 'grade_2_2')} |
-
-
-
-
-
-
-
-
- `;
-
- 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];
+ // FIXED PRINT FUNCTION
+ const handlePrintResult = useCallback(() => {
+ if (!selectedStudent) return;
- // 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;
+ // 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
+
+
+
+
+
+
+
Name: ${selectedStudent.name}
+
Roll: ${selectedStudent.roll}
+
CGPA: ${selectedStudent.cgpa}
+
+
+
+
+ | Semester |
+ GPA (Grade) |
+
+
+
+
+ | First Semester |
+ ${getGrade('gpa_1_1', 'grade_1_1')} |
+
+
+ | Second Semester |
+ ${getGrade('gpa_1_2', 'grade_1_2')} |
+
+
+ | Third Semester |
+ ${getGrade('gpa_2_1', 'grade_2_1')} |
+
+
+ | Fourth Semester |
+ ${getGrade('gpa_2_2', 'grade_2_2')} |
+
+
+
+
+
+
+
+
+ `;
+
+ 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;
}
- if (aValue < bValue) {
- return sortConfig.direction === 'ascending' ? -1 : 1;
+ 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);
}
- if (aValue > bValue) {
- return sortConfig.direction === 'ascending' ? 1 : -1;
+ }, [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 0;
- });
- }
- return sortableItems;
- }, [studentData, sortConfig]);
+ 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'].forEach(gradeKey => {
- const grade = student[gradeKey];
- if (grade) {
- gradeCounts[grade] = (gradeCounts[grade] || 0) + 1;
+ const requestSort = (key) => {
+ let direction = 'ascending';
+ if (sortConfig.key === key && sortConfig.direction === 'ascending') {
+ direction = 'descending';
}
- });
- });
-
- // 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'].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',
+ setSortConfig({ key, direction });
};
-
- // Sort semesters for proper line chart display and apply display names
- const semesterOrder = ['1-1', '1-2', '2-1', '2-2'];
- 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'];
-
- // 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
+ 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'].forEach(gradeKey => {
+ const grade = student[gradeKey];
+ if (grade) {
+ gradeCounts[grade] = (gradeCounts[grade] || 0) + 1;
+ }
+ });
});
- }
- }
-
- // Map semester names for display
- const semesterDisplayNames = {
- '1-1': 'First Semester',
- '1-2': 'Second Semester',
- '2-1': 'Third Semester',
- '2-2': 'Fourth 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 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 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]);
-
- // FIXED CGPA Distribution with cumulative counts
- 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 {
- range: threshold.toFixed(2),
- rangeLabel: `>${threshold.toFixed(2)}`,
- 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 (
-
-
-
-
- value.toFixed(2)}
- tickMargin={10}
- tick={{ fontSize: 12, fill: '#666' }}
- axisLine={{ stroke: '#ccc' }}
- tickLine={{ stroke: '#ccc' }}
- />
-
-
-
-
-
- );
- } 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 {/* Updated Dashboard Title */}
-
-
- Islamic University Bangladesh {/* Updated University Name */}
-
-
- {/* 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.
-
- )}
-
+ // 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'].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;
+ }
+ });
+ });
- {/* Dashboard Content */}
-
- {/* Conditional rendering based on selectedStudent */}
- {selectedStudent ? (
- // Layout when a student is selected (First Screenshot Design)
- <>
-
-
Selected Student Details
-
- {/* Student Image */}
-

{
- 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'}
-
-
-
-
-
- {/* Print Result button moved here */}
-
+ // 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',
+ };
+
+ // Sort semesters for proper line chart display and apply display names
+ const semesterOrder = ['1-1', '1-2', '2-1', '2-2'];
+ 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'];
+
+ // 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',
+ };
+
+ // 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 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 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.
+
-
-
-
-
- {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}
+ );
+ }
+
+ if (!selectedStudentGpaTrendData.length) {
+ return (
+
+ No GPA trend data available for this student.
-
-
- {/* Overall Charts - Now 50/50 split */}
-
- {/* Overall Average GPA Trend by Semester */}
-
-
Overall Average GPA Trend by Semester
-
-
+ );
+ }
+
+ try {
+ return (
+
-
-
- value.toFixed(2)}
- tickMargin={10}
- tick={{ fontSize: 12, fill: '#666' }}
- axisLine={{ stroke: '#ccc' }}
- tickLine={{ stroke: '#ccc' }}
- />
-
-
-
+
+
+ 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 {/* Updated Dashboard Title */}
+
+
+ Islamic University Bangladesh {/* Updated University Name */}
+
- {/* CGPA Distribution (Number of Students) */}
-
-
CGPA Distribution (Number of Students)
-
-
-
-
-
-
- } />
-
-
-
-
+ {/* 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 (First Screenshot Design)
+ <>
+
+
Selected Student Details
+
+ {/* Student Image */}
+

{
+ 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'}
+
+
+
+
+
+ {/* 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
+
+
+
+
+ | 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('cgpa')}
+ >
+ CGPA {getSortIndicator('cgpa')}
+ |
+
+
+ {
+ sortedStudentData.map((student) => (
+
+ | {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.cgpa} |
+
+ ))
+ }
+
+
+
+ )}
-
- >
- )}
-
- {/* Total Students Result Table - Only show if no student selected */}
- {!selectedStudent && (
-
-
All Student Results
-
-
-
-
- | 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('cgpa')}
- >
- CGPA {getSortIndicator('cgpa')}
- |
-
-
- {
- sortedStudentData.map((student) => (
-
- | {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.cgpa} |
-
- ))
- }
-
-
- )}
-
-
- );
+ );
}
export default App;
\ No newline at end of file