From 255d7ef59ab2a3d633f69554a37a2eb95e5042f2 Mon Sep 17 00:00:00 2001 From: "Md. Parvej Hossain" Date: Sat, 30 Aug 2025 23:06:15 +0600 Subject: [PATCH] 3-121 --- src/App.jsx | 1154 +++++++++++++++++++++++++++------- src/a.txt | 1712 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2646 insertions(+), 220 deletions(-) create mode 100644 src/a.txt diff --git a/src/App.jsx b/src/App.jsx index 79ba42b..07d017c 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, ResponsiveContainer, LineChart, Line } from 'recharts'; +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, Legend } from 'recharts'; // Define image URLs for students const studentImageMap = { @@ -33,6 +33,7 @@ const studentImageMap = { "2031024": "https://i.ibb.co/mCNbhMpX/MST-AFROZA-KHATUN24.png", // Specific for 2031024 "2031042": "https://i.postimg.cc/bNJkZxJB/KHADIJA-AKTER.png", // Unique image for Khadija Akter }; + const sharedImageUrl = "https://i.ibb.co/BVc0jwkq/JASMIN-AKTER-MUKTA.png"; // JASMIN AKTER MUKTA's image const studentsUsingSharedImage = [ "2031042", // KHADIJA AKTER @@ -46,229 +47,377 @@ const studentsUsingSharedImage = [ "2031027" // SADIA ISLAM ]; -// Manually extracted data from the provided Excel sheet image +// Manually extracted data from the provided Excel sheet with corrected values const studentData = [ { - "name": "MEFTAHUL JANNAT", "roll": "2031025", - "gpa_1_1": 3.90, "grade_1_1": "A-", "gpa_1_2": 3.737, "grade_1_2": "A-", - "gpa_2_1": 3.779, "grade_2_1": "A", "gpa_2_2": 3.852, "grade_2_2": "A", - "gpa_3_1": 3.89, "grade_3_1": "B+", + "name": "KOBIR HOSSAIN ", + "roll": "2031001", + "gpa_1_1": 3.54, + "gpa_1_2": 3.421, + "gpa_2_1": 3.517, + "gpa_2_2": 3.517, + "gpa_3_1": 3.705, + "cgpa": 3.54 }, { - "name": "MD. PARVEJ HOSSAIN", "roll": "2031005", - "gpa_1_1": 3.74, "grade_1_1": "A-", "gpa_1_2": 3.612, "grade_1_2": "A-", - "gpa_2_1": 3.750, "grade_2_1": "A-", "gpa_2_2": 3.886, "grade_2_2": "A", - "gpa_3_1": 3.936, "grade_3_1": "A", + "name": "MOHD. TAKIUDDIN ", + "roll": "2031003", + "gpa_1_1": 3.6, + "gpa_1_2": 3.651, + "gpa_2_1": 3.605, + "gpa_2_2": 3.841, + "gpa_3_1": 3.936, + "cgpa": 3.73 }, { - "name": "MST. AFROZA KHATUN", "roll": "2031036", - "gpa_1_1": 3.67, "grade_1_1": "A-", "gpa_1_2": 3.638, "grade_1_2": "A-", - "gpa_2_1": 3.605, "grade_2_1": "A-", "gpa_2_2": 3.992, "grade_2_2": "A", - "gpa_3_1": 3.85, "grade_3_1": "A", + "name": "MD. NAIMUL ISLAM", + "roll": "2031004", + "gpa_1_1": 2.71, + "gpa_1_2": 3.039, + "gpa_2_1": 3.047, + "gpa_2_2": 3.438, + "gpa_3_1": 3.56, + "cgpa": 3.16 }, { - "name": "MOHD. TAKIUDDIN", "roll": "2031003", - "gpa_1_1": 3.60, "grade_1_1": "A-", "gpa_1_2": 3.651, "grade_1_2": "A-", - "gpa_2_1": 3.605, "grade_2_1": "A-", "gpa_2_2": 3.841, "grade_2_2": "A", - "gpa_3_1": 3.936, "grade_3_1": "A-", + "name": "MD. PARVEJ HOSSAIN ", + "roll": "2031005", + "gpa_1_1": 3.74, + "gpa_1_2": 3.612, + "gpa_2_1": 3.75, + "gpa_2_2": 3.886, + "gpa_3_1": 3.936, + "cgpa": 3.78 }, { - "name": "SHAFKAT TASEEN SADIYA", "roll": "2031030", // Corrected name here - "gpa_1_1": 3.58, "grade_1_1": "A-", "gpa_1_2": 3.579, "grade_1_2": "A-", - "gpa_2_1": 3.551, "grade_2_1": "A-", "gpa_2_2": 3.852, "grade_2_2": "A", - "gpa_3_1": 3.86, "grade_3_1": "B+", + "name": "MD. MASHUK E RAHMAN ", + "roll": "2031006", + "gpa_1_1": 3.16, + "gpa_1_2": 3.224, + "gpa_2_1": 2.791, + "gpa_2_2": 3.472, + "gpa_3_1": 3.566, + "cgpa": 3.24 }, { - "name": "MST. AFROZA KHATUN", "roll": "2031024", - "gpa_1_1": 3.52, "grade_1_1": "A-", "gpa_1_2": 3.737, "grade_1_2": "A-", - "gpa_2_1": 3.587, "grade_2_1": "A-", "gpa_2_2": 3.716, "grade_2_2": "A-", - "gpa_3_1": 3.92, "grade_3_1": "A", + "name": "M.D. BELAL HOSSAIN BADHON ", + "roll": "2031007", + "gpa_1_1": 2.82, + "gpa_1_2": 3.059, + "gpa_2_1": 2.895, + "gpa_2_2": 3.233, + "gpa_3_1": 3.391, + "cgpa": 3.08 }, { - "name": "MST. ANANNA-AFRIN", "roll": "2031040", - "gpa_1_1": 3.44, "grade_1_1": "B+", "gpa_1_2": 3.592, "grade_1_2": "A-", - "gpa_2_1": 3.640, "grade_2_1": "A-", "gpa_2_2": 3.864, "grade_2_2": "A", - "gpa_3_1": 3.91, "grade_3_1": "A", + "name": "MD. TORIKUL ISLAM HIMAL ", + "roll": "2031008", + "gpa_1_1": 3.03, + "gpa_1_2": 3.513, + "gpa_2_1": 3.413, + "gpa_2_2": 3.614, + "gpa_3_1": 3.7, + "cgpa": 3.45 }, { - "name": "ORPA AHMED", "roll": "2031041", - "gpa_1_1": 3.53, "grade_1_1": "A-", "gpa_1_2": 3.704, "grade_1_2": "A-", - "gpa_2_1": 3.552, "grade_2_1": "A-", "gpa_2_2": 3.739, "grade_2_2": "A-", - "gpa_3_1": 3.89, "grade_3_1": "A", + "name": "MD. RASHIDUL ISLAM", + "roll": "2031009", + "gpa_1_1": 2.96, + "gpa_1_2": 3.184, + "gpa_2_1": 2.744, + "gpa_2_2": 3.426, + "gpa_3_1": 3.468, + "cgpa": 3.16 }, { - "name": "MD. SHANZAD ALI", "roll": "2031012", - "gpa_1_1": 3.45, "grade_1_1": "B+", "gpa_1_2": 3.566, "grade_1_2": "A-", - "gpa_2_1": 3.517, "grade_2_1": "A-", "gpa_2_2": 3.773, "grade_2_2": "A-", - "gpa_3_1": 3.91, "grade_3_1": "A", + "name": "WASHIM SHAHED SOVON ", + "roll": "2031010", + "gpa_1_1": 3.21, + "gpa_1_2": 3.487, + "gpa_2_1": 3.43, + "gpa_2_2": 3.784, + "gpa_3_1": 3.974, + "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-", - "gpa_3_1": 3.82, "grade_3_1": "B+", + "name": "MD. HABIBUL HASAN ", + "roll": "2031011", + "gpa_1_1": 3.34, + "gpa_1_2": 3.382, + "gpa_2_1": 3.157, + "gpa_2_2": 3.301, + "gpa_3_1": 3.44, + "cgpa": 3.32 }, { - "name": "MD. OMAYER HAQUE KHAN", "roll": "2031016", - "gpa_1_1": 3.42, "grade_1_1": "B+", "gpa_1_2": 3.559, "grade_1_2": "A-", - "gpa_2_1": 3.483, "grade_2_1": "B+", "gpa_2_2": 3.739, "grade_2_2": "A-", - "gpa_3_1": 3.87, "grade_3_1": "A-", + "name": "MD. SHANZAD ALI ", + "roll": "2031012", + "gpa_1_1": 3.45, + "gpa_1_2": 3.566, + "gpa_2_1": 3.517, + "gpa_2_2": 3.773, + "gpa_3_1": 3.91, + "cgpa": 3.64 }, { - "name": "KHADIJA AKTER", "roll": "2031042", - "gpa_1_1": 3.36, "grade_1_1": "B+", "gpa_1_2": 3.559, "grade_1_2": "A-", - "gpa_2_1": 3.517, "grade_2_1": "A-", "gpa_2_2": 3.682, "grade_2_2": "A-", - "gpa_3_1": 3.89, "grade_3_1": "A", + "name": "HOSSAIN MOHAMMAD SHIHAB SHARAR HIMEL ", + "roll": "2031013", + "gpa_1_1": 3.54, + "gpa_1_2": 3.579, + "gpa_2_1": 3.5, + "gpa_2_2": 3.795, + "gpa_3_1": 3.82, + "cgpa": 3.65 }, { - "name": "KOBIR HOSSAIN", "roll": "2031001", - "gpa_1_1": 3.54, "grade_1_1": "A-", "gpa_1_2": 3.421, "grade_1_2": "B+", - "gpa_2_1": 3.517, "grade_2_1": "A-", "gpa_2_2": 3.517, "grade_2_2": "A-", - "gpa_3_1": 3.705, "grade_3_1": "A-", + "name": "MD. OMAYER HAQUE KHAN ", + "roll": "2031016", + "gpa_1_1": 3.42, + "gpa_1_2": 3.559, + "gpa_2_1": 3.483, + "gpa_2_2": 3.739, + "gpa_3_1": 3.807, + "cgpa": 3.6 }, { - "name": "MOST. MAHFUJA ANAN", "roll": "2031023", - "gpa_1_1": 3.37, "grade_1_1": "B+", "gpa_1_2": 3.546, "grade_1_2": "A-", - "gpa_2_1": 3.360, "grade_2_1": "B+", "gpa_2_2": 3.705, "grade_2_2": "A-", - "gpa_3_1": 3.9, "grade_3_1": "A", + "name": "MD. NAFIU-UL-HAQUE", + "roll": "2031017", + "gpa_1_1": 3.32, + "gpa_1_2": 3.289, + "gpa_2_1": 3.355, + "gpa_2_2": 3.733, + "gpa_3_1": 3.64, + "cgpa": 3.47 }, { - "name": "SADIA ISLAM", "roll": "2031027", - "gpa_1_1": 3.44, "grade_1_1": "B+", "gpa_1_2": 3.382, "grade_1_2": "B+", - "gpa_2_1": 3.448, "grade_2_1": "B+", "gpa_2_2": 3.682, "grade_2_2": "A-", - "gpa_3_1": 3.82, "grade_3_1": "B+", + "name": "MD. EMRAN HOSSAIN ", + "roll": "2031018", + "gpa_1_1": 2.85, + "gpa_1_2": 3.283, + "gpa_2_1": 3.116, + "gpa_2_2": 3.653, + "gpa_3_1": 3.45, + "cgpa": 3.27 }, { - "name": "WASHIM SHAHED SOVON", "roll": "2031010", - "gpa_1_1": 3.21, "grade_1_1": "B", "gpa_1_2": 3.487, "grade_1_2": "B+", - "gpa_2_1": 3.430, "grade_2_1": "B+", "gpa_2_2": 3.784, "grade_2_2": "A-", - "gpa_3_1": 3.974, "grade_3_1": "A+", + "name": "TUSHAR BISWAS", + "roll": "2031019", + "gpa_1_1": 2.92, + "gpa_1_2": 3.434, + "gpa_2_1": 3.355, + "gpa_2_2": 3.574, + "gpa_3_1": 3.77, + "cgpa": 3.41 }, { - "name": "MD. NAFIU-UL-HAQUE", "roll": "2031017", - "gpa_1_1": 3.32, "grade_1_1": "B+", "gpa_1_2": 3.289, "grade_1_2": "B+", - "gpa_2_1": 3.355, "grade_2_1": "B+", "gpa_2_2": 3.733, "grade_2_2": "A-", - "gpa_3_1": 3.64, "grade_3_1": "A-", + "name": "MD. SHAHRIAR HOSSAIN ", + "roll": "2031021", + "gpa_1_1": 3.34, + "gpa_1_2": 3.368, + "gpa_2_1": 3.11, + "gpa_2_2": 3.466, + "gpa_3_1": 3.57, + "cgpa": 3.37 }, { - "name": "JASMIN AKTER MUKTA", "roll": "2031032", - "gpa_1_1": 3.22, "grade_1_1": "B", "gpa_1_2": 3.553, "grade_1_2": "A-", - "gpa_2_1": 3.203, "grade_2_1": "B", "gpa_2_2": 3.67, "grade_2_2": "A-", - "gpa_3_1": 3.735, "grade_3_1": "A-", + "name": "MOST. MAHFUJA ANAN", + "roll": "2031023", + "gpa_1_1": 3.45, + "gpa_1_2": 3.55, + "gpa_2_1": 3.36, + "gpa_2_2": 3.7, + "gpa_3_1": 3.9, + "cgpa": 3.59 }, { - "name": "UMME SAMIHA ISLAM", "roll": "2031038", - "gpa_1_1": 3.32, "grade_1_1": "B+", "gpa_1_2": 3.428, "grade_1_2": "B+", - "gpa_2_1": 3.331, "grade_2_1": "B+", "gpa_2_2": 3.506, "grade_2_2": "A-", - "gpa_3_1": 3.78, "grade_3_1": "A-", + "name": "MOST. AFROZA KHATUN ", + "roll": "2031024", + "gpa_1_1": 3.52, + "gpa_1_2": 3.737, + "gpa_2_1": 3.587, + "gpa_2_2": 3.716, + "gpa_3_1": 3.92, + "cgpa": 3.7 }, { - "name": "MD. TORIKUL ISLAM HIMEL", "roll": "2031008", - "gpa_1_1": 3.03, "grade_1_1": "B", "gpa_1_2": 3.513, "grade_1_2": "A-", - "gpa_2_1": 3.413, "grade_2_1": "B+", "gpa_2_2": 3.614, "grade_2_2": "A-", - "gpa_3_1": 3.70, "grade_3_1": "B+", + "name": "MEFTAHUL JANNAT", + "roll": "2031025", + "gpa_1_1": 3.9, + "gpa_1_2": 3.737, + "gpa_2_1": 3.779, + "gpa_2_2": 3.852, + "gpa_3_1": 3.89, + "cgpa": 3.83 }, { - "name": "ASHIKA RAHMAN PANNA", "roll": "2031039", - "gpa_1_1": 3.22, "grade_1_1": "B", "gpa_1_2": 3.434, "grade_1_2": "B+", - "gpa_2_1": 3.273, "grade_2_1": "B+", "gpa_2_2": 3.528, "grade_2_2": "A-", - "gpa_3_1": 3.79, "grade_3_1": "A-", + "name": "SADIA ISLAM", + "roll": "2031027", + "gpa_1_1": 3.44, + "gpa_1_2": 3.382, + "gpa_2_1": 3.448, + "gpa_2_2": 3.682, + "gpa_3_1": 3.82, + "cgpa": 3.55 }, { - "name": "RUKAIYA HOSSAIN", "roll": "2031045", - "gpa_1_1": 3.05, "grade_1_1": "B", "gpa_1_2": 3.454, "grade_1_2": "B+", - "gpa_2_1": 3.326, "grade_2_1": "B+", "gpa_2_2": 3.619, "grade_2_2": "A-", - "gpa_3_1": 3.48, "grade_3_1": "B+", + "name": "MST. SULTANA PARVIN SHIFA ", + "roll": "2031028", + "gpa_1_1": 2.76, + "gpa_1_2": 2.48, + "gpa_2_1": 1.895, + "gpa_2_2": 2.432, + "gpa_3_1": 2.955, + "cgpa": 2.5 }, { - "name": "MD. SHAHRIAR HOSSAIN", "roll": "2031021", - "gpa_1_1": 3.34, "grade_1_1": "B+", "gpa_1_2": 3.368, "grade_1_2": "B+", - "gpa_2_1": 3.110, "grade_2_1": "B", "gpa_2_2": 3.486, "grade_2_2": "B+", - "gpa_3_1": 3.57, "grade_3_1": "A-", + "name": "SHAFKAT TASEEN SADIYA ", + "roll": "2031030", + "gpa_1_1": 3.58, + "gpa_1_2": 3.579, + "gpa_2_1": 3.651, + "gpa_2_2": 3.852, + "gpa_3_1": 3.86, + "cgpa": 3.7 }, { - "name": "TUSHAR BISWAS", "roll": "2031019", - "gpa_1_1": 2.92, "grade_1_1": "B-", "gpa_1_2": 3.434, "grade_1_2": "B+", - "gpa_2_1": 3.355, "grade_2_1": "B+", "gpa_2_2": 3.574, "grade_2_2": "A-", - "gpa_3_1": 3.77, "grade_3_1": "A-", + "name": "JASMIN AKTER MUKTA ", + "roll": "2031032", + "gpa_1_1": 3.22, + "gpa_1_2": 3.553, + "gpa_2_1": 3.203, + "gpa_2_2": 3.67, + "gpa_3_1": 3.735, + "cgpa": 3.48 }, { - "name": "MST. ANIKA TABASSUM", "roll": "2031037", - "gpa_1_1": 3.17, "grade_1_1": "B", "gpa_1_2": 3.414, "grade_1_2": "B+", - "gpa_2_1": 3.233, "grade_2_1": "B", "gpa_2_2": 3.369, "grade_2_2": "B+", - "gpa_3_1": 3.54, "grade_3_1": "A-", + "name": "NAFIJA TASNIM ANNANA ", + "roll": "2031034", + "gpa_1_1": 2.78, + "gpa_1_2": 3.132, + "gpa_2_1": 2.93, + "gpa_2_2": 3.381, + "gpa_3_1": 3.19, + "cgpa": 3.08 }, { - "name": "MD. HABIBUL HASAN", "roll": "2031011", - "gpa_1_1": 3.34, "grade_1_1": "B+", "gpa_1_2": 3.382, "grade_1_2": "B+", - "gpa_2_1": 3.157, "grade_2_1": "B", "gpa_2_2": 3.301, "grade_2_2": "B+", - "gpa_3_1": 3.44, "grade_3_1": "B+", + "name": "MST. AFROZA KHATUN ", + "roll": "2031036", + "gpa_1_1": 3.67, + "gpa_1_2": 3.638, + "gpa_2_1": 3.605, + "gpa_2_2": 3.92, + "gpa_3_1": 3.85, + "cgpa": 3.74 }, { - "name": "AUTHORY BISWAS BIDDA", "roll": "2031048", - "gpa_1_1": 3.25, "grade_1_1": "B", "gpa_1_2": 3.211, "grade_1_2": "B", - "gpa_2_1": 3.070, "grade_2_1": "B", "gpa_2_2": 3.631, "grade_2_2": "A-", - "gpa_3_1": 3.58, "grade_3_1": "A-", + "name": "MST. ANIKA TABASSUM ", + "roll": "2031037", + "gpa_1_1": 3.17, + "gpa_1_2": 3.414, + "gpa_2_1": 3.233, + "gpa_2_2": 3.369, + "gpa_3_1": 3.54, + "cgpa": 3.35 }, { - "name": "MD. EMRAN HOSSAIN", "roll": "2031018", - "gpa_1_1": 2.85, "grade_1_1": "B-", "gpa_1_2": 3.283, "grade_1_2": "B+", - "gpa_2_1": 3.116, "grade_2_1": "B", "gpa_2_2": 3.653, "grade_2_2": "A-", - "gpa_3_1": 3.45, "grade_3_1": "A-", + "name": "UMME SAMIHA ISLAM ", + "roll": "2031038", + "gpa_1_1": 3.32, + "gpa_1_2": 3.428, + "gpa_2_1": 3.331, + "gpa_2_2": 3.506, + "gpa_3_1": 3.78, + "cgpa": 3.47 }, { - "name": "MAHMUDA JANNAT OWYSHE", "roll": "2031044", - "gpa_1_1": 3.10, "grade_1_1": "B", "gpa_1_2": 3.289, "grade_1_2": "B+", - "gpa_2_1": 2.797, "grade_2_1": "B-", "gpa_2_2": 3.483, "grade_2_2": "B+", - "gpa_3_1": 2.9, "grade_3_1": "B+", + "name": "ASHIKA RAHMAN PANNA", + "roll": "2031039", + "gpa_1_1": 3.22, + "gpa_1_2": 3.434, + "gpa_2_1": 3.273, + "gpa_2_2": 3.528, + "gpa_3_1": 3.79, + "cgpa": 3.45 }, { - "name": "MD. MASHUK E RAHMAN", "roll": "2031006", - "gpa_1_1": 3.16, "grade_1_1": "B", "gpa_1_2": 3.224, "grade_1_2": "B", - "gpa_2_1": 2.791, "grade_2_1": "B-", "gpa_2_2": 3.472, "grade_2_2": "B+", - "gpa_3_1": 3.566, "grade_3_1": "A-", + "name": "MST. ANANNA AFRIN ", + "roll": "2031040", + "gpa_1_1": 3.44, + "gpa_1_2": 3.592, + "gpa_2_1": 3.64, + "gpa_2_2": 3.864, + "gpa_3_1": 3.91, + "cgpa": 3.69 }, { - "name": "MAHFUZA KHATUN", "roll": "2031046", - "gpa_1_1": 2.88, "grade_1_1": "B-", "gpa_1_2": 3.072, "grade_1_2": "B", - "gpa_2_1": 2.953, "grade_2_1": "B-", "gpa_2_2": 3.483, "grade_2_2": "B+", - "gpa_3_1": 3.49, "grade_3_1": "B+", + "name": "ORPA AHMED ", + "roll": "2031041", + "gpa_1_1": 3.53, + "gpa_1_2": 3.704, + "gpa_2_1": 3.552, + "gpa_2_2": 3.739, + "gpa_3_1": 3.89, + "cgpa": 3.68 }, { - "name": "MD. RASHIDUL ISLAM", "roll": "2031009", - "gpa_1_1": 2.96, "grade_1_1": "B-", "gpa_1_2": 3.184, "grade_1_2": "B", - "gpa_2_1": 2.744, "grade_2_1": "C+", "gpa_2_2": 3.426, "grade_2_2": "B+", - "gpa_3_1": 3.468, "grade_3_1": "B+", + "name": "KHADIJA AKTER ", + "roll": "2031042", + "gpa_1_1": 3.36, + "gpa_1_2": 3.559, + "gpa_2_1": 3.512, + "gpa_2_2": 3.682, + "gpa_3_1": 3.89, + "cgpa": 3.6 }, { - "name": "MD. NAIMUL ISLAM", "roll": "2031004", - "gpa_1_1": 2.71, "grade_1_1": "C", "gpa_1_2": 3.039, "grade_1_2": "B", - "gpa_2_1": 3.047, "grade_2_1": "B", "gpa_2_2": 3.438, "grade_2_2": "B+", - "gpa_3_1": 3.56, "grade_3_1": "A", + "name": "JANNATUL MAOUA PROME ", + "roll": "2031043", + "gpa_1_1": 2.7, + "gpa_1_2": 3.039, + "gpa_2_1": 3.07, + "gpa_2_2": 3.267, + "gpa_3_1": 3.41, + "cgpa": 3.1 }, { - "name": "NAFiJA TASNIM ANANNA", "roll": "2031034", // Corrected name here - "gpa_1_1": 2.78, "grade_1_1": "B-", "gpa_1_2": 3.132, "grade_1_2": "B", - "gpa_2_1": 2.930, "grade_2_1": "B-", "gpa_2_2": 3.381, "grade_2_2": "B+", - "gpa_3_1": 3.19, "grade_3_1": "B", + "name": "MAHMUDA JANNAT OWYSHE", + "roll": "2031044", + "gpa_1_1": 3.1, + "gpa_1_2": 3.289, + "gpa_2_1": 2.797, + "gpa_2_2": 3.483, + "gpa_3_1": 2.9, + "cgpa": 3.11 }, { - "name": "JANNATUL MAOUA PROME", "roll": "2031043", - "gpa_1_1": 2.70, "grade_1_1": "C", "gpa_1_2": 3.039, "grade_1_2": "B", - "gpa_2_1": 3.070, "grade_2_1": "B", "gpa_2_2": 3.267, "grade_2_2": "B", - "gpa_3_1": 3.41, "grade_3_1": "B+", + "name": "RUKAIYA HOSSAIN ", + "roll": "2031045", + "gpa_1_1": 3.05, + "gpa_1_2": 3.454, + "gpa_2_1": 3.326, + "gpa_2_2": 3.619, + "gpa_3_1": 3.48, + "cgpa": 3.39 }, { - "name": "M.D. BELAL HOSSAIN BADHON", "roll": "2031007", - "gpa_1_1": 2.82, "grade_1_1": "B-", "gpa_1_2": 3.059, "grade_1_2": "B", - "gpa_2_1": 2.895, "grade_2_1": "B-", "gpa_2_2": 3.233, "grade_2_2": "B", - "gpa_3_1": 3.391, "grade_3_1": "B+", + "name": "MAHFUZA KHATUN ", + "roll": "2031046", + "gpa_1_1": 2.9, + "gpa_1_2": 3.072, + "gpa_2_1": 2.953, + "gpa_2_2": 3.483, + "gpa_3_1": 3.49, + "cgpa": 3.18 }, { - "name": "MST. SULTANA PARVIN SHIFA", "roll": "2031028", - "gpa_1_1": 2.76, "grade_1_1": "C", "gpa_1_2": 2.480, "grade_1_2": "C", - "gpa_2_1": 1.895, "grade_2_1": "F", "gpa_2_2": 2.438, "grade_2_2": "C", - "gpa_3_1": 2.955, "grade_3_1": "C", + "name": "AUTHOY BISWAS BIDDA ", + "roll": "2031048", + "gpa_1_1": 3.25, + "gpa_1_2": 3.211, + "gpa_2_1": 3.07, + "gpa_2_2": 3.631, + "gpa_3_1": 3.58, + "cgpa": 3.35 } ].map(student => { let imageUrl = studentImageMap[student.roll]; @@ -279,11 +428,9 @@ const studentData = [ if (!imageUrl) { imageUrl = "https://placehold.co/96x96/aabbcc/ffffff?text=No+Image"; // Generic placeholder } - // Calculate new CGPA as the average of the five semesters const totalGpa = (student.gpa_1_1 + student.gpa_1_2 + student.gpa_2_1 + student.gpa_2_2 + student.gpa_3_1); const newCgpa = (totalGpa / 5).toFixed(2); - return { ...student, imageUrl, cgpa: parseFloat(newCgpa) }; }); @@ -294,15 +441,14 @@ const chartColors = ['#6366F1', '#EC4899', '#10B981', '#F59E0B', '#EF4444', '#3B const CustomTooltip = ({ active, payload, label }) => { if (active && payload && payload.length) { const data = payload[0].payload; - // Collect all student rolls into a single comma-separated string + // Format students list to fit without a scrollbar const studentRolls = data.students.map(student => student.roll).join(', '); - return ( -
-

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

