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 - - - - - - - `; - - 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 + + + + + + + `; + + 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 */} - {`${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'} - -
  • -
-
- - {/* 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 */} + {`${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'} + +
  • +
+
+ + {/* 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('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.cgpa}
+
+
+ )}
-
- - )} - - {/* 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('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.cgpa}
-
- )}
-
-
- ); + ); } export default App; \ No newline at end of file