From 23bf4fd86adcc8f659e1e0e6d4f7a089d169a37f Mon Sep 17 00:00:00 2001 From: Shanmuga Krishnan S M Date: Thu, 20 Aug 2026 11:55:15 +0530 Subject: [PATCH] feat(dashboard): complete professional UI overhaul for student dashboard with executive analytics & matrix agenda --- .../StudentDashboard/StudentDashboard.tsx | 954 ++++++++++-------- 1 file changed, 539 insertions(+), 415 deletions(-) diff --git a/src/pages/StudentDashboard/StudentDashboard.tsx b/src/pages/StudentDashboard/StudentDashboard.tsx index 49374de..1a22fb5 100644 --- a/src/pages/StudentDashboard/StudentDashboard.tsx +++ b/src/pages/StudentDashboard/StudentDashboard.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { GraduationCap, Clock, MapPin, Users, @@ -7,7 +7,7 @@ import { AlertCircle, User, HelpCircle, Compass, ChevronRight, ExternalLink, Award, CheckCircle2, AlertTriangle, FileText, Calculator, TrendingUp, Layers, BookOpen, Check, Mail, Phone, RefreshCw, Edit2, Save, FlaskConical, - Star, Search, Filter, Grid, List, X + Star, Search, Filter, Grid, List, X, Copy, CheckCheck, Printer, Info } from 'lucide-react'; import { Link } from 'react-router-dom'; import { useAuth } from '@/context/AuthContext'; @@ -48,6 +48,7 @@ export default function StudentDashboard() { const [ttViewMode, setTtViewMode] = useState<'grid' | 'daily'>('grid'); const [ttFilterType, setTtFilterType] = useState<'ALL' | 'LAB' | 'THEORY' | 'ELECTIVE' | 'FREE'>('ALL'); const [ttSearchQuery, setTtSearchQuery] = useState(''); + const [copiedReg, setCopiedReg] = useState(false); const [starredPeriods, setStarredPeriods] = useState>(() => { try { const saved = localStorage.getItem('rit_starred_periods'); @@ -88,14 +89,14 @@ export default function StudentDashboard() { }; const todayKey = dayNamesMap[new Date().getDay()] || null; - const periodTimeRanges: Record = { - 1: { start: 525, end: 580 }, - 2: { start: 580, end: 635 }, - 3: { start: 650, end: 705 }, - 4: { start: 705, end: 760 }, - 5: { start: 810, end: 865 }, - 6: { start: 865, end: 920 }, - 7: { start: 920, end: 975 }, + const periodTimeRanges: Record = { + 1: { start: 525, end: 580, timeLabel: '08:45 AM - 09:40 AM' }, + 2: { start: 580, end: 635, timeLabel: '09:40 AM - 10:35 AM' }, + 3: { start: 650, end: 705, timeLabel: '10:50 AM - 11:45 AM' }, + 4: { start: 705, end: 760, timeLabel: '11:45 AM - 12:40 PM' }, + 5: { start: 810, end: 865, timeLabel: '01:30 PM - 02:25 PM' }, + 6: { start: 865, end: 920, timeLabel: '02:25 PM - 03:20 PM' }, + 7: { start: 920, end: 975, timeLabel: '03:20 PM - 04:15 PM' }, }; const isPeriodLive = (dayKey: string, pNum: number) => { @@ -141,12 +142,6 @@ export default function StudentDashboard() { const [isEditingName, setIsEditingName] = useState(false); const [nameInput, setNameInput] = useState(''); - // Login Form States - const [regInput, setRegInput] = useState(''); - const [passInput, setPassInput] = useState(''); - const [isLoggingIn, setIsLoggingIn] = useState(false); - const [loginError, setLoginError] = useState(null); - // Initial Load: Profile and Timetable only useEffect(() => { if (session?.token) { @@ -247,28 +242,6 @@ export default function StudentDashboard() { } }, [activeTab, session?.token, profile]); - const handleLogin = async (e: React.FormEvent) => { - e.preventDefault(); - if (!regInput.trim() || !passInput.trim()) return; - - setIsLoggingIn(true); - setLoginError(null); - - try { - const res = await loginWithIms(regInput.trim(), passInput.trim()); - if (res.success && res.token && res.profile) { - setSession({ token: res.token, profile: res.profile }); - setProfile(res.profile); - } else { - setLoginError(res.message || 'Invalid Register Number or Password.'); - } - } catch (err: any) { - setLoginError(err.message || 'Unable to connect to RIT IMS.'); - } finally { - setIsLoggingIn(false); - } - }; - const handleLogout = () => { clearImsSession(); setSession(null); @@ -300,8 +273,16 @@ export default function StudentDashboard() { if (activeTab === 'grades') ensureGradesLoaded(true); }; + const handleCopyRegisterNumber = () => { + if (profile?.registerNumber) { + navigator.clipboard.writeText(profile.registerNumber); + setCopiedReg(true); + setTimeout(() => setCopiedReg(false), 2000); + } + }; + // Dynamically compute handling faculties for the logged-in student - const getDynamicFaculties = () => { + const dynamicFaculties = useMemo(() => { const staffMap = new Map }>(); // 1. Extract staff from live Timetable @@ -390,7 +371,7 @@ export default function StudentDashboard() { fallbackEmail: f.email, matched: f, })); - }; + }, [timetable, catMarks, profile]); // Sync session with global auth state useEffect(() => { @@ -403,27 +384,28 @@ export default function StudentDashboard() { }, [user]); // ───────────────────────────────────────────────────────────── - // VIEW 1: UNAUTHENTICATED INVITATION SCREEN + // VIEW 1: UNAUTHENTICATED INVITATION SCREEN (PRO GRADE) // ───────────────────────────────────────────────────────────── if (!session || !user) { return ( -
-
+
+ {/* Top Breadcrumb Header */} +
- -
- Home - - Student Dashboard + +
+ Home + + Student Dashboard
-

- Student{' '} - - Dashboard +

+ RIT Student{' '} + + Portal & IMS Suite

-

- View your live daily timetable, course instructors, attendance breakdown, CAT marks, and semester GPA. +

+ Access your real-time 7-period timetable, course faculty, live attendance breakdown, CAT scores, and Anna University CGPA calculator.

@@ -431,29 +413,68 @@ export default function StudentDashboard() {
-
- + {/* Ambient Background Glow */} +
+
+ +
+
+ +
+
-
-

- Student Sign In Required +
+

+ Sign In with College Credentials

-

- Sign in with your RIT Register Number and IMS Password to access your live student profile, 7-period timetable, handling faculties, and academic grades. +

+ Securely authenticate using your RIT Register Number and IMS Portal Password to load your official academic records.

+ {/* Feature Highlights Grid */} +
+
+ +
+

7-Period Timetable

+

Live active period indicators

+
+
+
+ +
+

Live Attendance

+

Real-time hour metrics & buffer

+
+
+
+ +
+

CAT & Lab Scores

+

Official internal assessments

+
+
+
+ +
+

GPA & CGPA Engine

+

AU 2021 Regulation credits

+
+
+
+ @@ -463,32 +484,36 @@ export default function StudentDashboard() { } // ───────────────────────────────────────────────────────────── - // VIEW 2: AUTHENTICATED STUDENT DASHBOARD + // VIEW 2: AUTHENTICATED STUDENT DASHBOARD (EXECUTIVE GRADE) // ───────────────────────────────────────────────────────────── - const currentPeriods = (timetable && timetable[selectedDay]) || []; + const rawName = profile?.name; + const displayName = (rawName && rawName.trim() && rawName.trim() !== 'Student' && !rawName.toLowerCase().startsWith('student (')) + ? rawName + : (user?.name || `Student (${profile?.registerNumber || 'RIT'})`); + const filteredGrades = gradesData?.results ? ( selectedSemFilter === 0 ? gradesData.results : gradesData.results.filter(r => r.semester === selectedSemFilter) ) : []; - const rawName = profile?.name; - const displayName = (rawName && rawName.trim() && rawName.trim() !== 'Student' && !rawName.toLowerCase().startsWith('student (')) - ? rawName - : (user?.name || `Student (${profile?.registerNumber || 'RIT'})`); - return ( -
- {/* Header Profile Section */} -
-
-
+
+ {/* ─── EXECUTIVE PROFILE HEADER BAR ─── */} +
+
+
+ + {/* Student Identity Section */}
- {displayName} +
+ {displayName} + +
@@ -499,12 +524,12 @@ export default function StudentDashboard() { value={nameInput} onChange={(e) => setNameInput(e.target.value)} placeholder="Enter your name" - className="bg-slate-50 border border-[#F97316] rounded-lg px-2.5 py-1 text-sm font-bold text-[#1E293B] outline-none" + className="bg-slate-50 border-2 border-orange-500 rounded-xl px-3 py-1 text-sm font-bold text-slate-900 outline-none shadow-xs" autoFocus />
) : (
-

+

{displayName}

)} - - - Verified ({profile?.registerNumber}) - + {/* Register Number Badge with Click-to-Copy */} +
-
- Branch: {profile?.department} - • - Year {profile?.year}, Sem {profile?.semester} - • - Batch: {profile?.batch} + {/* Metadata Pills */} +
+ + {profile?.department || 'AI & DS'} + + + Year {profile?.year || '1'}, Sem {profile?.semester || '2'} + + + Batch: {profile?.batch || '2024-2028'} +
-
+ {/* Quick Actions Bar */} +
+ GPA Calculator +
+
+ {/* ─── ACADEMIC OVERVIEW & ANALYTICS DASHBOARD ─── */} {(() => { const activeArrearsCount = gradesData?.activeArrears ?? 0; const cumulativeCgpa = gradesData?.cgpa ?? 0; const overallAttendancePct = attendance?.overallPercentage ?? 0; + // Attendance margin calculation: >= 75% safe margin + const totalPresent = attendance?.totalPresent ?? 0; + const totalConducted = attendance?.totalConducted ?? 0; + const safeMarginClasses = totalConducted > 0 + ? Math.floor((totalPresent - 0.75 * totalConducted) / 0.75) + : 0; + const gradeCounts: Record = { 'O': 0, 'A+': 0, 'A': 0, 'B+': 0, 'B': 0, 'C': 0, 'RA': 0 }; let maxGradeCount = 1; if (gradesData?.results) { @@ -602,100 +650,140 @@ export default function StudentDashboard() { })); return ( -
-
+
+ + {/* Header Title */} +
-

- Academic Overview & Analytics -

-

- Performance summary calculated per Anna University 2021 Regulations +

+

+ Academic Analytics & Overview +

+ + AU 2021 Reg + +
+

+ Live performance metrics synchronized directly with college academic databases

- - - Synced with Live IMS Records + + + Live Sync Active
- {/* Top Metrics Cards */} -
- {/* CGPA Card */} -
+ {/* 3 Main Executive KPI Cards */} +
+ + {/* 1. CGPA Metric Card */} +
- Cumulative CGPA -
- +
+ Cumulative CGPA +
+ +
+
+
+ {cumulativeCgpa > 0 ? cumulativeCgpa.toFixed(2) : 'N/A'} - / 10.0 + / 10.00
-

Cleared Credits: {gradesData?.totalCredits || 0}

-
- +
+ Cleared Credits: {gradesData?.totalCredits || 0} + First Class
- {/* Attendance Card */} -
+ {/* 2. Attendance Metric Card */} +
- Overall Attendance -
- +
+ Overall Attendance +
+ +
+
+
+ {overallAttendancePct > 0 ? `${overallAttendancePct}%` : 'N/A'} + = 75 ? 'bg-emerald-100 text-emerald-700' : 'bg-rose-100 text-rose-700' + }`}> + {overallAttendancePct >= 75 ? 'ELIGIBLE' : 'CRITICAL'} +
-

- {attendance?.totalPresent || 0} / {attendance?.totalConducted || 0} classes attended -

-
- +
+ {attendance?.totalPresent || 0} / {attendance?.totalConducted || 0} hrs attended + {safeMarginClasses > 0 ? ( + +{safeMarginClasses} hrs buffer + ) : ( + Req: 75% + )}
- {/* Active Arrears Card */} -
0 ? 'border-rose-300 bg-rose-50/30' : 'border-[#E5E7EB] hover:border-emerald-200' + {/* 3. Standing Arrears Metric Card */} +
0 + ? 'from-rose-50/40 border-rose-200 hover:border-rose-300' + : 'from-emerald-50/40 border-slate-200 hover:border-emerald-300' }`}>
- Active Arrears -
- 0 ? 'text-rose-600' : 'text-emerald-600'}`} style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}> +
+ Standing Arrears +
0 ? 'bg-rose-100 text-rose-700 border-rose-200' : 'bg-emerald-100 text-emerald-700 border-emerald-200' + }`}> + {activeArrearsCount > 0 ? : } +
+
+
+ 0 ? 'text-rose-600' : 'text-emerald-700' + }`} style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}> {activeArrearsCount} - Arrears + Active Backlogs
-

- {activeArrearsCount > 0 ? 'Pending re-appear exams' : 'All enrolled subjects cleared'} -

-
0 ? 'bg-rose-100 text-rose-600 border-rose-200' : 'bg-emerald-100 text-emerald-600 border-emerald-200' - }`}> - {activeArrearsCount > 0 ? : } +
+ + {activeArrearsCount > 0 ? 'Re-appear exams scheduled' : 'All enrolled subjects cleared'} + + 0 ? 'text-rose-600' : 'text-emerald-700'}`}> + {activeArrearsCount > 0 ? 'Action Needed' : 'All Clear'} +
+
- {/* Analytics Charts Grid */} + {/* Analytics Graphs (GPA Progression Curve & Grade Distribution) */}
- {/* GPA Trend Line Chart */} -
+ + {/* GPA Trend Curve */} +
-

- GPA Trend Across Semesters +

+ Semester GPA Progression Curve

-

Semester GPA progression curve

+

Historical performance across semesters

+
+
+
-
{gpaTrend.length > 0 ? ( -
- +
+ @@ -703,12 +791,12 @@ export default function StudentDashboard() { - {/* Grid Lines */} + {/* Horizontal Guides */} {[40, 80, 120, 160].map(y => ( ))} - {/* Plot Data */} + {/* Curve & Nodes */} {(() => { const gpas = gpaTrend.map(t => t.gpa); const minVal = Math.min(...gpas, 6) - 0.5; @@ -730,17 +818,12 @@ export default function StudentDashboard() { {pts.map((p, idx) => ( - {/* GPA Badge Background */} - - + + {p.gpa.toFixed(2)} - - {/* Node Circle */} - - - {/* Semester Label */} - + + {p.sem} @@ -751,19 +834,19 @@ export default function StudentDashboard() {
) : ( -
- Click tab to sync your GPA trend. +
+ Switch to to sync GPA progression.
)}
- {/* Grade Distribution Bar Chart */} -
+ {/* Grade Distribution Bar Graph */} +
-

+

Grade Distribution

-

Letter grades across all subjects

+

Letter grade summary across courses

@@ -776,25 +859,27 @@ export default function StudentDashboard() { return (
- {grade} -
+ {grade} +
0 ? 8 : 0, pct)}%` }} />
- {cnt} + {cnt}
); })}
+
+
); })()} - {/* Navigation Tabs with Hover Pre-Fetch */} -
+ {/* ─── NAVIGATION SEGMENTED TAB CONTROL ─── */} +
{[ - { id: 'timetable', label: 'Timetable', icon: Clock, onHover: () => {} }, + { id: 'timetable', label: 'Timetable Matrix', icon: Clock, onHover: () => {} }, { id: 'faculties', label: 'Handling Faculties', icon: Users, onHover: () => ensureAttendanceLoaded() }, { id: 'attendance', label: 'Attendance Breakdown', icon: ShieldCheck, onHover: () => ensureAttendanceLoaded() }, { id: 'marks', label: 'CAT & Internal Marks', icon: FileText, onHover: () => ensureCatMarksLoaded() }, @@ -807,32 +892,33 @@ export default function StudentDashboard() { key={tab.id} onClick={() => setActiveTab(tab.id as any)} onMouseEnter={tab.onHover} - className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs sm:text-sm font-bold transition-all cursor-pointer whitespace-nowrap ${ + className={`flex items-center gap-2 px-4 py-2.5 rounded-2xl text-xs sm:text-sm font-bold transition-all cursor-pointer whitespace-nowrap active:scale-95 ${ isActive - ? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white shadow-md shadow-orange-500/20' - : 'bg-white text-[#64748B] hover:text-[#1E293B] border border-[#E5E7EB] hover:bg-slate-50' + ? 'bg-gradient-to-r from-orange-600 to-amber-500 text-white shadow-md shadow-orange-500/20 scale-[1.02]' + : 'bg-white text-slate-600 hover:text-slate-900 border border-slate-200/90 hover:bg-slate-50' }`} style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }} > - + {tab.label} ); })}
- {/* ─── 1. TIMETABLE TAB (NEXT-GEN AGENDA UI) ─── */} + {/* ─── 1. TIMETABLE MATRIX TAB (NEXT-GEN AGENDA UI) ─── */} {activeTab === 'timetable' && (
+ {/* Top Toolbar: Filter Pills & Search & View Toggle */} -
+
- {/* Filter Pills (Matching Screenshot Top Bar) */} + {/* Filter Pills */}
{[ - { id: 'ALL', label: 'ALL', color: 'bg-slate-900 text-white' }, - { id: 'LAB', label: 'LABORATORY', color: 'bg-amber-500 text-white shadow-amber-500/20' }, + { id: 'ALL', label: 'ALL PERIODS', color: 'bg-slate-900 text-white' }, + { id: 'LAB', label: 'LABORATORY', color: 'bg-purple-600 text-white shadow-purple-500/20' }, { id: 'THEORY', label: 'THEORY', color: 'bg-blue-600 text-white shadow-blue-500/20' }, { id: 'ELECTIVE', label: 'ELECTIVES', color: 'bg-emerald-600 text-white shadow-emerald-500/20' }, { id: 'FREE', label: 'FREE PERIODS', color: 'bg-slate-500 text-white' }, @@ -845,7 +931,7 @@ export default function StudentDashboard() { className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-full text-[11px] font-extrabold tracking-wide transition-all cursor-pointer ${ isSelected ? `${filter.color} shadow-md scale-105` - : 'bg-slate-100 text-[#64748B] hover:bg-slate-200 hover:text-[#1E293B]' + : 'bg-slate-100 text-slate-600 hover:bg-slate-200 hover:text-slate-900' }`} > {filter.label} @@ -864,10 +950,10 @@ export default function StudentDashboard() { setTtSearchQuery(e.target.value)} - className="w-full pl-9 pr-4 py-2 rounded-2xl bg-slate-50 border border-[#E5E7EB] text-xs text-[#1E293B] focus:outline-none focus:border-[#F97316] transition-all" + className="w-full pl-9 pr-4 py-2 rounded-2xl bg-slate-50 border border-slate-200 text-xs text-slate-900 focus:outline-none focus:border-orange-500 transition-all font-medium" /> {ttSearchQuery && (
{isInitialLoading ? ( -
-
-

Fetching timetable from RIT IMS...

+
+
+

Fetching timetable from RIT IMS records...

) : ttViewMode === 'grid' ? ( - /* ─── WEEKLY MATRIX AGENDA VIEW (MATCHING USER SCREENSHOT) ─── */ -
+ /* ─── WEEKLY MATRIX AGENDA VIEW ─── */ +
-
+
{/* Matrix Header Row: Days of Week */}
@@ -927,8 +1013,8 @@ export default function StudentDashboard() { key={dayKey} className={`py-2 px-3 rounded-2xl text-center uppercase flex items-center justify-between ${ isToday - ? 'bg-orange-500 text-white shadow-md shadow-orange-500/20' - : 'bg-slate-50 text-slate-700 border border-slate-200' + ? 'bg-gradient-to-r from-orange-600 to-amber-500 text-white shadow-md shadow-orange-500/20 font-black' + : 'bg-slate-50 text-slate-700 border border-slate-200 font-extrabold' }`} > {dayKey} @@ -953,15 +1039,14 @@ export default function StudentDashboard() { { type: 'period', pNum: 7, timeLabel: '03:20 PM' }, ].map((rowItem, rIdx) => { if (rowItem.type === 'gap') { - // Diagonal-Striped Gap Row (Matching Screenshot "1.5H GAP" block) return (
{rowItem.timeLabel}
-
+
- + {rowItem.label}
@@ -975,7 +1060,7 @@ export default function StudentDashboard() { {/* Time Column Marker */}
{rowItem.timeLabel} - P{pNum} + Period {pNum}
{/* 5 Day Period Cells */} @@ -987,7 +1072,7 @@ export default function StudentDashboard() { return (
-
@@ -1006,17 +1091,17 @@ export default function StudentDashboard() { key={`${dayKey}-${pNum}`} className={`relative p-3.5 rounded-2xl border transition-all flex flex-col justify-between group ${ isLiveNow - ? 'bg-orange-50 border-orange-400 ring-2 ring-orange-400/30 shadow-md' + ? 'bg-orange-50/80 border-orange-400 ring-2 ring-orange-400/30 shadow-md' : isFree ? 'bg-slate-50/80 border-slate-200' : isLab - ? 'bg-purple-50/50 border-purple-200 hover:border-purple-300' + ? 'bg-purple-50/60 border-purple-200 hover:border-purple-300' : isElective - ? 'bg-emerald-50/50 border-emerald-200 hover:border-emerald-300' - : 'bg-white border-slate-200 hover:border-slate-300 shadow-sm hover:shadow-md' + ? 'bg-emerald-50/60 border-emerald-200 hover:border-emerald-300' + : 'bg-white border-slate-200 hover:border-slate-300 shadow-2xs hover:shadow-sm' }`} > - {/* Live Now Indicator Badge */} + {/* Live Now Pulsing Badge */} {isLiveNow && ( @@ -1027,7 +1112,7 @@ export default function StudentDashboard() {
{/* Subject Title */}

{period.subjectName}

@@ -1041,24 +1126,23 @@ export default function StudentDashboard() {
{/* Badges & Star Footer */} -
+
{period.subjectCode && !isFree && ( - + {period.subjectCode} )} {period.type}
- {/* Interactive Star Favorite Button (Matching Screenshot) */} @@ -1118,17 +1202,17 @@ export default function StudentDashboard() { initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className={`bg-white rounded-3xl p-5 border transition-all shadow-sm flex flex-col justify-between ${ - isLiveNow ? 'border-orange-400 ring-2 ring-orange-400/20 bg-orange-50/30' : 'border-[#E5E7EB] hover:border-slate-300' + isLiveNow ? 'border-orange-400 ring-2 ring-orange-400/20 bg-orange-50/30' : 'border-slate-200 hover:border-slate-300' }`} > {/* Header */}
- + P{period.periodNumber} - - + + {period.timeSlot}
@@ -1142,8 +1226,8 @@ export default function StudentDashboard() { {period.type === 'LAB' ? : } @@ -1155,28 +1239,28 @@ export default function StudentDashboard() { {/* Subject Details */}
- + {period.subjectCode}
-

+

{period.subjectName}

{/* Faculty Footer */} -
+
{period.staffName || 'Faculty Instructor'} {period.staffCode && ( - {period.staffCode} + {period.staffCode} )}
@@ -1188,28 +1272,28 @@ export default function StudentDashboard() {
)} - {/* ─── 2. ATTENDANCE TAB (LAZY LOADED) ─── */} + {/* ─── 2. ATTENDANCE BREAKDOWN TAB ─── */} {activeTab === 'attendance' && (
{isTabLoading && !attendance ? ( -
-
-

Fetching attendance records on demand...

+
+
+

Fetching live attendance records from IMS...

) : attendance ? ( -
-
+
+
-

- Subject-wise Attendance Report +

+ Subject-Wise Attendance Breakdown

-

Official attendance data fetched from RIT Student IMS

+

Real-time attendance hours, percentage thresholds, and safe margin calculator

-
+
-

Overall Average

-

+

Overall Average

+

{attendance.overallPercentage}%

@@ -1219,18 +1303,22 @@ export default function StudentDashboard() {
{attendance.subjects.map((sub) => { const isSafe = sub.percentage >= 75; + const canMissClasses = sub.conducted > 0 + ? Math.floor((sub.present - 0.75 * sub.conducted) / 0.75) + : 0; + return ( -
+
- + {sub.code} -

+

{sub.name}

- {sub.percentage}% @@ -1238,19 +1326,39 @@ export default function StudentDashboard() {
{/* Progress Bar */} -
+
-
- Conducted: {sub.conducted} hrs - Attended: {sub.present} hrs - Absent: {sub.absent} hrs +
+ Conducted: {sub.conducted} hrs + Attended: {sub.present} hrs + Absent: {sub.absent} hrs +
+ + {/* Buffer Guidance */} +
+ {canMissClasses > 0 ? ( + + + Can safely skip up to {canMissClasses} hrs + + ) : canMissClasses === 0 ? ( + + + On threshold (Cannot miss upcoming classes) + + ) : ( + + + Shortage: Attend next {Math.ceil((0.75 * sub.conducted - sub.present) / 0.25)} hrs + + )}
); @@ -1265,81 +1373,86 @@ export default function StudentDashboard() { {activeTab === 'marks' && (
{/* Sub-Tab Selector */} -
+
{isTabLoading && !catMarks ? ( -
-
-

Fetching internal marks on demand...

+
+
+

Fetching internal marks on demand...

) : ( -
+
+ {/* SUB TAB 1: CAT MARKS */} {marksSubTab === 'cat' && catMarks && ( -
-
-

- Continuous Assessment Test (CAT) Marks +
+
+

+ Continuous Assessment Test (CAT) Scores

-

Internal examination scores synchronized with college records

+

Official internal examination scores synchronized with college evaluation portal

-
+
- - - - - - - - + + + + + + + + - + {catMarks.subjects.map((sub) => ( - - - - - - - - + + + + + + + + ))} @@ -1350,50 +1463,54 @@ export default function StudentDashboard() { {/* SUB TAB 2: ASSIGNMENT MARKS */} {marksSubTab === 'assignment' && ( -
-
-

- Assignment Marks +
+
+

+ Assignment Scores

-

Continuous assignment submissions (10 Marks per assignment)

+

Continuous assignment evaluations (10 Marks per assignment)

{assignmentMarks && assignmentMarks.length > 0 ? ( -
+

Subject CodeSubject TitleHandling FacultyCO 1CO 2TotalWeightage
Subject CodeCourse TitleHandling FacultyCO 1CO 2TotalWeightage
{sub.code}{sub.name}{sub.faculty || 'Faculty In-Charge'}{sub.co1 ?? '-'}{sub.co2 ?? '-'}{sub.total ?? '-'}{sub.weightage ?? '-'}
{sub.code}{sub.name}{sub.faculty || 'Faculty In-Charge'}{sub.co1 ?? '-'}{sub.co2 ?? '-'}{sub.total ?? '-'} + + {sub.weightage ?? '-'} + +
- - - - - - - - - - + + + + + + + + + + - + {assignmentMarks.map((item, idx) => ( - - - - - - - - - - + + + + + + + + + + ))}
Subject CodeSubject TitleHandling FacultyAssgn 1Assgn 2Assgn 3Assgn 4Assgn 5Total (50)
Subject CodeCourse TitleHandling FacultyA1A2A3A4A5Total (50)
{item.subjectCode}{item.subjectName}{item.facultyName || 'Faculty In-Charge'}{item.a1}{item.a2}{item.a3}{item.a4}{item.a5}{item.total}
{item.subjectCode}{item.subjectName}{item.facultyName || 'Faculty In-Charge'}{item.a1}{item.a2}{item.a3}{item.a4}{item.a5} + + {item.total} + +
) : ( -
- No Assignment Marks posted by faculty yet for this semester. +
+ No Assignment marks posted yet for this semester.
)}
@@ -1401,102 +1518,108 @@ export default function StudentDashboard() { {/* SUB TAB 3: LAB MARKS */} {marksSubTab === 'lab' && ( -
-
-

+
+
+

Laboratory Practical Marks

-

Practical course evaluations & lab experiment marks

+

Practical evaluations and lab experiment assessments

{labMarks && labMarks.length > 0 ? ( -
+
- - - - - - + + + + + + - + {labMarks.map((item, idx) => ( - - - - - - + + + + + + ))}
Subject CodeSubject TitleHandling FacultyMarksTotal
Subject CodeSubject TitleHandling FacultyMarksTotal
{item.subjectCode}{item.subjectName}{item.facultyName || 'Faculty In-Charge'}{item.marks}{item.total}
{item.subjectCode}{item.subjectName}{item.facultyName || 'Faculty In-Charge'}{item.marks} + + {item.total} + +
) : ( -
- No Lab Marks posted by faculty yet for this semester. +
+ No Lab marks posted yet for this semester.
)}
)} +
)}
)} - {/* ─── 4. GRADES TAB (LAZY LOADED) ─── */} + {/* ─── 4. ACADEMIC GRADES & CGPA TAB ─── */} {activeTab === 'grades' && (
{isTabLoading && !gradesData ? ( -
-
-

Fetching semester grades & calculating CGPA on demand...

+
+
+

Calculating Anna University CGPA & semester credits...

) : gradesData ? ( -
+
+
-

- Official Semester Grades & Cumulative CGPA +

+ Official Semester Results & CGPA

-

Multi-semester performance and credit calculations

+

Credit calculations and letter grades according to Anna University Regulations

Simulate in GPA Calculator
- {/* Banner */} -
+ {/* CGPA Banner */} +
-

Cumulative CGPA

+

Cumulative Grade Point Average

{gradesData.cgpa.toFixed(2)} / 10.00

-

- Total Earned Credits: {gradesData.totalCredits} credits +

+ Total Earned Credits: {gradesData.totalCredits} credits

-
-

Degree Classification

-

First Class with Distinction

+
+

Degree Classification

+

First Class with Distinction

- {/* Semester Tabs */} + {/* Semester Switcher */} {gradesData.results.length > 1 && ( -
+
)} - {/* Results Table */} + {/* Results Tables */} {filteredGrades.map((semResult) => (
-
+
- -

- Semester {semResult.semester} Results + +

+ Semester {semResult.semester} Grade Sheet

- Credits: {semResult.totalCredits} - + Total Credits: {semResult.totalCredits} + SGPA: {semResult.gpa.toFixed(2)}
-
+
- - - - - - + + + + + + - + {semResult.subjects.map((grade) => ( - - - - + + + +
Course CodeCourse TitleCreditsLetter GradeResult
Course CodeCourse TitleCreditsLetter GradeResult
{grade.code}{grade.name}{grade.credits}
{grade.code}{grade.name}{grade.credits} - + {grade.grade} {grade.result} @@ -1579,26 +1702,26 @@ export default function StudentDashboard() { {/* ─── 5. HANDLING FACULTIES TAB ─── */} {activeTab === 'faculties' && (
-
+
- -

- Subject Handling Faculties + +

+ Course Instructors & Laboratory In-Charges

-

- Enrolled course instructors and lab in-charges assigned to your batch ({profile?.department || 'AI&DS'}). +

+ Faculty mentors and professors assigned to your department ({profile?.department || 'AI & DS'}).

- - {getDynamicFaculties().length} Active Instructors + + {dynamicFaculties.length} Active Instructors
- {getDynamicFaculties().map((fac) => { + {dynamicFaculties.map((fac) => { const matched = fac.matched; const designation = fac.role; const department = fac.dept; @@ -1612,24 +1735,24 @@ export default function StudentDashboard() { key={fac.code} initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} - className="bg-slate-50 border border-[#E5E7EB] hover:border-[#F97316]/40 rounded-2xl p-5 shadow-xs transition-all flex flex-col justify-between" + className="bg-slate-50/70 border border-slate-200/90 hover:border-orange-300 rounded-2xl p-5 shadow-2xs hover:shadow-sm transition-all flex flex-col justify-between" >
-
+
{fac.name.replace('DR.', '').trim().slice(0, 2).toUpperCase()}
-

+

{matched?.name || fac.name}

- - Staff ID: {fac.code} + + ID: {fac.code} {qualification && ( - + {qualification} )} @@ -1639,36 +1762,36 @@ export default function StudentDashboard() {
-

+

{department}

- + {designation} {experience && ( - + ⌛ {experience} )}
{interest && ( -

- Specialization: {interest} +

+ Specialization: {interest}

)}
- - Handled Courses: + + Courses Handled:
{fac.subjects.map((sub, sIdx) => ( {sub} @@ -1677,20 +1800,20 @@ export default function StudentDashboard() {
-
+
- Email Instructor + Email Faculty - Faculty Profile + Profile
@@ -1701,6 +1824,7 @@ export default function StudentDashboard() {
)} +
);