+
+

{`CGPA \u2265 ${data.threshold}`}

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

Student Rolls:

-
+
{studentRolls.length > 0 ? ( {studentRolls} ) : ( @@ -315,6 +461,449 @@ const CustomTooltip = ({ active, payload, label }) => { return null; }; +// Comparison View Component +const ComparisonView = ({ comparedStudents, onBack }) => { + // Prepare data for comparison chart + const comparisonChartData = useMemo(() => { + const semesterOrder = ['1_1', '1_2', '2_1', '2_2', '3_1']; + const semesterNames = { + '1_1': 'First Semester', + '1_2': 'Second Semester', + '2_1': 'Third Semester', + '2_2': 'Fourth Semester', + '3_1': 'Fifth Semester' + }; + + // Create an array with one object per semester + return semesterOrder.map(sem => { + const semesterKey = `gpa_${sem}`; + const result = { + semester: semesterNames[sem] + }; + + // Add each student's GPA for this semester + comparedStudents.forEach((student, index) => { + result[student.roll] = student[semesterKey]; + }); + + return result; + }); + }, [comparedStudents]); + + // Dynamic Y-axis domain for the comparison chart + const comparisonGpaDomain = useMemo(() => { + if (!comparisonChartData.length) return [1.0, 4.0]; + try { + const allGpas = comparisonChartData.flatMap(d => + Object.keys(d) + .filter(key => key !== 'semester' && d[key] !== null && d[key] !== undefined) + .map(key => d[key]) + ); + if (allGpas.length === 0) return [1.0, 4.0]; + const minGpa = Math.min(...allGpas); + const maxGpa = Math.max(...allGpas); + if (isNaN(minGpa) || isNaN(maxGpa)) { + return [1.0, 4.0]; + } + const padding = 0.1; + const finalMin = Math.max(0, minGpa - padding); + const finalMax = Math.min(4.0, maxGpa + padding); + if (finalMin === finalMax) { + return [Math.max(0, finalMin - 0.5), Math.min(4.0, finalMax + 0.5)]; + } + return [finalMin, finalMax]; + } catch (error) { + console.error("Error calculating comparison GPA domain:", error); + return [1.0, 4.0]; + } + }, [comparisonChartData]); + + // Find the highest value in each category for highlighting + const findHighestInCategory = useCallback((category) => { + let highestValue = -Infinity; + let highestStudents = []; + + comparedStudents.forEach(student => { + let value; + if (category === 'cgpa') { + value = student.cgpa; + } else { + value = student[category]; + } + + if (value > highestValue) { + highestValue = value; + highestStudents = [student.roll]; + } else if (value === highestValue) { + highestStudents.push(student.roll); + } + }); + + return highestStudents; + }, [comparedStudents]); + + // Get colors for the lines + const getLineColor = (index) => { + return chartColors[index % chartColors.length]; + }; + + return ( +
+
+ {/* Back Button */} + + +
+ {/* Header */} +
+

+ Student Comparison +

+

+ Comparing {comparedStudents.length} students +

+
+ + {/* Comparison Chart */} +
+

+ GPA Trend Comparison +

+ + + + + value.toFixed(2)} + /> + + + + {comparedStudents.map((student, index) => ( + + ))} + + +
+ + {/* Comparison Table */} +
+

+ Detailed Comparison +

+
+ + + + + {comparedStudents.map(student => ( + + ))} + + + + {/* First Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Second Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Third Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Fourth Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Fifth Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* CGPA */} + + + {comparedStudents.map(student => ( + + ))} + + +
+ Category + + {student.name}
+ ({student.roll}) +
+ First Semester + + {student.gpa_1_1 !== undefined ? + `${student.gpa_1_1.toFixed(3)}` : + 'N/A'} +
+ Second Semester + + {student.gpa_1_2 !== undefined ? + `${student.gpa_1_2.toFixed(3)}` : + 'N/A'} +
+ Third Semester + + {student.gpa_2_1 !== undefined ? + `${student.gpa_2_1.toFixed(3)}` : + 'N/A'} +
+ Fourth Semester + + {student.gpa_2_2 !== undefined ? + `${student.gpa_2_2.toFixed(3)}` : + 'N/A'} +
+ Fifth Semester + + {student.gpa_3_1 !== undefined ? + `${student.gpa_3_1.toFixed(3)}` : + 'N/A'} +
+ CGPA + + {student.cgpa} +
+
+
+
+
+
+ ); +}; + +// Compare Modal Component +const CompareModal = ({ isOpen, onClose, studentData, onCompare }) => { + const [searchTerm, setSearchTerm] = useState(''); + const [selectedStudents, setSelectedStudents] = useState([]); + + // Filter students based on search term + const filteredStudents = useMemo(() => { + if (!searchTerm.trim()) return []; + + return studentData.filter(student => + student.name.toLowerCase().includes(searchTerm.toLowerCase()) || + student.roll.includes(searchTerm) + ); + }, [searchTerm, studentData]); + + // Handle adding a student to comparison list + const handleAddStudent = useCallback((student) => { + if (!selectedStudents.find(s => s.roll === student.roll)) { + setSelectedStudents(prev => [...prev, student]); + } + setSearchTerm(''); + }, [selectedStudents]); + + // Handle removing a student from comparison list + const handleRemoveStudent = useCallback((roll) => { + setSelectedStudents(prev => prev.filter(s => s.roll !== roll)); + }, []); + + // Start comparison + const handleStartComparison = useCallback(() => { + if (selectedStudents.length >= 2) { + onCompare(selectedStudents); + onClose(); + } + }, [selectedStudents, onCompare, onClose]); + + if (!isOpen) return null; + + return ( +
+
+
+

Compare Students

+

Select at least 2 students to compare

+
+ +
+ {/* Search Section */} +
+
+ setSearchTerm(e.target.value)} + /> +
+ + {/* Search Results */} + {searchTerm && filteredStudents.length > 0 && ( +
+ {filteredStudents.map(student => ( +
handleAddStudent(student)} + > +
+ {student.name} + ({student.roll}) +
+ +
+ ))} +
+ )} +
+ + {/* Selected Students */} +
+

Selected Students ({selectedStudents.length})

+ {selectedStudents.length === 0 ? ( +

+ No students selected. Search and add students above. +

+ ) : ( +
+ {selectedStudents.map(student => ( +
+
+ {student.name} + ({student.roll}) +
+ +
+ ))} +
+ )} +
+ + {/* Actions */} +
+ + +
+
+
+
+ ); +}; + function App() { const [searchTerm, setSearchTerm] = useState(''); const [filteredStudents, setSearchStudents] = useState([]); @@ -322,6 +911,9 @@ function App() { const [sortConfig, setSortConfig] = useState({ key: null, direction: 'ascending' }); const [isMobile, setIsMobile] = useState(false); const [chartError, setChartError] = useState(false); + const [showCompareModal, setShowCompareModal] = useState(false); + const [showComparisonView, setShowComparisonView] = useState(false); + const [comparedStudents, setComparedStudents] = useState([]); useEffect(() => { const handleResize = () => { @@ -340,7 +932,6 @@ function App() { // FIXED PRINT FUNCTION const handlePrintResult = useCallback(() => { if (!selectedStudent) return; - // Helper to get grade or 'N/A' const getGrade = (gpaKey, gradeKey) => { const gpaValue = selectedStudent[gpaKey]; @@ -350,7 +941,6 @@ function App() { } return 'N/A'; }; - const printContent = ` @@ -462,29 +1052,29 @@ function App() { Semester - GPA (Grade) + GPA First Semester - ${getGrade('gpa_1_1', 'grade_1_1')} + ${selectedStudent.gpa_1_1 !== undefined ? selectedStudent.gpa_1_1.toFixed(3) : 'N/A'} Second Semester - ${getGrade('gpa_1_2', 'grade_1_2')} + ${selectedStudent.gpa_1_2 !== undefined ? selectedStudent.gpa_1_2.toFixed(3) : 'N/A'} Third Semester - ${getGrade('gpa_2_1', 'grade_2_1')} + ${selectedStudent.gpa_2_1 !== undefined ? selectedStudent.gpa_2_1.toFixed(3) : 'N/A'} Fourth Semester - ${getGrade('gpa_2_2', 'grade_2_2')} + ${selectedStudent.gpa_2_2 !== undefined ? selectedStudent.gpa_2_2.toFixed(3) : 'N/A'} Fifth Semester - ${getGrade('gpa_3_1', 'grade_3_1')} + ${selectedStudent.gpa_3_1 !== undefined ? selectedStudent.gpa_3_1.toFixed(3) : 'N/A'} @@ -496,11 +1086,9 @@ function App() { `; - 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(); @@ -515,15 +1103,12 @@ function App() { 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]); @@ -539,7 +1124,6 @@ function App() { 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; @@ -549,7 +1133,6 @@ function App() { aValue = parseInt(aValue, 10) || 0; bValue = parseInt(bValue, 10) || 0; } - if (aValue < bValue) { return sortConfig.direction === 'ascending' ? -1 : 1; } @@ -572,7 +1155,7 @@ function App() { const getSortIndicator = (key) => { if (sortConfig.key === key) { - return sortConfig.direction === 'ascending' ? ' ▲' : ' ▼'; + return sortConfig.direction === 'ascending' ? ' \u25b2' : ' \u25bc'; } return ''; }; @@ -590,7 +1173,6 @@ function App() { } }); }); - // Convert to array of objects for recharts, maintaining order return grades .filter(grade => gradeCounts[grade] > 0) // Only include grades that exist in data @@ -617,13 +1199,11 @@ function App() { } }); }); - // 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', @@ -632,7 +1212,6 @@ function App() { '2-2': 'Fourth Semester', '3-1': 'Fifth Semester', }; - // Sort semesters for proper line chart display and apply display names const semesterOrder = ['1-1', '1-2', '2-1', '2-2', '3-1']; return trend.sort((a, b) => semesterOrder.indexOf(a.semester) - semesterOrder.indexOf(b.semester)) @@ -642,21 +1221,17 @@ function App() { // Prepare data for Selected Student's GPA Trend Line Chart const selectedStudentGpaTrendData = useMemo(() => { if (!selectedStudent) return []; - const trend = []; const semesterOrder = ['1_1', '1_2', '2_1', '2_2', '3_1']; - // Process each semester in order for (const sem of semesterOrder) { const gpaKey = `gpa_${sem}`; const semesterName = sem.replace('_', '-'); let gpaValue = selectedStudent[gpaKey]; - // Ensure gpaValue is a valid number if (typeof gpaValue === 'string') { gpaValue = parseFloat(gpaValue); } - // Only include valid GPA values if (typeof gpaValue === 'number' && !isNaN(gpaValue) && gpaValue >= 0 && gpaValue <= 4.0) { trend.push({ @@ -665,7 +1240,6 @@ function App() { }); } } - // Map semester names for display const semesterDisplayNames = { '1-1': 'First Semester', @@ -674,7 +1248,6 @@ function App() { '2-2': 'Fourth Semester', '3-1': 'Fifth Semester', }; - // Sort by semester order return trend.sort((a, b) => { const orderA = semesterOrder.indexOf(`gpa_${a.semester.replace('-', '_')}`); @@ -689,17 +1262,14 @@ function App() { // Dynamic Y-axis domain for individual student GPA trend const selectedStudentGpaDomain = useMemo(() => { if (!selectedStudentGpaTrendData.length) return [1.0, 4.0]; - try { const gpas = selectedStudentGpaTrendData.map(d => d.gpa); const minGpa = Math.min(...gpas); const maxGpa = Math.max(...gpas); - // Ensure min and max are valid numbers if (isNaN(minGpa) || isNaN(maxGpa)) { return [1.0, 4.0]; } - // Add a small buffer for better visualization. // Ensure the domain is at least a small range if min and max are the same. const padding = 0.1; @@ -719,17 +1289,14 @@ function App() { // 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), @@ -744,10 +1311,8 @@ function App() { // 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, @@ -780,7 +1345,6 @@ function App() {
); } - if (!selectedStudentGpaTrendData.length) { return (
@@ -788,7 +1352,6 @@ function App() {
); } - try { return ( @@ -850,8 +1413,166 @@ function App() { } }, [selectedStudentGpaTrendData, selectedStudentGpaDomain, chartError]); + // Handle comparison + const handleCompare = useCallback((students) => { + setComparedStudents(students); + setShowComparisonView(true); + }, []); + + // Print Table Function + const handlePrintTable = useCallback(() => { + // Create a printable version of the table + const printContent = ` + + + + All Students Results + + + + + + + `; + 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 + }; + }, [sortedStudentData]); + + // If in comparison view, show the comparison page + if (showComparisonView) { + return ( + setShowComparisonView(false)} + /> + ); + } + return (
+ {/* Compare Button - Now using text instead of icon */} + + + {/* Compare Modal */} + setShowCompareModal(false)} + studentData={studentData} + onCompare={handleCompare} + /> +
{/* Header */}
@@ -862,7 +1583,6 @@ function App() { Islamic University Bangladesh

- {/* Search Section - Made Sticky */}
@@ -880,7 +1600,6 @@ function App() { Search
- {/* Search Results / Suggestions */} {searchTerm.trim() !== '' && filteredStudents.length > 0 && (
@@ -902,14 +1621,12 @@ function App() {
)} - {searchTerm.trim() !== '' && filteredStudents.length === 0 && (
No students found matching "${searchTerm}". Please try a different name or roll.
)}
- {/* Dashboard Content */}
{/* Conditional rendering based on selectedStudent */} @@ -929,11 +1646,9 @@ function App() { e.target.src="https://placehold.co/96x96/aabbcc/ffffff?text=No+Image" }} /> -

Name: {selectedStudent.name}

Roll: {selectedStudent.roll}

CGPA: {selectedStudent.cgpa}

-

Semester-wise Grades:

    @@ -941,7 +1656,7 @@ function App() { First Semester: {selectedStudent.gpa_1_1 !== undefined && selectedStudent.gpa_1_1 !== null ? - `${selectedStudent.gpa_1_1.toFixed(3)} (${selectedStudent.grade_1_1 || 'N/A'})` : + `${selectedStudent.gpa_1_1.toFixed(3)}` : 'N/A'} @@ -949,7 +1664,7 @@ function App() { Second Semester: {selectedStudent.gpa_1_2 !== undefined && selectedStudent.gpa_1_2 !== null ? - `${selectedStudent.gpa_1_2.toFixed(3)} (${selectedStudent.grade_1_2 || 'N/A'})` : + `${selectedStudent.gpa_1_2.toFixed(3)}` : 'N/A'} @@ -957,7 +1672,7 @@ function App() { Third Semester: {selectedStudent.gpa_2_1 !== undefined && selectedStudent.gpa_2_1 !== null ? - `${selectedStudent.gpa_2_1.toFixed(3)} (${selectedStudent.grade_2_1 || 'N/A'})` : + `${selectedStudent.gpa_2_1.toFixed(3)}` : 'N/A'} @@ -965,7 +1680,7 @@ function App() { Fourth Semester: {selectedStudent.gpa_2_2 !== undefined && selectedStudent.gpa_2_2 !== null ? - `${selectedStudent.gpa_2_2.toFixed(3)} (${selectedStudent.grade_2_2 || 'N/A'})` : + `${selectedStudent.gpa_2_2.toFixed(3)}` : 'N/A'} @@ -973,13 +1688,12 @@ function App() { Fifth Semester: {selectedStudent.gpa_3_1 !== undefined && selectedStudent.gpa_3_1 !== null ? - `${selectedStudent.gpa_3_1.toFixed(3)} (${selectedStudent.grade_3_1 || 'N/A'})` : + `${selectedStudent.gpa_3_1.toFixed(3)}` : '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 @@ -1017,13 +1728,11 @@ function App() {

Average CGPA: {overallStats.averageCGPA}

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

Overall Average GPA Trend by Semester

-
- {/* Cumulative CGPA Distribution */}

Cumulative CGPA Distribution

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

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

+

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

+

Student Rolls:

+
+ {studentRolls.length > 0 ? ( + {studentRolls} + ) : ( + 'No students in this range.' + )} +
+
+ ); + } + return null; +}; + +// Comparison View Component +const ComparisonView = ({ comparedStudents, onBack }) => { + // Prepare data for comparison chart + const comparisonChartData = useMemo(() => { + const semesterOrder = ['1_1', '1_2', '2_1', '2_2', '3_1']; + const semesterNames = { + '1_1': 'First Semester', + '1_2': 'Second Semester', + '2_1': 'Third Semester', + '2_2': 'Fourth Semester', + '3_1': 'Fifth Semester' + }; + + // Create an array with one object per semester + return semesterOrder.map(sem => { + const semesterKey = `gpa_${sem}`; + const result = { + semester: semesterNames[sem] + }; + + // Add each student's GPA for this semester + comparedStudents.forEach((student, index) => { + result[student.roll] = student[semesterKey]; + }); + + return result; + }); + }, [comparedStudents]); + + // Find the highest value in each category for highlighting + const findHighestInCategory = useCallback((category) => { + let highestValue = -Infinity; + let highestStudents = []; + + comparedStudents.forEach(student => { + let value; + if (category === 'cgpa') { + value = student.cgpa; + } else { + value = student[category]; + } + + if (value > highestValue) { + highestValue = value; + highestStudents = [student.roll]; + } else if (value === highestValue) { + highestStudents.push(student.roll); + } + }); + + return highestStudents; + }, [comparedStudents]); + + // Get colors for the lines + const getLineColor = (index) => { + return chartColors[index % chartColors.length]; + }; + + return ( +
+
+ {/* Back Button */} + + +
+ {/* Header */} +
+

+ Student Comparison +

+

+ Comparing {comparedStudents.length} students +

+
+ + {/* Comparison Chart */} +
+

+ GPA Trend Comparison +

+ + + + + + + + + {comparedStudents.map((student, index) => ( + + ))} + + +
+ + {/* Comparison Table */} +
+

+ Detailed Comparison +

+
+ + + + + {comparedStudents.map(student => ( + + ))} + + + + {/* First Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Second Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Third Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Fourth Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* Fifth Semester */} + + + {comparedStudents.map(student => ( + + ))} + + + {/* CGPA */} + + + {comparedStudents.map(student => ( + + ))} + + +
+ Category + + {student.name}
+ ({student.roll}) +
+ First Semester + + {student.gpa_1_1 !== undefined ? + `${student.gpa_1_1.toFixed(3)}` : + 'N/A'} +
+ Second Semester + + {student.gpa_1_2 !== undefined ? + `${student.gpa_1_2.toFixed(3)}` : + 'N/A'} +
+ Third Semester + + {student.gpa_2_1 !== undefined ? + `${student.gpa_2_1.toFixed(3)}` : + 'N/A'} +
+ Fourth Semester + + {student.gpa_2_2 !== undefined ? + `${student.gpa_2_2.toFixed(3)}` : + 'N/A'} +
+ Fifth Semester + + {student.gpa_3_1 !== undefined ? + `${student.gpa_3_1.toFixed(3)}` : + 'N/A'} +
+ CGPA + + {student.cgpa} +
+
+
+
+
+
+ ); +}; + +// Compare Modal Component +const CompareModal = ({ isOpen, onClose, studentData, onCompare }) => { + const [searchTerm, setSearchTerm] = useState(''); + const [selectedStudents, setSelectedStudents] = useState([]); + + // Filter students based on search term + const filteredStudents = useMemo(() => { + if (!searchTerm.trim()) return []; + + return studentData.filter(student => + student.name.toLowerCase().includes(searchTerm.toLowerCase()) || + student.roll.includes(searchTerm) + ); + }, [searchTerm, studentData]); + + // Handle adding a student to comparison list + const handleAddStudent = useCallback((student) => { + if (!selectedStudents.find(s => s.roll === student.roll)) { + setSelectedStudents(prev => [...prev, student]); + } + setSearchTerm(''); + }, [selectedStudents]); + + // Handle removing a student from comparison list + const handleRemoveStudent = useCallback((roll) => { + setSelectedStudents(prev => prev.filter(s => s.roll !== roll)); + }, []); + + // Start comparison + const handleStartComparison = useCallback(() => { + if (selectedStudents.length >= 2) { + onCompare(selectedStudents); + onClose(); + } + }, [selectedStudents, onCompare, onClose]); + + if (!isOpen) return null; + + return ( +
+
+
+

Compare Students

+

Select at least 2 students to compare

+
+ +
+ {/* Search Section */} +
+
+ setSearchTerm(e.target.value)} + /> +
+ + {/* Search Results */} + {searchTerm && filteredStudents.length > 0 && ( +
+ {filteredStudents.map(student => ( +
handleAddStudent(student)} + > +
+ {student.name} + ({student.roll}) +
+ +
+ ))} +
+ )} +
+ + {/* Selected Students */} +
+

Selected Students ({selectedStudents.length})

+ {selectedStudents.length === 0 ? ( +

+ No students selected. Search and add students above. +

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

Error displaying GPA chart. Please try another student.

+ +
+ ); + } + if (!selectedStudentGpaTrendData.length) { + return ( +
+ No GPA trend data available for this student. +
+ ); + } + try { + return ( + + + + + index === 0 ? '' : value.toFixed(2)} + /> + + + + + ); + } catch (error) { + console.error("Chart rendering error:", error); + setChartError(true); + return ( +
+ Error displaying GPA chart. Please try another student. +
+ ); + } + }, [selectedStudentGpaTrendData, selectedStudentGpaDomain, chartError]); + + // Handle comparison + const handleCompare = useCallback((students) => { + setComparedStudents(students); + setShowComparisonView(true); + }, []); + + // Print Table Function + const handlePrintTable = useCallback(() => { + // Create a printable version of the table + const printContent = ` + + + + All Students Results + + + + + + + `; + 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 + }; + }, [sortedStudentData]); + + // If in comparison view, show the comparison page + if (showComparisonView) { + return ( + setShowComparisonView(false)} + /> + ); + } + + return ( +
+ {/* Compare Button - Now using text instead of icon */} + + + {/* Compare Modal */} + setShowCompareModal(false)} + studentData={studentData} + onCompare={handleCompare} + /> + +
+ {/* Header */} +
+

+ ESG 20-21 Result Dashboard +

+

+ Islamic University Bangladesh +

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

Matching Students:

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

Selected Student Details

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

Name: {selectedStudent.name}

+

Roll: {selectedStudent.roll}

+

CGPA: {selectedStudent.cgpa}

+
+

Semester-wise Grades:

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

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

+ {renderStudentGpaChart()} + {selectedStudent && ( +

+ Developed by MD. PARVEJ HOSSAIN +

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

Overall Statistics:

+

Total Students: {studentData.length}

+

Highest CGPA: {overallStats.maxCGPA}

+

Lowest CGPA: {overallStats.minCGPA}

+

Average CGPA: {overallStats.averageCGPA}

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

Overall Average GPA Trend by Semester

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

Cumulative CGPA Distribution

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

All Student Results

+
+ + + + + + + + + + + + + + { + sortedStudentData.map((student) => ( + + + + + + + + + + + )) + } +
requestSort('name')} + > + Name {getSortIndicator('name')} + requestSort('roll')} + > + Roll {getSortIndicator('roll')} + requestSort('gpa_1_1')} + > + First Semester {getSortIndicator('gpa_1_1')} + requestSort('gpa_1_2')} + > + Second Semester {getSortIndicator('gpa_1_2')} + requestSort('gpa_2_1')} + > + Third Semester {getSortIndicator('gpa_2_1')} + requestSort('gpa_2_2')} + > + Fourth Semester {getSortIndicator('gpa_2_2')} + requestSort('gpa_3_1')} + > + Fifth Semester {getSortIndicator('gpa_3_1')} + requestSort('cgpa')} + > + CGPA {getSortIndicator('cgpa')} +
{student.name}{student.roll} + {student.gpa_1_1 !== undefined ? + `${student.gpa_1_1.toFixed(3)}` : + 'N/A'} + + {student.gpa_1_2 !== undefined ? + `${student.gpa_1_2.toFixed(3)}` : + 'N/A'} + + {student.gpa_2_1 !== undefined ? + `${student.gpa_2_1.toFixed(3)}` : + 'N/A'} + + {student.gpa_2_2 !== undefined ? + `${student.gpa_2_2.toFixed(3)}` : + 'N/A'} + + {student.gpa_3_1 !== undefined ? + `${student.gpa_3_1.toFixed(3)}` : + 'N/A'} + {student.cgpa}
+
+ {/* Print Table Button */} +
+ +
+
+ )} +
+
+
+ ); +} +export default App; \ No newline at end of file