From 2d56876b605199d3f6615b6851cfc59f0e35bb48 Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:03:33 -0500 Subject: [PATCH 1/9] Fix: Generated Student Evaluation Results --- .../EvaluationResults/CategoryBreakdown.jsx | 304 +++++++++ .../CategoryBreakdown.module.css | 597 +++++++++++++++++ .../EvaluationResults/EvaluationResults.jsx | 166 +++++ .../EvaluationResults.module.css | 353 ++++++++++ .../EvaluationResults/OverallPerformance.jsx | 293 +++++++++ .../OverallPerformance.module.css | 533 +++++++++++++++ .../EvaluationResults/SummaryStats.jsx | 381 +++++++++++ .../EvaluationResults/SummaryStats.module.css | 317 +++++++++ .../EvaluationResults/TaskDetailsList.jsx | 440 +++++++++++++ .../TaskDetailsList.module.css | 617 ++++++++++++++++++ .../EvaluationResults/TeacherFeedback.jsx | 155 +++++ .../TeacherFeedback.module.css | 458 +++++++++++++ .../evaluationNotificationService.js | 189 ++++++ .../EvaluationResults/mockData.js | 437 +++++++++++++ src/routes.jsx | 2 + 15 files changed, 5242 insertions(+) create mode 100644 src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx create mode 100644 src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css create mode 100644 src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx create mode 100644 src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css create mode 100644 src/components/EductionPortal/EvaluationResults/OverallPerformance.jsx create mode 100644 src/components/EductionPortal/EvaluationResults/OverallPerformance.module.css create mode 100644 src/components/EductionPortal/EvaluationResults/SummaryStats.jsx create mode 100644 src/components/EductionPortal/EvaluationResults/SummaryStats.module.css create mode 100644 src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx create mode 100644 src/components/EductionPortal/EvaluationResults/TaskDetailsList.module.css create mode 100644 src/components/EductionPortal/EvaluationResults/TeacherFeedback.jsx create mode 100644 src/components/EductionPortal/EvaluationResults/TeacherFeedback.module.css create mode 100644 src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js create mode 100644 src/components/EductionPortal/EvaluationResults/mockData.js diff --git a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx new file mode 100644 index 0000000000..8e942fa131 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx @@ -0,0 +1,304 @@ +import React from 'react'; +import { Card, CardBody, CardHeader, Progress, Badge, Row, Col } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faChartPie, + faClipboardCheck, + faQuestion, + faGraduationCap, + faProjectDiagram, + faWeight, + faCheckCircle, + faClock, + faExclamationTriangle, +} from '@fortawesome/free-solid-svg-icons'; +import { getPerformanceLevel, calculateCategoryProgress } from './mockData'; +import styles from './CategoryBreakdown.module.css'; + +const CategoryBreakdown = ({ categories, selectedCategory = 'all', isLoading }) => { + if (isLoading) { + return ( + + +
+
+

Loading category breakdown...

+
+ + + ); + } + + if (!categories || categories.length === 0) { + return ( + + +
+ +

No category data available yet.

+
+
+
+ ); + } + + // Filter categories based on selected category + const filteredCategories = + selectedCategory === 'all' + ? categories + : categories.filter(category => category.name.toLowerCase() === selectedCategory); + + const getCategoryIcon = categoryId => { + const icons = { + assignments: faClipboardCheck, + quizzes: faQuestion, + exams: faGraduationCap, + projects: faProjectDiagram, + }; + return icons[categoryId] || faClipboardCheck; + }; + + const getStatusIcon = status => { + if (status === 'excellent') return faCheckCircle; + if (status === 'good') return faCheckCircle; + if (status === 'fair') return faClock; + return faExclamationTriangle; + }; + + return ( + + +
+ +
+

+ {selectedCategory === 'all' + ? 'Category Breakdown' + : `${selectedCategory.charAt(0).toUpperCase() + + selectedCategory.slice(1)} Performance`} +

+

+ {selectedCategory === 'all' + ? `Showing all ${categories.length} categories` + : `Showing ${filteredCategories.length} selected category`} +

+
+
+
+ + + + {filteredCategories.map(category => { + const performanceInfo = getPerformanceLevel(category.percentage); + const progressInfo = calculateCategoryProgress(category); + const isOverdue = new Date(category.dueDate) < new Date() && !progressInfo.isComplete; + + return ( + +
+ {/* Category Header */} +
+
+
+ +
+
+
{category.name}
+

{category.description}

+
+
+
+ + + {performanceInfo.label} + + + + {category.weightage}% + +
+
+ + {/* Score Display */} +
+
+
{category.percentage.toFixed(1)}%
+
+ {category.earnedMarks} + / + {category.totalMarks} +
+
+
+ + {/* Clear visual indicator instead of confusing lines */} +
+
+
+
+
+ + {/* Completion Status */} +
+
+
Completion:
+
+ {category.completedItems}/{category.totalItems} ( + {progressInfo.completionRate}%) +
+
+
+
+
+ + On Time: {category.submissions?.onTime || 0} + +
+
+
+ + Late: {category.submissions?.late || 0} + +
+
+
+ + Missing: {category.submissions?.missing || 0} + +
+
+
+ + {/* Due Date Alert */} + {isOverdue && ( +
+
+ + + Overdue: {new Date(category.dueDate).toLocaleDateString()} + +
+
+ )} + + {/* Due Date Info */} + {!isOverdue && category.dueDate && ( +
+
+ + + Due:{' '} + {new Date(category.dueDate).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} + +
+
+ )} +
+ + ); + })} + + + {/* Summary Statistics */} +
+
Summary Statistics
+ + +
+
+ {categories.reduce((sum, cat) => sum + cat.totalItems, 0)} +
+
Total Items
+
+ + +
+
+ {categories.reduce((sum, cat) => sum + cat.completedItems, 0)} +
+
Completed
+
+ + +
+
+ {Math.round( + categories.reduce((sum, cat) => sum + cat.percentage * cat.weightage, 0) / + categories.reduce((sum, cat) => sum + cat.weightage, 0), + )} + % +
+
Weighted Average
+
+ + +
+
+ { + categories.filter( + cat => getPerformanceLevel(cat.percentage).level === 'excellent', + ).length + } +
+
Excellent Categories
+
+ +
+
+ + + ); +}; + +export default CategoryBreakdown; diff --git a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css new file mode 100644 index 0000000000..e146f7ba16 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css @@ -0,0 +1,597 @@ +/* CategoryBreakdown Component Styles */ + +.categoryCard { + background: #ffffff; + border: none; + border-radius: 16px; + margin-bottom: 1.5rem; + overflow: hidden; + transition: all 0.3s ease; + position: relative; +} + +.categoryCard:hover { + transform: translateY(-2px); +} + +.categoryCard.filtered { + border: 2px solid #4f46e5; + background: #f8faff; +} + +.breakdownCard.filtered .categoryHeader { + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); +} + +.categoryCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: var(--category-accent); + z-index: 1; +} + +.categoryCard.excellent::before { + --category-accent: linear-gradient(90deg, #10b981, #065f46); +} + +.categoryCard.good::before { + --category-accent: linear-gradient(90deg, #3b82f6, #1e40af); +} + +.categoryCard.fair::before { + --category-accent: linear-gradient(90deg, #f59e0b, #92400e); +} + +.categoryCard.poor::before { + --category-accent: linear-gradient(90deg, #ef4444, #991b1b); +} + +.categoryCard:hover { + transform: translateY(-4px); +} + +/* Clear Performance Indicator Bar - replaces confusing line charts */ +.performanceIndicatorBar { + width: 100%; + height: 8px; + background: rgba(255, 255, 255, 0.2); + border-radius: 4px; + overflow: hidden; + margin-top: 0.5rem; + position: relative; +} + +.performanceLevel { + height: 100%; + border-radius: 4px; + transition: width 0.8s ease; + position: relative; +} + +.performanceLevel.excellent { + background: linear-gradient(90deg, #10b981, #065f46); +} + +.performanceLevel.good { + background: linear-gradient(90deg, #3b82f6, #1e40af); +} + +.performanceLevel.fair { + background: linear-gradient(90deg, #f59e0b, #92400e); +} + +.performanceLevel.poor { + background: linear-gradient(90deg, #ef4444, #991b1b); +} + +/* Add a subtle pulse animation for better visibility */ +.performanceLevel::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.2); + animation: shimmer 2s infinite; +} + +@keyframes shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +.categoryHeader { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 2rem 1.5rem; + border-bottom: none; + position: relative; + overflow: hidden; +} + +.categoryHeader::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 1px; + background: rgba(255, 255, 255, 0.2); +} + +/* Category card content styles */ +.categoryInfo { + display: flex; + align-items: flex-start; + gap: 1rem; + flex: 1; +} + +.categoryIconWrapper { + width: 48px; + height: 48px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.15); + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(10px); +} + +.categoryIcon { + font-size: 1.25rem; + color: rgba(255, 255, 255, 0.9) !important; +} + +.categoryDetails { + flex: 1; +} + +.categoryName { + margin: 0 0 0.5rem 0; + font-size: 1.25rem; + font-weight: 700; + color: #ffffff !important; + line-height: 1.2; + transition: all 0.3s ease; +} + +.categoryDescription { + margin: 0; + font-size: 0.875rem; + color: rgba(255, 255, 255, 0.85) !important; + line-height: 1.4; + font-weight: 400; + transition: all 0.3s ease; +} + +/* Improve text visibility on hover */ +.categoryCard:hover .categoryName { + color: #000000 !important; + font-weight: 800; +} + +.categoryCard:hover .categoryDescription { + color: rgba(0, 0, 0, 0.75) !important; +} + +.categoryBadges { + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-end; +} + +/* Score section styles */ +.scoreSection { + padding: 1.5rem; + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); +} + +.mainScore { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; +} + +.scoreNumber { + font-size: 2rem; + font-weight: 800; + color: #ffffff !important; + line-height: 1; +} + +/* Performance-based color coding for percentages - Clean light shades */ +.categoryCard.excellent .scoreNumber { + color: #34d399 !important; +} + +.categoryCard.good .scoreNumber { + color: #60a5fa !important; +} + +.categoryCard.fair .scoreNumber { + color: #fbbf24 !important; +} + +.categoryCard.poor .scoreNumber { + color: #f87171 !important; +} + +.scoreDetails { + font-size: 0.875rem; + color: rgba(255, 255, 255, 0.8) !important; + font-weight: 500; +} + +.earnedMarks { + color: rgba(255, 255, 255, 0.9) !important; + font-weight: 600; +} + +.separator { + color: rgba(255, 255, 255, 0.6) !important; + margin: 0 0.25rem; +} + +.totalMarks { + color: rgba(255, 255, 255, 0.7) !important; +} + +.progressWrapper { + margin-top: 0.5rem; +} + +/* Completion section styles */ +.completionSection { + padding: 1.5rem; + background: rgba(255, 255, 255, 0.95); + color: #374151; +} + +.completionRow { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.completionLabel { + font-weight: 600; + color: #374151 !important; + font-size: 0.875rem; +} + +.completionValue { + font-weight: 700; + color: #1f2937 !important; + font-size: 0.875rem; +} + +.submissionStats { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +.statItem { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.statDot { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.statLabel { + font-size: 0.75rem; + color: #4b5563 !important; + font-weight: 500; +} + +.headerContent { + display: flex; + justify-content: space-between; + align-items: center; +} + +.headerLeft { + display: flex; + align-items: center; + gap: 1rem; +} + +.headerIcon { + font-size: 1.5rem; + color: rgba(255, 255, 255, 0.9); +} + +.headerTitle { + margin: 0; + font-size: 1.5rem; + font-weight: 600; + color: white; +} + +.headerSubtitle { + margin: 0.25rem 0 0 0; + font-size: 0.875rem; + color: rgba(255, 255, 255, 0.8); + font-weight: 400; +} + +.headerRight { + display: flex; + align-items: center; +} + +.countBadge { + background: rgba(255, 255, 255, 0.2); + color: white; + border: 1px solid rgba(255, 255, 255, 0.3); + font-weight: 500; +} + +.categoryBody { + padding: 0; +} + +.categoryTable { + margin: 0; + border-collapse: separate; + border-spacing: 0; +} + +.categoryTable thead th { + background: #f8f9fa; + color: #374151; + font-weight: 600; + font-size: 0.875rem; + padding: 1rem; + border-bottom: 2px solid #e5e7eb; + text-align: left; + position: sticky; + top: 0; + z-index: 10; +} + +.categoryTable thead th:first-child { + border-top-left-radius: 0; +} + +.categoryTable thead th:last-child { + border-top-right-radius: 0; +} + +.categoryTable tbody tr { + transition: all 0.2s ease; +} + +.categoryTable tbody tr:hover { + background: #f8f9fa; +} + +.categoryTable tbody td { + padding: 1rem; + border-bottom: 1px solid #e5e7eb; + vertical-align: middle; +} + +.categoryName { + font-weight: 600; + color: #1f2937; + font-size: 0.95rem; +} + +.categoryIcon { + margin-right: 0.5rem; + width: 16px; + text-align: center; +} + +.weightageCell { + font-weight: 600; + color: #4b5563; +} + +.countCell { + font-size: 0.875rem; + color: #6b7280; +} + +.marksCell { + font-weight: 500; + color: #374151; +} + +.percentageCell { + font-weight: 600; + font-size: 1rem; +} + +.performanceCell { + text-align: center; +} + +.performanceIndicator { + display: inline-flex; + align-items: center; + justify-content: center; + width: 80px; + padding: 0.375rem 0.75rem; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.025em; +} + +.performanceIndicator.excellent { + background: #d1fae5; + color: #065f46; + border: 1px solid #a7f3d0; +} + +.performanceIndicator.good { + background: #dbeafe; + color: #1e40af; + border: 1px solid #93c5fd; +} + +.performanceIndicator.fair { + background: #fef3c7; + color: #92400e; + border: 1px solid #fcd34d; +} + +.performanceIndicator.poor { + background: #fee2e2; + color: #991b1b; + border: 1px solid #fca5a5; +} + +.progressBar { + width: 100%; + height: 8px; + background: #e5e7eb; + border-radius: 4px; + overflow: hidden; + margin-top: 0.25rem; +} + +.progressFill { + height: 100%; + border-radius: 4px; + transition: width 0.6s ease; +} + +.progressFill.excellent { + background: linear-gradient(90deg, #10b981 0%, #065f46 100%); +} + +.progressFill.good { + background: linear-gradient(90deg, #3b82f6 0%, #1e40af 100%); +} + +.progressFill.fair { + background: linear-gradient(90deg, #f59e0b 0%, #92400e 100%); +} + +.progressFill.poor { + background: linear-gradient(90deg, #ef4444 0%, #991b1b 100%); +} + +.loadingContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + text-align: center; +} + +.loadingSpinner { + width: 40px; + height: 40px; + border: 3px solid #e5e7eb; + border-top: 3px solid #4f46e5; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; +} + +.loadingText { + color: #6b7280; + font-size: 0.875rem; + margin: 0; +} + +.noDataContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + text-align: center; +} + +.noDataIcon { + font-size: 3rem; + color: #d1d5db; + margin-bottom: 1rem; +} + +.noDataText { + color: #6b7280; + font-size: 1rem; + margin: 0; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .categoryHeader { + padding: 1rem; + } + + .headerContent { + flex-direction: column; + gap: 0.75rem; + align-items: flex-start; + } + + .categoryTable { + font-size: 0.875rem; + } + + .categoryTable thead th, + .categoryTable tbody td { + padding: 0.75rem 0.5rem; + } + + .categoryName { + font-size: 0.875rem; + } + + .performanceIndicator { + width: 70px; + font-size: 0.6875rem; + padding: 0.25rem 0.5rem; + } +} + +@media (max-width: 480px) { + .categoryTable thead th, + .categoryTable tbody td { + padding: 0.5rem 0.375rem; + font-size: 0.8125rem; + } + + .categoryName { + font-size: 0.8125rem; + } + + .performanceIndicator { + width: 60px; + font-size: 0.625rem; + padding: 0.2rem 0.4rem; + } + + .categoryIcon { + display: none; + } +} \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx new file mode 100644 index 0000000000..808670c621 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -0,0 +1,166 @@ +import React, { useState, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { Container, Row, Col, Card, CardBody, Badge, Alert } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faChartLine, + faTrophy, + faClipboardCheck, + faExclamationTriangle, + faClock, + faCheckCircle, + faTimesCircle, + faStar, + faGraduationCap, +} from '@fortawesome/free-solid-svg-icons'; +import styles from './EvaluationResults.module.css'; +import OverallPerformance from './OverallPerformance'; +import CategoryBreakdown from './CategoryBreakdown'; +import TaskDetailsList from './TaskDetailsList'; +import SummaryStats from './SummaryStats'; +import TeacherFeedback from './TeacherFeedback'; +import EvaluationNotificationService from './evaluationNotificationService'; +import { mockEvaluationData } from './mockData'; + +const EvaluationResults = ({ auth }) => { + const [evaluationData, setEvaluationData] = useState(null); + const [loading, setLoading] = useState(true); + const [selectedCategory, setSelectedCategory] = useState('all'); + + useEffect(() => { + // Simulate API call with realistic loading time + const loadEvaluationData = async () => { + try { + setLoading(true); + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 1500)); + setEvaluationData(mockEvaluationData); + + // Trigger notification for new results (simulate this is new data) + if (auth?.user?.userid && mockEvaluationData) { + EvaluationNotificationService.showPerformanceNotification( + mockEvaluationData.student.overallScore, + auth.user.firstName || 'Student', + ); + + // Mark results as viewed when user opens the page + EvaluationNotificationService.markResultsAsViewed( + auth.user.userid, + mockEvaluationData.student.id, + ); + } + } catch (error) { + // Handle error silently for now + } finally { + setLoading(false); + } + }; + + loadEvaluationData(); + }, [auth]); + + if (loading) { + return ( +
+
+ +

Loading Your Academic Performance...

+
+
+
+
+
+ ); + } + + if (!evaluationData) { + return ( + + + + No evaluation results available at this time. + + + ); + } + + const { student, overallScore, categories, tasks, summary, teacherFeedback } = evaluationData; + + return ( +
+ {/* Header Section */} +
+ +
+
+ +
+

Academic Performance Dashboard

+

Welcome back, {student.name}

+ + Last updated: {new Date(student.lastUpdated).toLocaleDateString()} + +
+
+
+
+ {overallScore}% + Overall +
+
+
+
+
+ + + {/* Teacher Feedback Section */} + + + {/* Overall Performance Section */} + + + {/* Summary Statistics */} + + + {/* Category Filter */} +
+

+ + Performance Breakdown +

+
+ {['all', ...categories.map(cat => cat.name.toLowerCase())].map(category => ( + + ))} +
+
+ + {/* Category Breakdown Table */} + + + {/* Detailed Tasks List */} + +
+
+ ); +}; + +const mapStateToProps = state => ({ + auth: state.auth, +}); + +export default connect(mapStateToProps)(EvaluationResults); diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css new file mode 100644 index 0000000000..766f741a80 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css @@ -0,0 +1,353 @@ +/* ============================================================================ + EVALUATION RESULTS - PREMIUM CSS MODULE + Modern, responsive design with excellent UX + ============================================================================ */ + +/* CSS Variables for consistent theming */ +:root { + --primary-color: #3b82f6; + --success-color: #10b981; + --warning-color: #f59e0b; + --error-color: #ef4444; + --info-color: #6366f1; + --text-primary: #1f2937; + --text-secondary: #6b7280; + --text-light: #9ca3af; + --bg-primary: #ffffff; + --bg-secondary: #f9fafb; + --bg-accent: #f3f4f6; + --border-color: #e5e7eb; + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + --border-radius: 12px; + --border-radius-lg: 16px; + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* ============================================================================ + MAIN PAGE LAYOUT + ============================================================================ */ + +.evaluationResultsPage { + min-height: 100vh; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + background-attachment: fixed; +} + +.headerSection { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(20px); + border-bottom: 1px solid var(--border-color); + padding: 2rem 0; + margin-bottom: 2rem; + box-shadow: var(--shadow-sm); +} + +.headerContent { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1.5rem; +} + +.studentInfo { + display: flex; + align-items: center; + gap: 1rem; +} + +.headerIcon { + font-size: 2.5rem; + color: var(--primary-color); + opacity: 0.9; +} + +.pageTitle { + margin: 0; + font-size: 2rem; + font-weight: 700; + color: var(--text-primary); + background: linear-gradient(135deg, var(--primary-color), var(--info-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.studentName { + margin: 0.25rem 0 0.125rem 0; + font-size: 1.125rem; + color: var(--text-secondary); + font-weight: 500; +} + +.lastUpdated { + color: var(--text-light); + font-size: 0.875rem; +} + +.overallScoreBadge { + background: linear-gradient(135deg, var(--success-color), #059669); + border-radius: 50%; + padding: 4px; + box-shadow: var(--shadow-lg); +} + +.scoreCircle { + background: white; + border-radius: 50%; + width: 120px; + height: 120px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; +} + +.scoreNumber { + font-size: 2rem; + font-weight: 800; + color: var(--success-color); + line-height: 1; +} + +.scoreLabel { + font-size: 0.75rem; + color: var(--text-secondary); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.mainContent { + padding-bottom: 4rem; +} + +/* ============================================================================ + LOADING STATES + ============================================================================ */ + +.loadingContainer { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +.loadingSpinner { + text-align: center; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(20px); + padding: 3rem; + border-radius: var(--border-radius-lg); + box-shadow: var(--shadow-xl); + max-width: 400px; + width: 90%; +} + +.loadingIcon { + font-size: 4rem; + color: var(--primary-color); + margin-bottom: 1.5rem; + animation: pulse 2s infinite; +} + +.loadingSpinner h3 { + color: var(--text-primary); + margin-bottom: 2rem; + font-weight: 600; +} + +.loadingBar { + width: 100%; + height: 8px; + background: var(--bg-accent); + border-radius: 4px; + overflow: hidden; + position: relative; +} + +.loadingProgress { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--info-color)); + border-radius: 4px; + animation: loading 2s infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +@keyframes loading { + 0% { width: 0%; } + 50% { width: 70%; } + 100% { width: 100%; } +} + +/* ============================================================================ + SECTION STYLING + ============================================================================ */ + +.sectionTitle { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); + margin-bottom: 1.5rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.sectionTitle svg { + color: var(--primary-color); +} + +.filterSection { + background: var(--bg-primary); + padding: 1.5rem; + border-radius: var(--border-radius); + box-shadow: var(--shadow-sm); + margin-bottom: 2rem; + border: 1px solid var(--border-color); +} + +.categoryFilter { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + margin-top: 1rem; +} + +.filterButton { + padding: 0.75rem 1.5rem; + border: 2px solid var(--border-color); + background: var(--bg-primary); + color: var(--text-secondary); + border-radius: 50px; + font-weight: 500; + font-size: 0.875rem; + cursor: pointer; + transition: var(--transition); + white-space: nowrap; +} + +.filterButton:hover { + border-color: #4f46e5; + color: #000000; + background: rgba(255, 255, 255, 0.9); + transform: translateY(-2px); + font-weight: 600; +} + +.filterButton.active { + background: linear-gradient(135deg, var(--primary-color), var(--info-color)); + border-color: transparent; + color: white; + box-shadow: var(--shadow-md); +} + +.filterButton.active:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-lg); +} + +/* ============================================================================ + ALERT STYLING + ============================================================================ */ + +.noDataAlert { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(20px); + border: 1px solid rgba(245, 158, 11, 0.3); + border-radius: var(--border-radius); + color: var(--text-primary); + font-weight: 500; + box-shadow: var(--shadow-md); +} + +/* ============================================================================ + RESPONSIVE DESIGN + ============================================================================ */ + +@media (max-width: 768px) { + .headerContent { + flex-direction: column; + text-align: center; + } + + .pageTitle { + font-size: 1.75rem; + } + + .scoreCircle { + width: 100px; + height: 100px; + } + + .scoreNumber { + font-size: 1.5rem; + } + + .categoryFilter { + justify-content: center; + } + + .filterButton { + font-size: 0.8rem; + padding: 0.625rem 1.25rem; + } +} + +@media (max-width: 480px) { + .headerSection { + padding: 1.5rem 0; + } + + .pageTitle { + font-size: 1.5rem; + } + + .loadingSpinner { + padding: 2rem; + } + + .loadingIcon { + font-size: 3rem; + } +} + +/* ============================================================================ + ACCESSIBILITY + ============================================================================ */ + +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +/* Focus styles for accessibility */ +.filterButton:focus-visible { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + :root { + --primary-color: #0066cc; + --success-color: #008800; + --warning-color: #cc6600; + --error-color: #cc0000; + --text-primary: #000000; + --text-secondary: #333333; + --border-color: #666666; + } +} \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/OverallPerformance.jsx b/src/components/EductionPortal/EvaluationResults/OverallPerformance.jsx new file mode 100644 index 0000000000..c087e697d5 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/OverallPerformance.jsx @@ -0,0 +1,293 @@ +import React from 'react'; +import { Card, CardBody, CardHeader, Progress, Badge, Row, Col } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faChartLine, + faTrophy, + faBullseye, + faArrowTrendUp, + faUsers, + faCalendar, + faClock, + faCheckCircle, +} from '@fortawesome/free-solid-svg-icons'; +import styles from './OverallPerformance.module.css'; + +const OverallPerformance = ({ data, analytics, isLoading }) => { + if (isLoading) { + return ( + + +
+
+

Loading performance data...

+
+ + + ); + } + + if (!data || !analytics) { + return ( + + +
+ +

No performance data available yet.

+
+
+
+ ); + } + + const getPerformanceLevel = score => { + if (score >= 90) return { level: 'excellent', color: 'success', icon: faTrophy }; + if (score >= 80) return { level: 'good', color: 'primary', icon: faBullseye }; + if (score >= 70) return { level: 'fair', color: 'warning', icon: faArrowTrendUp }; + return { level: 'poor', color: 'danger', icon: faBullseye }; + }; + + const performanceLevel = getPerformanceLevel(data.overallScore); + const percentile = analytics.percentile; + const improvementTrend = data.summary?.improvementTrend || '+0%'; + const isImproving = improvementTrend.startsWith('+'); + + return ( + + +
+
+ +
+

Overall Performance

+

Academic Progress Overview

+
+
+
+ + + {performanceLevel.level.toUpperCase()} + +
+
+
+ + + {/* Main Performance Score */} +
+
+
+
{data.overallScore.toFixed(1)}
+
Overall Score
+
+
+
+ Class Rank + + #{analytics.classRank} of {analytics.totalStudents} + +
+
+ Percentile + {percentile}th +
+
+ GPA + {analytics.gpa} +
+
+ Trend + + + {improvementTrend} + +
+
+
+
+ + {/* Performance Metrics Grid */} + + +
+
+ + Completion Rate +
+
+
+ {Math.round( + (data.summary.completedAssignments / data.summary.totalAssignments) * 100, + )} + % +
+
+ {data.summary.completedAssignments} of {data.summary.totalAssignments} assignments +
+ +
+
+ + + +
+
+ + On-Time Rate +
+
+
+ {Math.round( + (data.summary.onTimeSubmissions / + (data.summary.onTimeSubmissions + + data.summary.lateSubmissions + + data.summary.missingSubmissions)) * + 100, + )} + % +
+
+ {data.summary.onTimeSubmissions} on-time submissions +
+ +
+
+ + + +
+
+ + Attendance +
+
+
{analytics.attendanceRate}%
+
Class attendance rate
+ = 90 + ? 'success' + : analytics.attendanceRate >= 80 + ? 'warning' + : 'danger' + } + className={styles.metricProgress} + /> +
+
+ + + +
+
+ + Participation +
+
+
{analytics.participationScore}%
+
Class participation score
+ = 90 + ? 'success' + : analytics.participationScore >= 80 + ? 'primary' + : 'warning' + } + className={styles.metricProgress} + /> +
+
+ +
+ + {/* Performance Summary */} +
+
Performance Summary
+ + +
+
Academic Achievements
+
+
+ + Highest Score: {data.summary.highestScore}% +
+
+ + Average Score: {data.summary.averageScore}% +
+
+ + + Time Management:{' '} + {data.summary.timeManagement?.excellent > + data.summary.timeManagement?.needsImprovement + ? 'Excellent' + : 'Needs Improvement'} + +
+
+
+ + +
+
Key Insights
+
+ {data.summary.strengths?.slice(0, 3).map((strength, index) => ( +
+
+ {strength} +
+ ))} +
+
+ + +
+ + {/* Last Updated */} +
+ + + Last updated:{' '} + {new Date(data.student.lastUpdated).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + })} + +
+ + + ); +}; + +export default OverallPerformance; diff --git a/src/components/EductionPortal/EvaluationResults/OverallPerformance.module.css b/src/components/EductionPortal/EvaluationResults/OverallPerformance.module.css new file mode 100644 index 0000000000..f0533641ca --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/OverallPerformance.module.css @@ -0,0 +1,533 @@ +/* ============================================================================ + OVERALL PERFORMANCE COMPONENT STYLES - Premium Dashboard Design + ============================================================================ */ + +.performanceCard { + border: none; + border-radius: var(--border-radius-lg); + box-shadow: var(--shadow-elegant); + background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 100%); + overflow: hidden; + transition: all 0.3s ease; +} + +.performanceCard:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-floating); +} + +/* Header Styles */ +.performanceHeader { + background: var(--primary-gradient); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding: 1.5rem; +} + +.headerContent { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1rem; +} + +.headerLeft { + display: flex; + align-items: center; + gap: 1rem; +} + +.headerIcon { + font-size: 2.5rem; + color: var(--accent-light); + background: rgba(255, 255, 255, 0.1); + padding: 0.75rem; + border-radius: var(--border-radius-full); + backdrop-filter: blur(10px); +} + +.headerTitle { + color: var(--text-light); + margin: 0; + font-size: 1.5rem; + font-weight: 600; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.headerSubtitle { + color: var(--accent-light); + margin: 0.25rem 0 0 0; + font-size: 0.9rem; + font-weight: 400; + opacity: 0.9; +} + +.headerRight { + display: flex; + align-items: center; +} + +.performanceBadge { + font-size: 0.85rem; + font-weight: 600; + padding: 0.6rem 1.2rem; + border-radius: var(--border-radius-full); + text-transform: uppercase; + letter-spacing: 0.5px; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.badgeIcon { + font-size: 1rem; +} + +/* Body Styles */ +.performanceBody { + padding: 2rem; + background: var(--surface-light); +} + +/* Main Score Section */ +.mainScoreSection { + margin-bottom: 2.5rem; +} + +.scoreDisplay { + display: flex; + align-items: center; + gap: 2rem; + padding: 2rem; + background: linear-gradient(135deg, #f8faff 0%, #e8f2ff 100%); + border: 1px solid rgba(59, 130, 246, 0.2); + border-radius: var(--border-radius-lg); + position: relative; + overflow: hidden; +} + +.scoreDisplay::before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 6px; + background: var(--primary-gradient); +} + +.scoreCircle { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 120px; + height: 120px; + background: var(--primary-gradient); + border-radius: var(--border-radius-full); + color: white; + text-align: center; + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3); + flex-shrink: 0; +} + +.scoreNumber { + font-size: 2rem; + font-weight: 700; + line-height: 1; + margin-bottom: 0.25rem; +} + +.scoreLabel { + font-size: 0.8rem; + font-weight: 500; + opacity: 0.9; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.scoreDetails { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 1.5rem; + flex: 1; +} + +.scoreMetric { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.metricLabel { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.metricValue { + font-size: 1.5rem; + font-weight: 600; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.trendPositive { + color: var(--success-color); +} + +.trendNegative { + color: var(--error-color); +} + +.trendIcon { + font-size: 1rem; +} + +.trendUp { + transform: rotate(0deg); +} + +.trendDown { + transform: rotate(180deg); +} + +/* Metrics Grid */ +.metricsGrid { + margin: 0 -0.75rem; +} + +.metricCol { + padding: 0 0.75rem; + margin-bottom: 1.5rem; +} + +.metricCard { + background: white; + border: 1px solid var(--border-light); + border-radius: var(--border-radius-md); + padding: 1.5rem; + height: 100%; + transition: all 0.2s ease; + position: relative; + overflow: hidden; +} + +.metricCard:hover { + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); +} + +.metricCard::before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 3px; + background: var(--primary-color); + opacity: 0.7; +} + +.metricHeader { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.metricIcon { + font-size: 1.25rem; + color: var(--primary-color); + background: rgba(59, 130, 246, 0.1); + padding: 0.5rem; + border-radius: var(--border-radius-sm); +} + +.metricTitle { + font-size: 0.9rem; + font-weight: 600; + color: var(--text-secondary); + margin: 0; + text-transform: uppercase; + letter-spacing: 0.3px; +} + +.metricContent { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.metricNumber { + font-size: 2rem; + font-weight: 700; + color: var(--text-primary); + line-height: 1; +} + +.metricDescription { + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.4; +} + +.metricProgress { + height: 8px; + border-radius: var(--border-radius-full); +} + +/* Summary Section */ +.summarySection { + margin-top: 2.5rem; + padding-top: 2rem; + border-top: 2px solid var(--accent-light); +} + +.summaryTitle { + font-size: 1.3rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 1.5rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.summaryCard { + background: white; + border: 1px solid var(--border-light); + border-radius: var(--border-radius-md); + padding: 1.5rem; + height: 100%; +} + +.summaryCardTitle { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid var(--accent-light); +} + +.achievementsList, +.insightsList { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.achievementItem, +.insightItem { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem; + border-radius: var(--border-radius-sm); + transition: all 0.2s ease; +} + +.achievementItem:hover, +.insightItem:hover { + background: var(--surface-light); +} + +.achievementIcon { + color: var(--primary-color); + font-size: 1rem; + flex-shrink: 0; +} + +.insightBullet { + width: 6px; + height: 6px; + background: var(--primary-color); + border-radius: var(--border-radius-full); + flex-shrink: 0; +} + +.insightText { + font-size: 0.9rem; + color: var(--text-secondary); + line-height: 1.4; +} + +/* Last Updated */ +.lastUpdated { + margin-top: 2rem; + padding-top: 1rem; + border-top: 1px solid var(--border-light); + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--text-muted); + font-size: 0.85rem; +} + +.updateIcon { + font-size: 0.9rem; +} + +.updateText { + font-style: italic; +} + +/* Loading States */ +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 4rem; + gap: 1rem; +} + +.loadingSpinner { + width: 50px; + height: 50px; + border: 4px solid var(--accent-light); + border-top: 4px solid var(--primary-color); + border-radius: var(--border-radius-full); + animation: spin 1s linear infinite; +} + +.loadingText { + color: var(--text-muted); + font-size: 1rem; + text-align: center; + margin: 0; +} + +/* No Data State */ +.noDataContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 4rem; + gap: 1rem; +} + +.noDataIcon { + font-size: 4rem; + color: var(--accent-light); + opacity: 0.6; +} + +.noDataText { + color: var(--text-muted); + font-size: 1.1rem; + text-align: center; + margin: 0; +} + +/* Responsive Design */ +@media (max-width: 992px) { + .scoreDisplay { + flex-direction: column; + text-align: center; + gap: 1.5rem; + } + + .scoreDetails { + grid-template-columns: repeat(2, 1fr); + } + + .metricCol { + margin-bottom: 1rem; + } +} + +@media (max-width: 768px) { + .performanceBody { + padding: 1.5rem; + } + + .scoreDisplay { + padding: 1.5rem; + } + + .scoreCircle { + width: 100px; + height: 100px; + } + + .scoreNumber { + font-size: 1.75rem; + } + + .scoreDetails { + grid-template-columns: 1fr; + gap: 1rem; + } + + .metricNumber { + font-size: 1.75rem; + } + + .metricCard { + padding: 1rem; + } +} + +@media (max-width: 480px) { + .performanceHeader { + padding: 1rem; + } + + .performanceBody { + padding: 1rem; + } + + .headerContent { + flex-direction: column; + align-items: flex-start; + } + + .scoreDisplay { + padding: 1rem; + } + + .summarySection { + margin-top: 1.5rem; + padding-top: 1.5rem; + } +} + +/* Animations */ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Accessibility */ +@media (prefers-reduced-motion: reduce) { + .performanceCard, + .metricCard, + .achievementItem, + .insightItem { + transition: none; + } + + .loadingSpinner { + animation: none; + } +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + .performanceCard, + .metricCard, + .summaryCard { + border-width: 2px; + } + + .scoreCircle { + border: 2px solid var(--text-primary); + } +} \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/SummaryStats.jsx b/src/components/EductionPortal/EvaluationResults/SummaryStats.jsx new file mode 100644 index 0000000000..d376a0e275 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/SummaryStats.jsx @@ -0,0 +1,381 @@ +import React from 'react'; +import { Card, CardBody, CardHeader, Progress, Badge, Row, Col } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faChartBar, + faTrophy, + faCalendar, + faCheckCircle, + faClock, + faExclamationTriangle, + faArrowTrendUp, + faBullseye, + faUsers, + faPercentage, + faAward, + faLightbulb, +} from '@fortawesome/free-solid-svg-icons'; +import styles from './SummaryStats.module.css'; + +const SummaryStats = ({ summary, analytics, trends, isLoading }) => { + if (isLoading) { + return ( + + +
+
+

Loading summary statistics...

+
+ + + ); + } + + if (!summary || !analytics) { + return ( + + +
+ +

No summary data available yet.

+
+
+
+ ); + } + + const improvementTrend = summary.improvementTrend || '+0%'; + const isImproving = improvementTrend.startsWith('+'); + + const completionRate = Math.round( + (summary.completedAssignments / summary.totalAssignments) * 100, + ); + const onTimeRate = Math.round( + (summary.onTimeSubmissions / + (summary.onTimeSubmissions + summary.lateSubmissions + summary.missingSubmissions)) * + 100, + ); + + return ( + + +
+ +
+

Summary Statistics

+

Comprehensive performance overview

+
+
+
+ + + {/* Key Performance Indicators */} +
+
Key Performance Indicators
+ + +
+
+ +
+
+
{summary.averageScore}%
+
Average Score
+
+ + + {improvementTrend} + +
+
+
+ + + +
+
+ +
+
+
#{analytics.classRank}
+
Class Rank
+
+ {analytics.percentile}th percentile of {analytics.totalStudents} +
+
+
+ + + +
+
+ +
+
+
{completionRate}%
+
Completion Rate
+
+ {summary.completedAssignments}/{summary.totalAssignments} completed +
+
+
+ + + +
+
+ +
+
+
{onTimeRate}%
+
On-Time Rate
+
+ {summary.onTimeSubmissions} on-time submissions +
+
+
+ +
+
+ + {/* Performance Breakdown */} +
+
Performance Breakdown
+ + +
+
Submission Status
+
+
+
+
On Time
+ +
{summary.onTimeSubmissions}
+
+
+
+
+
Late
+ +
{summary.lateSubmissions}
+
+
+
+
+
Missing
+ +
{summary.missingSubmissions}
+
+
+
+
+ + + +
+
Score Distribution
+
+
+ + Highest Score: + {summary.highestScore}% +
+
+ + Average Score: + {summary.averageScore}% +
+
+ + Lowest Score: + {summary.lowestScore}% +
+
+
+ +
+
+ + {/* Academic Insights */} +
+
Academic Insights
+ + +
+
+ +
Strengths
+ + {summary.strengths?.length || 0} + +
+
+ {summary.strengths?.slice(0, 4).map((strength, index) => ( +
+
+ {strength} +
+ ))} +
+
+ + + +
+
+ +
Areas for Improvement
+ + {summary.areasForImprovement?.length || 0} + +
+
+ {summary.areasForImprovement?.slice(0, 4).map((area, index) => ( +
+
+ {area} +
+ ))} +
+
+ + +
+ + {/* Performance Trends */} + {trends && ( +
+
Performance Trends
+
+
+ {trends.monthly?.map((month, index) => ( +
+
{month.month}
+
{month.score}%
+
Rank #{month.rank}
+ {index > 0 && ( +
+ trends.monthly[index - 1].score + ? styles.trendUp + : styles.trendDown + }`} + /> + trends.monthly[index - 1].score + ? styles.trendPositive + : styles.trendNegative + }`} + > + {month.score > trends.monthly[index - 1].score ? '+' : ''} + {(month.score - trends.monthly[index - 1].score).toFixed(1)}% + +
+ )} +
+ ))} +
+
+
+ )} + + {/* Additional Metrics */} +
+
Additional Metrics
+ + +
+ +
{analytics.attendanceRate}%
+
Attendance
+
+ + +
+ +
{analytics.participationScore}%
+
Participation
+
+ + +
+ +
{analytics.gpa}
+
Current GPA
+
+ + +
+ +
{analytics.creditHours}
+
Credit Hours
+
+ +
+
+ + + ); +}; + +export default SummaryStats; diff --git a/src/components/EductionPortal/EvaluationResults/SummaryStats.module.css b/src/components/EductionPortal/EvaluationResults/SummaryStats.module.css new file mode 100644 index 0000000000..81e848f85a --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/SummaryStats.module.css @@ -0,0 +1,317 @@ +/* SummaryStats Component Styles */ + +.summaryContainer { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.statCard { + background: #ffffff; + border: none; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + overflow: hidden; + transition: all 0.3s ease; + position: relative; +} + +.statCard:hover { + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); + transform: translateY(-4px); +} + +.statCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: var(--accent-color); +} + +.statCard.totalAssignments::before { + --accent-color: linear-gradient(90deg, #3b82f6, #1d4ed8); +} + +.statCard.onTime::before { + --accent-color: linear-gradient(90deg, #10b981, #059669); +} + +.statCard.late::before { + --accent-color: linear-gradient(90deg, #f59e0b, #d97706); +} + +.statCard.average::before { + --accent-color: linear-gradient(90deg, #8b5cf6, #7c3aed); +} + +.statCardBody { + padding: 1.5rem; +} + +.statHeader { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; +} + +.statIcon { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.25rem; + color: white; + background: var(--icon-bg); +} + +.statCard.totalAssignments .statIcon { + --icon-bg: linear-gradient(135deg, #3b82f6, #1d4ed8); +} + +.statCard.onTime .statIcon { + --icon-bg: linear-gradient(135deg, #10b981, #059669); +} + +.statCard.late .statIcon { + --icon-bg: linear-gradient(135deg, #f59e0b, #d97706); +} + +.statCard.average .statIcon { + --icon-bg: linear-gradient(135deg, #8b5cf6, #7c3aed); +} + +.statTrend { + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 0.5rem; + border-radius: 12px; +} + +.statTrend.positive { + color: #065f46; + background: #d1fae5; +} + +.statTrend.negative { + color: #991b1b; + background: #fee2e2; +} + +.statTrend.neutral { + color: #374151; + background: #f3f4f6; +} + +.trendIcon { + font-size: 0.625rem; +} + +.statContent { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.statValue { + font-size: 2.25rem; + font-weight: 800; + line-height: 1; + color: #1f2937; + margin: 0; +} + +.statLabel { + font-size: 0.875rem; + font-weight: 600; + color: #6b7280; + text-transform: uppercase; + letter-spacing: 0.05em; + margin: 0; +} + +.statDescription { + font-size: 0.8125rem; + color: #9ca3af; + line-height: 1.4; + margin: 0; +} + +.statProgress { + margin-top: 1rem; +} + +.progressLabel { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.75rem; + color: #6b7280; + margin-bottom: 0.5rem; +} + +.progressBar { + width: 100%; + height: 6px; + background: #f3f4f6; + border-radius: 3px; + overflow: hidden; +} + +.progressFill { + height: 100%; + border-radius: 3px; + transition: width 0.8s ease; +} + +.statCard.totalAssignments .progressFill { + background: linear-gradient(90deg, #3b82f6, #1d4ed8); +} + +.statCard.onTime .progressFill { + background: linear-gradient(90deg, #10b981, #059669); +} + +.statCard.late .progressFill { + background: linear-gradient(90deg, #f59e0b, #d97706); +} + +.statCard.average .progressFill { + background: linear-gradient(90deg, #8b5cf6, #7c3aed); +} + +.loadingContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + text-align: center; +} + +.loadingSpinner { + width: 40px; + height: 40px; + border: 3px solid #e5e7eb; + border-top: 3px solid #3b82f6; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; +} + +.loadingText { + color: #6b7280; + font-size: 0.875rem; + margin: 0; +} + +.noDataContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + text-align: center; +} + +.noDataIcon { + font-size: 3rem; + color: #d1d5db; + margin-bottom: 1rem; +} + +.noDataText { + color: #6b7280; + font-size: 1rem; + margin: 0; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Animation for counting effect */ +@keyframes countUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.statValue { + animation: countUp 0.6s ease-out; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .summaryContainer { + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + } + + .statCardBody { + padding: 1.25rem; + } + + .statIcon { + width: 40px; + height: 40px; + font-size: 1rem; + } + + .statValue { + font-size: 1.875rem; + } + + .statLabel { + font-size: 0.8125rem; + } + + .statDescription { + font-size: 0.75rem; + } +} + +@media (max-width: 480px) { + .summaryContainer { + grid-template-columns: 1fr; + gap: 0.75rem; + } + + .statCardBody { + padding: 1rem; + } + + .statHeader { + margin-bottom: 0.75rem; + } + + .statIcon { + width: 36px; + height: 36px; + font-size: 0.875rem; + } + + .statValue { + font-size: 1.75rem; + } + + .statTrend { + font-size: 0.6875rem; + padding: 0.2rem 0.4rem; + } +} \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx b/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx new file mode 100644 index 0000000000..5c1165a189 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx @@ -0,0 +1,440 @@ +import React, { useState } from 'react'; +import { Card, CardBody, CardHeader, Badge, Collapse, Row, Col, Progress } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + faList, + faChevronDown, + faChevronUp, + faCheckCircle, + faClock, + faExclamationTriangle, + faFileAlt, + faCalendar, + faWeight, + faComments, + faDownload, + faEye, +} from '@fortawesome/free-solid-svg-icons'; +import { getStatusInfo } from './mockData'; +import styles from './TaskDetailsList.module.css'; + +const TaskDetailsList = ({ tasks, selectedCategory = 'all', categories, isLoading }) => { + const [expandedTasks, setExpandedTasks] = useState(new Set()); + const [filterCategory, setFilterCategory] = useState(selectedCategory); + const [sortBy, setSortBy] = useState('dueDate'); + + // Update local filter when parent category changes + React.useEffect(() => { + setFilterCategory(selectedCategory); + }, [selectedCategory]); + + if (isLoading) { + return ( + + +
+
+

Loading task details...

+
+ + + ); + } + + if (!tasks || tasks.length === 0) { + return ( + + +
+ +

No task details available yet.

+
+
+
+ ); + } + + const toggleTaskExpansion = taskId => { + const newExpanded = new Set(expandedTasks); + if (newExpanded.has(taskId)) { + newExpanded.delete(taskId); + } else { + newExpanded.add(taskId); + } + setExpandedTasks(newExpanded); + }; + + // Filter and sort tasks + const filteredTasks = tasks + .filter(task => { + if (filterCategory === 'all') return true; + // Handle both category name and category id formats + return task.category === filterCategory || task.category === filterCategory.toLowerCase(); + }) + .sort((a, b) => { + if (sortBy === 'dueDate') { + return new Date(a.dueDate) - new Date(b.dueDate); + } + if (sortBy === 'score') { + return b.percentage - a.percentage; + } + if (sortBy === 'name') { + return a.name.localeCompare(b.name); + } + return 0; + }); + + const getTaskTypeIcon = type => { + if (type?.toLowerCase().includes('exam')) return faFileAlt; + if (type?.toLowerCase().includes('quiz')) return faCheckCircle; + if (type?.toLowerCase().includes('project')) return faEye; + return faFileAlt; + }; + + const getCategoryColor = category => { + const colors = { + assignments: '#3b82f6', + quizzes: '#10b981', + exams: '#f59e0b', + projects: '#8b5cf6', + }; + return colors[category] || '#6b7280'; + }; + + // Create dropdown options from parent categories or fallback to task categories + const categoryOptions = categories + ? ['all', ...categories.map(cat => cat.name.toLowerCase())] + : ['all', ...new Set(tasks.map(task => task.category))]; + + return ( + + +
+
+ +
+

+ {filterCategory === 'all' + ? 'Task Details' + : `${filterCategory.charAt(0).toUpperCase() + filterCategory.slice(1)} Tasks`} +

+

+ {filterCategory === 'all' + ? 'Detailed breakdown of all assignments and assessments' + : `Showing ${filteredTasks.length} ${filterCategory} task${ + filteredTasks.length !== 1 ? 's' : '' + }`} +

+
+
+
+ + {filteredTasks.length} tasks + + {filterCategory !== 'all' && ( + + Filtered + + )} +
+
+ + {/* Filters and Controls */} +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ {filteredTasks.map(task => { + const isExpanded = expandedTasks.has(task.id); + const statusInfo = getStatusInfo(task.status); + const categoryColor = getCategoryColor(task.category); + const isOverdue = new Date(task.dueDate) < new Date() && task.status === 'Missing'; + + return ( +
+ {/* Task Header */} +
toggleTaskExpansion(task.id)} + role="button" + tabIndex={0} + onKeyDown={e => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + toggleTaskExpansion(task.id); + } + }} + > +
+
+ +
+
+
{task.name}
+
+ + {task.category} + + + {task.type} + + + + {task.weightage}% + +
+
+
+ +
+
+
+ {task.percentage}% +
+
+ {task.earnedMarks}/{task.totalMarks} +
+
+
+ + + {statusInfo.label} + +
+ + + {new Date(task.dueDate).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + })} + +
+
+
+ +
+
+
+ + {/* Expandable Details */} + +
+ {/* Progress Bar */} +
+ = 90 + ? 'success' + : task.percentage >= 80 + ? 'primary' + : task.percentage >= 70 + ? 'warning' + : 'danger' + } + className={styles.taskProgress} + /> +
+ Score: {task.percentage}% ({task.earnedMarks}/{task.totalMarks} points) +
+
+ + {/* Submission Info */} + + +
+
Submission Details
+
+ Submitted:{' '} + {task.submittedDate + ? new Date(task.submittedDate).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + }) + : 'Not submitted'} +
+
+ Due Date:{' '} + {new Date(task.dueDate).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + })} +
+ {task.timeSpent && ( +
+ Time Spent: {task.timeSpent} +
+ )} + {task.attempts && ( +
+ Attempts: {task.attempts} + {task.maxAttempts && ` / ${task.maxAttempts}`} +
+ )} +
+ + + +
+
Additional Info
+ {task.rubricScores && ( +
+ Rubric Breakdown: +
+ {Object.entries(task.rubricScores).map(([criteria, score]) => ( +
+ {criteria}: + + {score.earned}/{score.total} + + +
+ ))} +
+
+ )} + + {task.sections && ( +
+ Exam Sections: +
+ {Object.entries(task.sections).map(([section, score]) => ( +
+ {section}: + + {score.earned}/{score.total} + +
+ ))} +
+
+ )} + + {task.technologies && ( +
+ Technologies: +
+ {task.technologies.map(tech => ( + + {tech} + + ))} +
+
+ )} +
+ +
+ + {/* Teacher Feedback */} + {task.teacherFeedback && ( +
+
+ + Teacher Feedback +
+
+

{task.teacherFeedback}

+
+
+ )} + + {/* Attachments */} + {task.attachments && task.attachments.length > 0 && ( +
+
+ + Attachments +
+
+ {task.attachments.map(attachment => ( +
+ + {attachment} +
+ ))} +
+
+ )} +
+
+
+ ); + })} +
+
+
+ ); +}; + +export default TaskDetailsList; diff --git a/src/components/EductionPortal/EvaluationResults/TaskDetailsList.module.css b/src/components/EductionPortal/EvaluationResults/TaskDetailsList.module.css new file mode 100644 index 0000000000..7caa8af55f --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/TaskDetailsList.module.css @@ -0,0 +1,617 @@ +/* TaskDetailsList Component Styles */ + +.tasksCard { + background: #ffffff; + border: none; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + margin-bottom: 2rem; + overflow: hidden; + transition: all 0.3s ease; +} + +.tasksCard:hover { + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12); + transform: translateY(-2px); +} + +.tasksHeader { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 1.5rem; + border-bottom: none; +} + +.headerContent { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 1rem; +} + +.headerLeft { + display: flex; + align-items: center; + gap: 1rem; +} + +.headerIcon { + font-size: 1.5rem; + color: rgba(255, 255, 255, 0.9); +} + +.headerTitle { + margin: 0; + font-size: 1.5rem; + font-weight: 600; + color: white; +} + +.headerSubtitle { + margin: 0.25rem 0 0 0; + font-size: 0.875rem; + color: rgba(255, 255, 255, 0.8); + font-weight: 400; +} + +.headerRight { + display: flex; + align-items: center; +} + +.countBadge { + background: rgba(255, 255, 255, 0.2); + color: white; + border: 1px solid rgba(255, 255, 255, 0.3); + font-weight: 500; +} + +.filterBadge { + background: rgba(255, 255, 255, 0.9); + color: #667eea; + border: 1px solid rgba(255, 255, 255, 0.5); + font-weight: 600; + font-size: 0.7rem; + margin-left: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.025em; +} + +.controlsSection { + padding-top: 1rem; + border-top: 1px solid rgba(255, 255, 255, 0.2); +} + +.filterControls { + display: flex; + gap: 1.5rem; + align-items: center; + flex-wrap: wrap; +} + +.filterGroup { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.filterLabel { + font-size: 0.875rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + margin: 0; +} + +.filterSelect { + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 6px; + color: white; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + min-width: 140px; + transition: all 0.2s ease; +} + +.filterSelect:focus { + background: rgba(255, 255, 255, 0.25); + border-color: rgba(255, 255, 255, 0.5); + outline: none; + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2); +} + +.filterSelect option { + background: #333; + color: white; +} + +.tasksBody { + padding: 0; +} + +.tasksList { + display: flex; + flex-direction: column; +} + +.taskCard { + border-bottom: 1px solid #e9ecef; + transition: all 0.2s ease; +} + +.taskCard:hover { + background: #f8f9fa; +} + +.taskCard:last-child { + border-bottom: none; +} + +.taskCard.overdue { + border-left: 4px solid #dc3545; + background: #fff5f5; +} + +.taskHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.25rem; + cursor: pointer; + transition: all 0.2s ease; +} + +.taskHeader:hover { + background: rgba(102, 126, 234, 0.05); +} + +.taskInfo { + display: flex; + align-items: center; + gap: 1rem; + flex: 1; +} + +.taskIcon { + width: 40px; + height: 40px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + background: #f8f9fa; + font-size: 1.1rem; +} + +.taskMain { + flex: 1; +} + +.taskName { + margin: 0 0 0.5rem 0; + font-size: 1.1rem; + font-weight: 600; + color: #2d3748; + line-height: 1.3; +} + +.taskMeta { + display: flex; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} + +.categoryBadge { + font-size: 0.75rem; + font-weight: 500; + padding: 0.25rem 0.5rem; + border-radius: 4px; + background: transparent; + border: 1px solid; +} + +.typeBadge { + font-size: 0.75rem; + font-weight: 500; + padding: 0.25rem 0.5rem; + border-radius: 4px; +} + +.weightInfo { + font-size: 0.75rem; + color: #6b7280; + display: flex; + align-items: center; + gap: 0.25rem; +} + +.weightIcon { + font-size: 0.7rem; +} + +.taskSummary { + display: flex; + align-items: center; + gap: 1.5rem; +} + +.scoreDisplay { + text-align: center; + min-width: 80px; +} + +.scoreNumber { + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + margin-bottom: 0.25rem; +} + +.scoreBreakdown { + font-size: 0.75rem; + color: #6b7280; + font-weight: 500; +} + +.statusSection { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + min-width: 100px; +} + +.statusBadge { + font-size: 0.75rem; + font-weight: 500; + padding: 0.25rem 0.5rem; + border-radius: 4px; + display: flex; + align-items: center; + gap: 0.25rem; +} + +.statusIcon { + font-size: 0.7rem; +} + +.dueDateInfo { + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75rem; + color: #6b7280; +} + +.dueDateIcon { + font-size: 0.7rem; +} + +.dueDateText { + font-weight: 500; +} + +.expandToggle { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background: #f3f4f6; + color: #6b7280; + transition: all 0.2s ease; +} + +.expandToggle:hover { + background: #e5e7eb; + color: #374151; +} + +.expandIcon { + font-size: 0.875rem; +} + +.taskDetails { + padding: 1.5rem; + background: #f8f9fa; + border-top: 1px solid #e9ecef; +} + +.progressSection { + margin-bottom: 1.5rem; +} + +.taskProgress { + margin-bottom: 0.5rem; +} + +.progressLabel { + font-size: 0.875rem; + color: #6b7280; + font-weight: 500; + text-align: center; +} + +.detailsRow { + margin-bottom: 1.5rem; +} + +.detailSection { + background: white; + border-radius: 8px; + padding: 1rem; + height: 100%; +} + +.detailTitle { + font-size: 0.875rem; + font-weight: 600; + color: #374151; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #e5e7eb; +} + +.detailItem { + margin-bottom: 0.5rem; + font-size: 0.875rem; + line-height: 1.4; +} + +.detailItem strong { + color: #374151; + font-weight: 600; +} + +.rubricSection { + margin-top: 0.75rem; +} + +.rubricScores { + margin-top: 0.5rem; +} + +.rubricItem { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.5rem; +} + +.rubricCriteria { + font-size: 0.75rem; + color: #6b7280; + min-width: 80px; + font-weight: 500; +} + +.rubricScore { + font-size: 0.75rem; + font-weight: 600; + color: #374151; + min-width: 40px; +} + +.rubricProgress { + flex: 1; + height: 4px; +} + +.sectionsInfo { + margin-top: 0.75rem; +} + +.sectionScores { + margin-top: 0.5rem; +} + +.sectionItem { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.25rem 0; + font-size: 0.75rem; +} + +.sectionName { + color: #6b7280; + font-weight: 500; +} + +.sectionScore { + font-weight: 600; + color: #374151; +} + +.technologiesInfo { + margin-top: 0.75rem; +} + +.techTags { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + margin-top: 0.5rem; +} + +.techTag { + font-size: 0.7rem; + padding: 0.125rem 0.375rem; + border-radius: 3px; +} + +.feedbackSection { + background: white; + border-radius: 8px; + padding: 1rem; + margin-bottom: 1rem; + border-left: 4px solid #3b82f6; +} + +.feedbackIcon { + margin-right: 0.5rem; + color: #3b82f6; +} + +.feedbackContent { + margin-top: 0.5rem; +} + +.feedbackText { + font-size: 0.875rem; + line-height: 1.5; + color: #4b5563; + margin: 0; +} + +.attachmentsSection { + background: white; + border-radius: 8px; + padding: 1rem; + border-left: 4px solid #10b981; +} + +.attachmentIcon { + margin-right: 0.5rem; + color: #10b981; +} + +.attachmentsList { + margin-top: 0.5rem; +} + +.attachmentItem { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.25rem 0; + font-size: 0.875rem; +} + +.fileIcon { + color: #6b7280; + font-size: 0.75rem; +} + +.fileName { + color: #374151; + font-weight: 500; +} + +.loadingContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + text-align: center; +} + +.loadingSpinner { + width: 40px; + height: 40px; + border: 3px solid #e5e7eb; + border-top: 3px solid #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; +} + +.loadingText { + color: #6b7280; + font-size: 0.875rem; + margin: 0; +} + +.noDataContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + text-align: center; +} + +.noDataIcon { + font-size: 3rem; + color: #d1d5db; + margin-bottom: 1rem; +} + +.noDataText { + color: #6b7280; + font-size: 1rem; + margin: 0; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .tasksHeader { + padding: 1rem; + } + + .headerContent { + flex-direction: column; + gap: 1rem; + align-items: flex-start; + } + + .filterControls { + flex-direction: column; + gap: 0.75rem; + align-items: stretch; + } + + .taskHeader { + flex-direction: column; + gap: 1rem; + align-items: stretch; + } + + .taskInfo { + gap: 0.75rem; + } + + .taskSummary { + justify-content: space-between; + gap: 1rem; + } + + .taskDetails { + padding: 1rem; + } + + .rubricItem { + flex-direction: column; + align-items: stretch; + gap: 0.25rem; + } + + .rubricCriteria { + min-width: auto; + } +} + +@media (max-width: 480px) { + .taskMeta { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + + .scoreDisplay { + min-width: auto; + } + + .statusSection { + min-width: auto; + align-items: flex-start; + } +} \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/TeacherFeedback.jsx b/src/components/EductionPortal/EvaluationResults/TeacherFeedback.jsx new file mode 100644 index 0000000000..56f4950cbe --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/TeacherFeedback.jsx @@ -0,0 +1,155 @@ +import React from 'react'; +import { Card, CardBody, CardHeader, Badge } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faUser, faCalendar, faStar, faComments } from '@fortawesome/free-solid-svg-icons'; +import styles from './TeacherFeedback.module.css'; + +const TeacherFeedback = ({ feedback, isLoading }) => { + if (isLoading) { + return ( + + +
+
+

Loading teacher feedback...

+
+ + + ); + } + + if (!feedback) { + return ( + + +
+ +

No teacher feedback available yet.

+
+
+
+ ); + } + + const getRatingStars = rating => { + const stars = []; + const fullStars = Math.floor(rating / 20); // Convert 100-point scale to 5-star + const hasHalfStar = rating % 20 >= 10; + + for (let i = 0; i < 5; i++) { + if (i < fullStars) { + stars.push(); + } else if (i === fullStars && hasHalfStar) { + stars.push(); + } else { + stars.push(); + } + } + return stars; + }; + + return ( + + +
+
+ +
+

{feedback.teacherName}

+

{feedback.teacherTitle}

+
+
+
+
+
+ {getRatingStars(feedback.overallRating === 'Excellent' ? 95 : 85)} +
+ + {feedback.overallRating} + +
+
+ + + {new Date(feedback.lastUpdated).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + })} + +
+
+
+
+ + + {/* Overall Feedback */} +
+
Overall Assessment
+
+

{feedback.overall}

+
+
+ + {/* Strengths */} +
+
+ Strengths + + {feedback.strengths?.length || 0} + +
+
+ {feedback.strengths?.map((strength, index) => ( +
+
+ {strength} +
+ ))} +
+
+ + {/* Recommendations */} +
+
+ Areas for Improvement + + {feedback.recommendations?.length || 0} + +
+
+ {feedback.recommendations?.map((recommendation, index) => ( +
+
+ {recommendation} +
+ ))} +
+
+ + {/* Next Steps */} +
+
+ Suggested Next Steps + + {feedback.nextSteps?.length || 0} + +
+
+ {feedback.nextSteps?.map((step, index) => ( +
+
{index + 1}
+ {step} +
+ ))} +
+
+ + + ); +}; + +export default TeacherFeedback; diff --git a/src/components/EductionPortal/EvaluationResults/TeacherFeedback.module.css b/src/components/EductionPortal/EvaluationResults/TeacherFeedback.module.css new file mode 100644 index 0000000000..3461176ece --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/TeacherFeedback.module.css @@ -0,0 +1,458 @@ +/* ============================================================================ + TEACHER FEEDBACK COMPONENT STYLES - Premium UI Design + ============================================================================ */ + +.feedbackCard { + border: none; + border-radius: var(--border-radius-lg); + box-shadow: var(--shadow-elegant); + background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 100%); + overflow: hidden; + transition: all 0.3s ease; +} + +.feedbackCard:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-floating); +} + +/* Header Styles */ +.feedbackHeader { + background: var(--primary-gradient); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding: 1.5rem; +} + +.headerContent { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1rem; +} + +.headerLeft { + display: flex; + align-items: center; + gap: 1rem; +} + +.teacherIcon { + font-size: 2.5rem; + color: var(--accent-light); + background: rgba(255, 255, 255, 0.1); + padding: 0.75rem; + border-radius: var(--border-radius-full); + backdrop-filter: blur(10px); +} + +.teacherInfo h4.teacherName { + color: var(--text-light); + margin: 0; + font-size: 1.4rem; + font-weight: 600; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.teacherInfo p.teacherTitle { + color: var(--accent-light); + margin: 0.25rem 0 0 0; + font-size: 0.9rem; + font-weight: 400; + opacity: 0.9; +} + +.headerRight { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.5rem; +} + +.ratingContainer { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.stars { + display: flex; + gap: 0.25rem; +} + +.stars .starFilled { + color: #fbbf24; + font-size: 1.1rem; + filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2)); +} + +.stars .starHalf { + color: #fbbf24; + font-size: 1.1rem; + opacity: 0.7; + filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2)); +} + +.stars .starEmpty { + color: rgba(255, 255, 255, 0.3); + font-size: 1.1rem; +} + +.ratingBadge { + font-size: 0.8rem; + font-weight: 600; + padding: 0.4rem 0.8rem; + border-radius: var(--border-radius-full); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.dateContainer { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--accent-light); + font-size: 0.85rem; + opacity: 0.9; +} + +.dateIcon { + font-size: 0.9rem; +} + +/* Body Styles */ +.feedbackBody { + padding: 2rem; + background: var(--surface-light); +} + +.feedbackSection { + margin-bottom: 2rem; +} + +.feedbackSection:last-child { + margin-bottom: 0; +} + +.sectionTitle { + font-size: 1.2rem; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + border-bottom: 2px solid var(--accent-light); + padding-bottom: 0.5rem; +} + +.countBadge { + font-size: 0.7rem; + padding: 0.25rem 0.5rem; + border-radius: var(--border-radius-full); + font-weight: 600; +} + +.overallFeedback { + background: linear-gradient(135deg, #f8faff 0%, #e8f2ff 100%); + border: 1px solid rgba(59, 130, 246, 0.2); + border-radius: var(--border-radius-md); + padding: 1.5rem; + position: relative; + overflow: hidden; +} + +.overallFeedback::before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 4px; + background: var(--primary-gradient); +} + +.feedbackText { + color: var(--text-secondary); + line-height: 1.7; + margin: 0; + font-size: 1rem; + font-style: italic; +} + +/* Strengths Styles */ +.strengthsTitle { + color: var(--success-color); + font-weight: 600; +} + +.strengthsList { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.strengthItem { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.75rem; + background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%); + border: 1px solid rgba(34, 197, 94, 0.2); + border-radius: var(--border-radius-md); + transition: all 0.2s ease; +} + +.strengthItem:hover { + transform: translateX(4px); + box-shadow: 0 2px 8px rgba(34, 197, 94, 0.15); +} + +.strengthBullet { + width: 8px; + height: 8px; + background: var(--success-color); + border-radius: var(--border-radius-full); + margin-top: 0.5rem; + flex-shrink: 0; +} + +.strengthText { + color: var(--text-secondary); + line-height: 1.6; + font-size: 0.95rem; +} + +/* Recommendations Styles */ +.recommendationsTitle { + color: var(--warning-color); + font-weight: 600; +} + +.recommendationsList { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.recommendationItem { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.75rem; + background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%); + border: 1px solid rgba(245, 158, 11, 0.2); + border-radius: var(--border-radius-md); + transition: all 0.2s ease; +} + +.recommendationItem:hover { + transform: translateX(4px); + box-shadow: 0 2px 8px rgba(245, 158, 11, 0.15); +} + +.recommendationBullet { + width: 8px; + height: 8px; + background: var(--warning-color); + border-radius: var(--border-radius-full); + margin-top: 0.5rem; + flex-shrink: 0; +} + +.recommendationText { + color: var(--text-secondary); + line-height: 1.6; + font-size: 0.95rem; +} + +/* Next Steps Styles */ +.nextStepsTitle { + color: var(--info-color); + font-weight: 600; +} + +.nextStepsList { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.nextStepItem { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.75rem; + background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%); + border: 1px solid rgba(59, 130, 246, 0.2); + border-radius: var(--border-radius-md); + transition: all 0.2s ease; +} + +.nextStepItem:hover { + transform: translateX(4px); + box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15); +} + +.nextStepNumber { + background: var(--info-color); + color: white; + width: 24px; + height: 24px; + border-radius: var(--border-radius-full); + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + font-weight: 600; + flex-shrink: 0; + margin-top: 0.1rem; +} + +.nextStepText { + color: var(--text-secondary); + line-height: 1.6; + font-size: 0.95rem; +} + +/* Loading States */ +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem; + gap: 1rem; +} + +.loadingSpinner { + width: 40px; + height: 40px; + border: 3px solid var(--accent-light); + border-top: 3px solid var(--primary-color); + border-radius: var(--border-radius-full); + animation: spin 1s linear infinite; +} + +.loadingText { + color: var(--text-muted); + font-size: 0.9rem; + text-align: center; + margin: 0; +} + +/* No Feedback State */ +.noFeedbackContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem; + gap: 1rem; +} + +.noFeedbackIcon { + font-size: 3rem; + color: var(--accent-light); + opacity: 0.6; +} + +.noFeedbackText { + color: var(--text-muted); + font-size: 1rem; + text-align: center; + margin: 0; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .headerContent { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + + .headerRight { + align-self: stretch; + align-items: flex-start; + } + + .ratingContainer { + justify-content: space-between; + width: 100%; + } + + .feedbackBody { + padding: 1.5rem; + } + + .strengthItem, + .recommendationItem, + .nextStepItem { + padding: 1rem; + } + + .teacherIcon { + font-size: 2rem; + padding: 0.6rem; + } + + .teacherInfo h4.teacherName { + font-size: 1.2rem; + } + + .teacherInfo p.teacherTitle { + font-size: 0.85rem; + } +} + +@media (max-width: 480px) { + .feedbackHeader { + padding: 1rem; + } + + .feedbackBody { + padding: 1rem; + } + + .sectionTitle { + font-size: 1.1rem; + } + + .overallFeedback { + padding: 1rem; + } +} + +/* Animations */ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Accessibility */ +@media (prefers-reduced-motion: reduce) { + .feedbackCard, + .strengthItem, + .recommendationItem, + .nextStepItem { + transition: none; + } + + .loadingSpinner { + animation: none; + } +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + .feedbackCard { + border: 2px solid var(--text-primary); + } + + .strengthItem, + .recommendationItem, + .nextStepItem { + border-width: 2px; + } +} \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js b/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js new file mode 100644 index 0000000000..458399fe3d --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js @@ -0,0 +1,189 @@ +import { toast } from 'react-toastify'; +import httpService from '../../../services/httpService'; +import { ENDPOINTS } from '../../../utils/URL'; + +/** + * Service for handling evaluation results notifications + */ +class EvaluationNotificationService { + /** + * Trigger notification when new evaluation results are published + */ + static triggerNewResultsNotification(studentId, evaluationData) { + try { + // Show immediate toast notification + toast.success( + `🎓 New evaluation results available! Overall score: ${evaluationData.student.overallScore}%`, + { + position: 'top-right', + autoClose: 8000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + }, + ); + + // Send system notification to student + this.createSystemNotification(studentId, { + type: 'evaluation_results', + title: 'New Evaluation Results Available', + message: `Your evaluation results have been published. Overall score: ${evaluationData.student.overallScore}%. View detailed feedback and task breakdown.`, + data: { + evaluationId: evaluationData.student.id, + overallScore: evaluationData.student.overallScore, + publishedDate: new Date().toISOString(), + }, + priority: 'medium', + actionUrl: '/evaluation-results', + }); + + return true; + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error triggering evaluation notification:', error); + return false; + } + } + + /** + * Create system notification for student + */ + static async createSystemNotification(studentId, notificationData) { + try { + const notification = { + recipient: studentId, + message: `
+
${notificationData.title}
+

${notificationData.message}

+

Published: ${new Date( + notificationData.data.publishedDate, + ).toLocaleDateString()}

+ View Results +
`, + isSystemGenerated: true, + type: notificationData.type, + priority: notificationData.priority, + data: notificationData.data, + }; + + // Send to notification service + const response = await httpService.post( + `${ENDPOINTS.APIEndpoint()}/notification/`, + notification, + ); + + if (response.status === 201) { + // eslint-disable-next-line no-console + console.log('Evaluation notification created successfully'); + return response.data; + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error creating system notification:', error); + // Fallback to toast if system notification fails + toast.info('📚 Check your notifications for new evaluation results!'); + } + } + + /** + * Check for new evaluation results and notify if found + */ + static async checkForNewResults(studentId, lastChecked = null) { + try { + const queryParams = lastChecked ? `?since=${lastChecked}` : ''; + const response = await httpService.get( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results/notifications${queryParams}`, + ); + + if (response.data?.hasNewResults) { + this.triggerNewResultsNotification(studentId, response.data.evaluationData); + return true; + } + + return false; + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error checking for new evaluation results:', error); + return false; + } + } + + /** + * Mark evaluation notification as viewed + */ + static async markResultsAsViewed(studentId, evaluationId) { + try { + await httpService.post( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results/${evaluationId}/mark-viewed`, + { studentId }, + ); + return true; + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error marking results as viewed:', error); + return false; + } + } + + /** + * Show specific notification for performance level + */ + static showPerformanceNotification(score, studentName = 'Student') { + let message = ''; + let type = 'info'; + + if (score >= 90) { + message = `🏆 Excellent work, ${studentName}! You scored ${score}%`; + type = 'success'; + } else if (score >= 80) { + message = `👏 Great job, ${studentName}! You scored ${score}%`; + type = 'success'; + } else if (score >= 70) { + message = `📈 Good progress, ${studentName}. You scored ${score}%`; + type = 'info'; + } else { + message = `💪 Keep working hard, ${studentName}. You scored ${score}%`; + type = 'warning'; + } + + toast[type](message, { + position: 'top-right', + autoClose: 6000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + }); + } + + /** + * Batch notification for multiple students (educator use) + */ + static async notifyMultipleStudents(studentIds, evaluationData) { + try { + const notifications = studentIds.map(studentId => ({ + studentId, + evaluationData, + })); + + const response = await httpService.post( + `${ENDPOINTS.APIEndpoint()}/evaluation-results/batch-notify`, + { notifications }, + ); + + if (response.status === 200) { + toast.success(`Notifications sent to ${studentIds.length} students successfully!`); + return true; + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error sending batch notifications:', error); + toast.error('Failed to send notifications to some students'); + return false; + } + } +} + +export default EvaluationNotificationService; diff --git a/src/components/EductionPortal/EvaluationResults/mockData.js b/src/components/EductionPortal/EvaluationResults/mockData.js new file mode 100644 index 0000000000..183e0f1100 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/mockData.js @@ -0,0 +1,437 @@ +// ============================================================================ +// MOCK EVALUATION DATA - Realistic Academic Performance Data +// ============================================================================ + +export const mockEvaluationData = { + student: { + id: 'STU-2024-001', + name: 'Alex Johnson', + email: 'alex.johnson@school.edu', + class: 'Computer Science - Year 3', + semester: 'Fall 2024', + lastUpdated: '2024-09-25T10:30:00Z', + profileImage: '/api/placeholder/60/60', + }, + + // Overall performance score (calculated from all categories) + overallScore: 87.3, + + // Performance categories with detailed breakdown + categories: [ + { + id: 'assignments', + name: 'Assignments', + weightage: 40, + totalItems: 12, + completedItems: 11, + totalMarks: 1200, + earnedMarks: 1050, + percentage: 87.5, + performanceLevel: 'excellent', // excellent, good, fair, poor + color: '#10b981', + icon: 'faClipboardCheck', + description: 'Programming assignments, essays, and individual homework tasks', + dueDate: '2024-10-15', + submissions: { + onTime: 9, + late: 2, + missing: 1, + }, + }, + { + id: 'quizzes', + name: 'Quizzes', + weightage: 25, + totalItems: 8, + completedItems: 8, + totalMarks: 800, + earnedMarks: 720, + percentage: 90.0, + performanceLevel: 'excellent', + color: '#3b82f6', + icon: 'faQuestion', + description: 'Short weekly quizzes and knowledge check assessments', + dueDate: '2024-09-30', + submissions: { + onTime: 7, + late: 1, + missing: 0, + }, + }, + { + id: 'exams', + name: 'Exams', + weightage: 25, + totalItems: 3, + completedItems: 2, + totalMarks: 300, + earnedMarks: 245, + percentage: 81.7, + performanceLevel: 'good', + color: '#f59e0b', + icon: 'faGraduationCap', + description: 'Comprehensive midterm and final examinations', + dueDate: '2024-12-10', + submissions: { + onTime: 2, + late: 0, + missing: 1, + }, + }, + { + id: 'projects', + name: 'Projects', + weightage: 10, + totalItems: 2, + completedItems: 2, + totalMarks: 200, + earnedMarks: 185, + percentage: 92.5, + performanceLevel: 'excellent', + color: '#8b5cf6', + icon: 'faProjectDiagram', + description: 'Collaborative group projects and individual capstone work', + dueDate: '2024-11-20', + submissions: { + onTime: 1, + late: 1, + missing: 0, + }, + }, + ], + + // Detailed task/assignment list + tasks: [ + // Assignments + { + id: 'task-001', + name: 'Data Structures Implementation', + category: 'assignments', + type: 'Programming Assignment', + weightage: 8, + totalMarks: 100, + earnedMarks: 95, + percentage: 95, + status: 'On Time', + statusColor: '#10b981', + submittedDate: '2024-09-10T14:30:00Z', + dueDate: '2024-09-12T23:59:00Z', + teacherFeedback: + 'Excellent implementation! Clean code structure and efficient algorithms. Minor improvement needed in edge case handling.', + rubricScores: { + 'Code Quality': { earned: 18, total: 20 }, + Functionality: { earned: 20, total: 20 }, + Documentation: { earned: 17, total: 20 }, + Testing: { earned: 16, total: 20 }, + Performance: { earned: 19, total: 20 }, + }, + attachments: ['solution.py', 'test_cases.py', 'documentation.pdf'], + }, + { + id: 'task-002', + name: 'Algorithm Analysis Report', + category: 'assignments', + type: 'Research Assignment', + weightage: 6, + totalMarks: 100, + earnedMarks: 88, + percentage: 88, + status: 'On Time', + statusColor: '#10b981', + submittedDate: '2024-09-20T16:45:00Z', + dueDate: '2024-09-22T23:59:00Z', + teacherFeedback: + 'Good analysis of time complexity. Your comparison of different sorting algorithms was thorough. Consider adding more real-world examples.', + rubricScores: { + 'Research Quality': { earned: 17, total: 20 }, + 'Analysis Depth': { earned: 18, total: 20 }, + 'Writing Quality': { earned: 16, total: 20 }, + Citations: { earned: 19, total: 20 }, + Presentation: { earned: 18, total: 20 }, + }, + }, + { + id: 'task-003', + name: 'Database Design Assignment', + category: 'assignments', + type: 'Design Assignment', + weightage: 7, + totalMarks: 100, + earnedMarks: 78, + percentage: 78, + status: 'Late', + statusColor: '#f59e0b', + submittedDate: '2024-09-28T10:15:00Z', + dueDate: '2024-09-25T23:59:00Z', + teacherFeedback: + 'Submitted 3 days late (-10 points). Good ER diagram design, but normalization could be improved. Review 3NF principles.', + rubricScores: { + 'ER Diagram': { earned: 18, total: 20 }, + Normalization: { earned: 14, total: 20 }, + 'SQL Queries': { earned: 17, total: 20 }, + Documentation: { earned: 15, total: 20 }, + Timeliness: { earned: 10, total: 20 }, + }, + }, + + // Quizzes + { + id: 'quiz-001', + name: 'Arrays and Linked Lists Quiz', + category: 'quizzes', + type: 'Online Quiz', + weightage: 3, + totalMarks: 100, + earnedMarks: 92, + percentage: 92, + status: 'On Time', + statusColor: '#10b981', + submittedDate: '2024-09-15T11:30:00Z', + dueDate: '2024-09-15T12:00:00Z', + teacherFeedback: + 'Excellent understanding of data structures. Only minor mistake in time complexity analysis.', + timeSpent: '25 minutes', + attempts: 1, + maxAttempts: 1, + }, + { + id: 'quiz-002', + name: 'Sorting Algorithms Quiz', + category: 'quizzes', + type: 'Online Quiz', + weightage: 3, + totalMarks: 100, + earnedMarks: 85, + percentage: 85, + status: 'On Time', + statusColor: '#10b981', + submittedDate: '2024-09-22T14:45:00Z', + dueDate: '2024-09-22T15:00:00Z', + teacherFeedback: 'Good performance. Review merge sort implementation details.', + timeSpent: '28 minutes', + attempts: 1, + maxAttempts: 1, + }, + + // Exams + { + id: 'exam-001', + name: 'Midterm Examination', + category: 'exams', + type: 'Written Exam', + weightage: 15, + totalMarks: 150, + earnedMarks: 128, + percentage: 85.3, + status: 'On Time', + statusColor: '#10b981', + submittedDate: '2024-10-15T12:00:00Z', + dueDate: '2024-10-15T12:00:00Z', + teacherFeedback: + 'Strong performance across all topics. Excellent problem-solving in dynamic programming section. Minor errors in graph algorithms.', + sections: { + 'Data Structures': { earned: 28, total: 30 }, + Algorithms: { earned: 25, total: 30 }, + 'Dynamic Programming': { earned: 30, total: 30 }, + 'Graph Theory': { earned: 22, total: 30 }, + 'Problem Solving': { earned: 23, total: 30 }, + }, + }, + { + id: 'exam-002', + name: 'Final Examination', + category: 'exams', + type: 'Comprehensive Exam', + weightage: 10, + totalMarks: 150, + earnedMarks: 0, + percentage: 0, + status: 'Missing', + statusColor: '#ef4444', + submittedDate: null, + dueDate: '2024-12-10T12:00:00Z', + teacherFeedback: 'Exam not yet taken. Scheduled for December 10th.', + }, + + // Projects + { + id: 'project-001', + name: 'Social Media Analytics Platform', + category: 'projects', + type: 'Group Project', + weightage: 6, + totalMarks: 100, + earnedMarks: 94, + percentage: 94, + status: 'On Time', + statusColor: '#10b981', + submittedDate: '2024-11-01T18:30:00Z', + dueDate: '2024-11-02T23:59:00Z', + teacherFeedback: + 'Outstanding project! Excellent use of modern technologies, clean architecture, and comprehensive testing. Great teamwork evident.', + teamMembers: ['Alex Johnson', 'Sarah Chen', 'Michael Rodriguez'], + technologies: ['React', 'Node.js', 'MongoDB', 'Python', 'Machine Learning'], + deliverables: ['Source Code', 'Documentation', 'Presentation', 'Demo Video'], + }, + { + id: 'project-002', + name: 'Algorithm Visualization Tool', + category: 'projects', + type: 'Individual Project', + weightage: 4, + totalMarks: 100, + earnedMarks: 91, + percentage: 91, + status: 'Late', + statusColor: '#f59e0b', + submittedDate: '2024-11-22T14:20:00Z', + dueDate: '2024-11-20T23:59:00Z', + teacherFeedback: + 'Great interactive visualizations! Submitted 2 days late (-5 points). Consider adding more algorithms and improving UI responsiveness.', + technologies: ['JavaScript', 'D3.js', 'HTML5 Canvas', 'CSS3'], + features: [ + 'Sorting Visualizations', + 'Graph Algorithms', + 'Interactive Controls', + 'Step-by-step Execution', + ], + }, + ], + + // Summary statistics + summary: { + totalAssignments: 12, + completedAssignments: 11, + onTimeSubmissions: 19, + lateSubmissions: 4, + missingSubmissions: 2, + averageScore: 87.3, + highestScore: 95, + lowestScore: 78, + improvementTrend: '+5.2%', // compared to previous semester + timeManagement: { + excellent: 19, + good: 4, + needsImprovement: 2, + }, + strengths: [ + 'Excellent programming skills', + 'Strong analytical thinking', + 'Good documentation practices', + 'Consistent performance', + ], + areasForImprovement: [ + 'Time management for large assignments', + 'Edge case handling in code', + 'Exam preparation strategies', + ], + }, + + // Teacher's general feedback + teacherFeedback: { + overall: + 'Alex demonstrates exceptional understanding of computer science concepts and consistently produces high-quality work. Strong programming abilities and analytical skills are evident throughout the semester.', + strengths: [ + 'Outstanding problem-solving capabilities', + 'Clean and well-documented code', + 'Excellent participation in class discussions', + 'Strong collaboration skills in group projects', + 'Consistent effort and improvement mindset', + ], + recommendations: [ + 'Focus on time management to avoid late submissions', + 'Practice more complex algorithm implementations', + 'Prepare more thoroughly for comprehensive exams', + 'Consider tutoring other students to reinforce learning', + ], + nextSteps: [ + 'Enroll in Advanced Algorithms course', + 'Consider internship opportunities', + 'Join competitive programming club', + 'Start working on senior capstone project ideas', + ], + overallRating: 'Excellent', + teacherName: 'Dr. Emily Rodriguez', + teacherTitle: 'Professor of Computer Science', + lastUpdated: '2024-09-25T15:45:00Z', + }, + + // Performance trends over time + trends: { + monthly: [ + { month: 'August', score: 82.5, assignments: 3, rank: 15 }, + { month: 'September', score: 87.8, assignments: 5, rank: 12 }, + { month: 'October', score: 89.2, assignments: 4, rank: 8 }, + ], + categoryTrends: { + assignments: [85, 87, 89], + quizzes: [88, 90, 92], + exams: [82, 85, 0], // 0 for not yet taken + projects: [90, 94, 91], + }, + }, + + // Notifications about new results + notifications: [ + { + id: 'notif-001', + type: 'new_grade', + title: 'New Grade Posted', + message: 'Your grade for "Algorithm Visualization Tool" has been posted.', + date: '2024-09-25T09:15:00Z', + read: false, + priority: 'medium', + }, + { + id: 'notif-002', + type: 'feedback_available', + title: 'Teacher Feedback Available', + message: 'Dr. Rodriguez has provided feedback on your midterm exam.', + date: '2024-09-24T14:30:00Z', + read: false, + priority: 'high', + }, + ], + + // Performance analytics + analytics: { + classRank: 8, + totalStudents: 45, + percentile: 82, + gpa: 3.8, + creditHours: 15, + attendanceRate: 95, + participationScore: 92, + }, +}; + +// Helper functions for data processing +export const getPerformanceLevel = percentage => { + if (percentage >= 90) return { level: 'excellent', color: '#10b981', label: 'Excellent' }; + if (percentage >= 80) return { level: 'good', color: '#3b82f6', label: 'Good' }; + if (percentage >= 70) return { level: 'fair', color: '#f59e0b', label: 'Fair' }; + return { level: 'poor', color: '#ef4444', label: 'Needs Improvement' }; +}; + +export const getStatusInfo = status => { + switch (status.toLowerCase()) { + case 'on time': + return { color: '#10b981', icon: 'faCheckCircle', label: 'On Time' }; + case 'late': + return { color: '#f59e0b', icon: 'faClock', label: 'Late Submission' }; + case 'missing': + return { color: '#ef4444', icon: 'faTimesCircle', label: 'Missing' }; + default: + return { color: '#6b7280', icon: 'faQuestion', label: 'Unknown' }; + } +}; + +export const calculateCategoryProgress = category => { + const completionRate = (category.completedItems / category.totalItems) * 100; + const scoreRate = category.percentage; + return { + completionRate: Math.round(completionRate), + scoreRate: Math.round(scoreRate * 10) / 10, + isComplete: category.completedItems === category.totalItems, + }; +}; diff --git a/src/routes.jsx b/src/routes.jsx index 7aee4b2b03..4c59331331 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -138,6 +138,7 @@ import CommunityCalendar from './components/CommunityPortal/Calendar/CommunityCa import EPProtectedRoute from './components/common/EPDashboard/EPProtectedRoute'; import EPLogin from './components/EductionPortal/Login'; import EPDashboard from './components/EductionPortal'; +import EvaluationResults from './components/EductionPortal/EvaluationResults/EvaluationResults'; import MostSusceptibleTools from './components/MostSusceptible/toolBreakdownChart'; @@ -681,6 +682,7 @@ export default ( {/* Good Education Portal Routes */} + Date: Sat, 25 Oct 2025 19:53:46 -0500 Subject: [PATCH 2/9] Fix: Student Evaluation results-1 --- .../EvaluationResults/CategoryBreakdown.jsx | 91 ++++---- .../CategoryBreakdown.module.css | 218 +++++++++++++++++- .../EvaluationResults/EvaluationResults.jsx | 39 +++- .../EvaluationResults/evaluationApiService.js | 87 +++++++ .../evaluationNotificationService.js | 8 +- .../EvaluationResults/mockData.js | 14 +- src/routes.jsx | 6 +- 7 files changed, 405 insertions(+), 58 deletions(-) create mode 100644 src/components/EductionPortal/EvaluationResults/evaluationApiService.js diff --git a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx index 8e942fa131..9f5468d0e5 100644 --- a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx +++ b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx @@ -71,18 +71,25 @@ const CategoryBreakdown = ({ categories, selectedCategory = 'all', isLoading }) >
- -
-

- {selectedCategory === 'all' - ? 'Category Breakdown' - : `${selectedCategory.charAt(0).toUpperCase() + - selectedCategory.slice(1)} Performance`} -

-

- {selectedCategory === 'all' - ? `Showing all ${categories.length} categories` - : `Showing ${filteredCategories.length} selected category`} +

+ +
+

+ {selectedCategory === 'all' + ? 'Category Breakdown' + : `${selectedCategory.charAt(0).toUpperCase() + + selectedCategory.slice(1)} Performance`} +

+

+ {selectedCategory === 'all' + ? `Showing all ${categories.length} categories` + : `Showing ${filteredCategories.length} selected category`} +

+
+
+
+

+ Instructor: Dr. Emily Rodriguez • Professor of Computer Science

@@ -213,35 +220,41 @@ const CategoryBreakdown = ({ categories, selectedCategory = 'all', isLoading })
- {/* Due Date Alert */} - {isOverdue && ( + {/* Date Alerts Section */} + {(isOverdue || (!isOverdue && category.dueDate)) && (
-
- - - Overdue: {new Date(category.dueDate).toLocaleDateString()} - -
-
- )} + {/* Overdue Alert */} + {isOverdue && ( +
+ + + Overdue:{' '} + {new Date(category.dueDate).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} + +
+ )} - {/* Due Date Info */} - {!isOverdue && category.dueDate && ( -
-
- - - Due:{' '} - {new Date(category.dueDate).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - })} - -
+ {/* Due Date Info */} + {!isOverdue && category.dueDate && ( +
+ + + Due:{' '} + {new Date(category.dueDate).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} + +
+ )}
)}
diff --git a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css index e146f7ba16..0d0e622c95 100644 --- a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css +++ b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.module.css @@ -1,22 +1,54 @@ /* CategoryBreakdown Component Styles */ +/* Main breakdown container */ +.breakdownCard { + border: 1px solid #e5e7eb; + border-radius: 16px; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + background: #ffffff; + overflow: hidden; + transition: all 0.3s ease; +} + +.breakdownCard:hover { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +.breakdownCard.filtered { + border: 2px solid #4f46e5; + box-shadow: 0 10px 15px -3px rgba(79, 70, 229, 0.1), 0 4px 6px -2px rgba(79, 70, 229, 0.05); +} + +.breakdownCard.filtered .categoryHeader { + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); +} + +/* Individual category cards */ .categoryCard { background: #ffffff; - border: none; + border: 1px solid #e5e7eb; border-radius: 16px; margin-bottom: 1.5rem; overflow: hidden; transition: all 0.3s ease; position: relative; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } .categoryCard:hover { transform: translateY(-2px); + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + border-color: #d1d5db; } .categoryCard.filtered { border: 2px solid #4f46e5; background: #f8faff; + box-shadow: 0 10px 15px -3px rgba(79, 70, 229, 0.1), 0 4px 6px -2px rgba(79, 70, 229, 0.05); +} + +.categoryCard.filtered:hover { + box-shadow: 0 20px 25px -5px rgba(79, 70, 229, 0.1), 0 10px 10px -5px rgba(79, 70, 229, 0.04); } .breakdownCard.filtered .categoryHeader { @@ -38,20 +70,52 @@ --category-accent: linear-gradient(90deg, #10b981, #065f46); } +.categoryCard.excellent { + border-color: #d1fae5; +} + +.categoryCard.excellent:hover { + border-color: #10b981; + box-shadow: 0 10px 15px -3px rgba(16, 185, 129, 0.1), 0 4px 6px -2px rgba(16, 185, 129, 0.05); +} + .categoryCard.good::before { --category-accent: linear-gradient(90deg, #3b82f6, #1e40af); } +.categoryCard.good { + border-color: #dbeafe; +} + +.categoryCard.good:hover { + border-color: #3b82f6; + box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.1), 0 4px 6px -2px rgba(59, 130, 246, 0.05); +} + .categoryCard.fair::before { --category-accent: linear-gradient(90deg, #f59e0b, #92400e); } +.categoryCard.fair { + border-color: #fef3c7; +} + +.categoryCard.fair:hover { + border-color: #f59e0b; + box-shadow: 0 10px 15px -3px rgba(245, 158, 11, 0.1), 0 4px 6px -2px rgba(245, 158, 11, 0.05); +} + .categoryCard.poor::before { --category-accent: linear-gradient(90deg, #ef4444, #991b1b); } -.categoryCard:hover { - transform: translateY(-4px); +.categoryCard.poor { + border-color: #fee2e2; +} + +.categoryCard.poor:hover { + border-color: #ef4444; + box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.1), 0 4px 6px -2px rgba(239, 68, 68, 0.05); } /* Clear Performance Indicator Bar - replaces confusing line charts */ @@ -303,6 +367,7 @@ display: flex; justify-content: space-between; align-items: center; + width: 100%; } .headerLeft { @@ -311,22 +376,40 @@ gap: 1rem; } +.headerRight { + flex-shrink: 0; +} + +.instructorInfo { + font-size: 0.875rem; + color: #6b7280; + margin: 0; + font-weight: 500; +} + +/* Header styles */ +.breakdownHeader { + background: #f9fafb; + border-bottom: 1px solid #e5e7eb; + padding: 1.5rem 2rem; +} + .headerIcon { font-size: 1.5rem; - color: rgba(255, 255, 255, 0.9); + color: #6366f1; } .headerTitle { margin: 0; font-size: 1.5rem; font-weight: 600; - color: white; + color: #111827; } .headerSubtitle { margin: 0.25rem 0 0 0; font-size: 0.875rem; - color: rgba(255, 255, 255, 0.8); + color: #6b7280; font-weight: 400; } @@ -594,4 +677,127 @@ .categoryIcon { display: none; } +} + +.alertSection { + margin-top: 1rem; + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +.overdueAlert { + background-color: #fee2e2; /* Light red background */ + border: 1px solid #fca5a5; + border-radius: 8px; + padding: 0.75rem 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + flex: 1; + min-width: 250px; +} + +.alertIcon { + color: #dc2626; /* Red color for icon */ + font-size: 1rem; +} + +.alertText { + color: #7f1d1d; /* Dark red text */ + font-weight: 500; + font-size: 0.875rem; +} + +.dueDateSection { + margin-top: 1rem; + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +.dueDateInfo { + background-color: #dbeafe; /* Light blue background */ + border: 1px solid #93c5fd; + border-radius: 8px; + padding: 0.75rem 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + flex: 1; + min-width: 250px; +} + +.dueDateIcon { + color: #2563eb; /* Blue color for icon */ + font-size: 1rem; +} + +.dueDateText { + color: #1e3a8a; /* Dark blue text */ + font-weight: 500; + font-size: 0.875rem; +} + +/* Summary Statistics Styles */ +.summarySection { + margin-top: 2rem; + padding-top: 2rem; + border-top: 1px solid #e5e7eb; +} + +.summaryTitle { + text-align: center; + font-size: 1.5rem; + font-weight: 600; + color: #111827; + margin-bottom: 2rem; +} + +.summaryStats { + margin: 0; +} + +.summaryCard { + background: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 12px; + padding: 1.5rem 1rem; + text-align: center; + position: relative; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + transition: all 0.3s ease; + margin-bottom: 1rem; +} + +.summaryCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #6366f1, #8b5cf6); + border-radius: 12px 12px 0 0; +} + +.summaryCard:hover { + transform: translateY(-2px); + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.summaryNumber { + font-size: 2.5rem; + font-weight: 700; + color: #111827; + line-height: 1; + margin-bottom: 0.5rem; +} + +.summaryLabel { + font-size: 0.875rem; + font-weight: 500; + color: #6b7280; + text-transform: uppercase; + letter-spacing: 0.025em; } \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx index 808670c621..81608179f3 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -34,7 +34,33 @@ const EvaluationResults = ({ auth }) => { setLoading(true); // Simulate network delay await new Promise(resolve => setTimeout(resolve, 1500)); - setEvaluationData(mockEvaluationData); + + // Personalize the evaluation data with actual user name + const userName = auth?.user?.firstName || 'Student'; + const userLastName = auth?.user?.lastName || ''; + const fullName = `${userName} ${userLastName}`.trim(); + const userEmail = auth?.user?.email || 'student@school.edu'; + + const personalizedData = { + ...mockEvaluationData, + student: { + ...mockEvaluationData.student, + name: fullName, + email: userEmail, + }, + teacherFeedback: { + ...mockEvaluationData.teacherFeedback, + overall: mockEvaluationData.teacherFeedback.overall.replace('Alex', userName), + }, + tasks: mockEvaluationData.tasks.map(task => ({ + ...task, + teamMembers: task.teamMembers + ? task.teamMembers.map(member => (member === 'Alex Johnson' ? fullName : member)) + : task.teamMembers, + })), + }; + + setEvaluationData(personalizedData); // Trigger notification for new results (simulate this is new data) if (auth?.user?.userid && mockEvaluationData) { @@ -96,9 +122,16 @@ const EvaluationResults = ({ auth }) => {

Academic Performance Dashboard

-

Welcome back, {student.name}

+

+ Welcome back, {auth?.user?.firstName} {auth?.user?.lastName} +

- Last updated: {new Date(student.lastUpdated).toLocaleDateString()} + Last updated:{' '} + {new Date(student.lastUpdated).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })}
diff --git a/src/components/EductionPortal/EvaluationResults/evaluationApiService.js b/src/components/EductionPortal/EvaluationResults/evaluationApiService.js new file mode 100644 index 0000000000..acc05232fa --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/evaluationApiService.js @@ -0,0 +1,87 @@ +import httpService from '../../../services/httpService'; +import { ENDPOINTS } from '../../../utils/URL'; + +/** + * API service for evaluation results + * Handles all backend communication for student evaluation data + */ +class EvaluationApiService { + /** + * Fetch evaluation results for the current student + */ + static async getEvaluationResults() { + try { + const response = await httpService.get( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results`, + ); + return response.data; + } catch (error) { + if (error.response?.status === 404) { + throw new Error('No evaluation results found'); + } + if (error.response?.status === 403) { + throw new Error('Access denied to evaluation results'); + } + throw new Error('Failed to fetch evaluation results'); + } + } + + /** + * Check for new evaluation notifications + */ + static async checkNewNotifications(lastChecked = null) { + try { + const queryParams = lastChecked ? `?since=${lastChecked}` : ''; + const response = await httpService.get( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results/notifications${queryParams}`, + ); + return response.data; + } catch (error) { + throw new Error('Failed to check for new notifications'); + } + } + + /** + * Mark evaluation results as viewed + */ + static async markAsViewed(evaluationId) { + try { + const response = await httpService.post( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results/${evaluationId}/mark-viewed`, + ); + return response.data; + } catch (error) { + throw new Error('Failed to mark results as viewed'); + } + } + + /** + * Get evaluation details by category + */ + static async getEvaluationByCategory(category) { + try { + const response = await httpService.get( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results?category=${category}`, + ); + return response.data; + } catch (error) { + throw new Error(`Failed to fetch ${category} evaluation data`); + } + } + + /** + * Get detailed task information + */ + static async getTaskDetails(taskId) { + try { + const response = await httpService.get( + `${ENDPOINTS.APIEndpoint()}/student/evaluation-results/tasks/${taskId}`, + ); + return response.data; + } catch (error) { + throw new Error('Failed to fetch task details'); + } + } +} + +export default EvaluationApiService; diff --git a/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js b/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js index 458399fe3d..45daa067a7 100644 --- a/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js +++ b/src/components/EductionPortal/EvaluationResults/evaluationNotificationService.js @@ -35,7 +35,7 @@ class EvaluationNotificationService { publishedDate: new Date().toISOString(), }, priority: 'medium', - actionUrl: '/evaluation-results', + actionUrl: 'http://localhost:5173/educationportal/evaluation-results', }); return true; @@ -58,7 +58,11 @@ class EvaluationNotificationService {

${notificationData.message}

Published: ${new Date( notificationData.data.publishedDate, - ).toLocaleDateString()}

+ ).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })}

View Results diff --git a/src/components/EductionPortal/EvaluationResults/mockData.js b/src/components/EductionPortal/EvaluationResults/mockData.js index 183e0f1100..72b7f2b929 100644 --- a/src/components/EductionPortal/EvaluationResults/mockData.js +++ b/src/components/EductionPortal/EvaluationResults/mockData.js @@ -31,7 +31,7 @@ export const mockEvaluationData = { color: '#10b981', icon: 'faClipboardCheck', description: 'Programming assignments, essays, and individual homework tasks', - dueDate: '2024-10-15', + dueDate: '2024-10-14T12:00:00', submissions: { onTime: 9, late: 2, @@ -51,7 +51,7 @@ export const mockEvaluationData = { color: '#3b82f6', icon: 'faQuestion', description: 'Short weekly quizzes and knowledge check assessments', - dueDate: '2024-09-30', + dueDate: '2024-09-29T12:00:00', submissions: { onTime: 7, late: 1, @@ -71,7 +71,7 @@ export const mockEvaluationData = { color: '#f59e0b', icon: 'faGraduationCap', description: 'Comprehensive midterm and final examinations', - dueDate: '2024-12-10', + dueDate: '2024-12-09T12:00:00', submissions: { onTime: 2, late: 0, @@ -91,7 +91,7 @@ export const mockEvaluationData = { color: '#8b5cf6', icon: 'faProjectDiagram', description: 'Collaborative group projects and individual capstone work', - dueDate: '2024-11-20', + dueDate: '2024-11-19T12:00:00', submissions: { onTime: 1, late: 1, @@ -115,7 +115,7 @@ export const mockEvaluationData = { status: 'On Time', statusColor: '#10b981', submittedDate: '2024-09-10T14:30:00Z', - dueDate: '2024-09-12T23:59:00Z', + dueDate: '2024-09-29T23:59:00Z', teacherFeedback: 'Excellent implementation! Clean code structure and efficient algorithms. Minor improvement needed in edge case handling.', rubricScores: { @@ -225,8 +225,8 @@ export const mockEvaluationData = { percentage: 85.3, status: 'On Time', statusColor: '#10b981', - submittedDate: '2024-10-15T12:00:00Z', - dueDate: '2024-10-15T12:00:00Z', + submittedDate: '2024-10-14T12:00:00Z', + dueDate: '2024-10-14T12:00:00Z', teacherFeedback: 'Strong performance across all topics. Excellent problem-solving in dynamic programming section. Minor errors in graph algorithms.', sections: { diff --git a/src/routes.jsx b/src/routes.jsx index 4c59331331..5b0ae0da3c 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -682,7 +682,11 @@ export default ( {/* Good Education Portal Routes */} - + Date: Tue, 28 Oct 2025 21:39:49 -0500 Subject: [PATCH 3/9] Fix: Student Evaluation results-3 --- .../EvaluationResults/CategoryBreakdown.jsx | 2 +- .../EvaluationResults/EvaluationResults.jsx | 282 +++++-- .../EvaluationResults.module.css | 737 ++++++++++++------ .../EvaluationResults/TaskDetailsList.jsx | 2 +- .../EvaluationResults/mockData.js | 437 ----------- .../EvaluationResults/mockData_new.js | 252 ++++++ 6 files changed, 971 insertions(+), 741 deletions(-) delete mode 100644 src/components/EductionPortal/EvaluationResults/mockData.js create mode 100644 src/components/EductionPortal/EvaluationResults/mockData_new.js diff --git a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx index 9f5468d0e5..c1033b4375 100644 --- a/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx +++ b/src/components/EductionPortal/EvaluationResults/CategoryBreakdown.jsx @@ -12,7 +12,7 @@ import { faClock, faExclamationTriangle, } from '@fortawesome/free-solid-svg-icons'; -import { getPerformanceLevel, calculateCategoryProgress } from './mockData'; +import { getPerformanceLevel, calculateCategoryProgress } from './mockData_new'; import styles from './CategoryBreakdown.module.css'; const CategoryBreakdown = ({ categories, selectedCategory = 'all', isLoading }) => { diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx index 81608179f3..4e15c0a9cc 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -1,26 +1,18 @@ import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; -import { Container, Row, Col, Card, CardBody, Badge, Alert } from 'reactstrap'; +import { Container, Alert } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { - faChartLine, - faTrophy, - faClipboardCheck, - faExclamationTriangle, - faClock, - faCheckCircle, - faTimesCircle, - faStar, + faUser, + faBell, + faEye, faGraduationCap, + faExclamationTriangle, } from '@fortawesome/free-solid-svg-icons'; + import styles from './EvaluationResults.module.css'; -import OverallPerformance from './OverallPerformance'; -import CategoryBreakdown from './CategoryBreakdown'; -import TaskDetailsList from './TaskDetailsList'; -import SummaryStats from './SummaryStats'; -import TeacherFeedback from './TeacherFeedback'; import EvaluationNotificationService from './evaluationNotificationService'; -import { mockEvaluationData } from './mockData'; +import { mockEvaluationData } from './mockData_new'; const EvaluationResults = ({ auth }) => { const [evaluationData, setEvaluationData] = useState(null); @@ -28,13 +20,9 @@ const EvaluationResults = ({ auth }) => { const [selectedCategory, setSelectedCategory] = useState('all'); useEffect(() => { - // Simulate API call with realistic loading time - const loadEvaluationData = async () => { + // Load evaluation data immediately since we're using mock data + const loadEvaluationData = () => { try { - setLoading(true); - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 1500)); - // Personalize the evaluation data with actual user name const userName = auth?.user?.firstName || 'Student'; const userLastName = auth?.user?.lastName || ''; @@ -76,8 +64,10 @@ const EvaluationResults = ({ auth }) => { ); } } catch (error) { - // Handle error silently for now + // Set fallback data to prevent infinite loading + setEvaluationData(mockEvaluationData); } finally { + // Ensure loading is always set to false setLoading(false); } }; @@ -110,83 +100,213 @@ const EvaluationResults = ({ auth }) => { ); } - const { student, overallScore, categories, tasks, summary, teacherFeedback } = evaluationData; + const { student, overallScore, categories, tasks, summary } = evaluationData; + + // Calculate total score for new design (77/100 format) + const totalEarnedPoints = categories.reduce((sum, cat) => sum + cat.earnedMarks, 0); + const totalPossiblePoints = categories.reduce((sum, cat) => sum + cat.totalMarks, 0); + const percentageScore = Math.round((totalEarnedPoints / totalPossiblePoints) * 100); + + // Helper functions + const getPerformanceColor = percentage => { + if (percentage >= 80) return 'excellent'; + if (percentage >= 70) return 'good'; + if (percentage >= 60) return 'fair'; + return 'poor'; + }; + + const getStatusClass = status => { + if (status === 'On time' || status === 'completed') return 'onTime'; + if (status?.toLowerCase().includes('late')) return 'late'; + return 'onTime'; + }; + + const getPerformanceColorClass = percentage => { + if (percentage >= 80) return styles.excellent; + if (percentage >= 70) return styles.good; + if (percentage >= 60) return styles.fair; + return styles.poor; + }; return (
- {/* Header Section */} + {/* New Clean Header */}
-
- -
-

Academic Performance Dashboard

-

- Welcome back, {auth?.user?.firstName} {auth?.user?.lastName} -

- - Last updated:{' '} - {new Date(student.lastUpdated).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - })} - -
+
+

Evaluation Results

+

+ Comprehensive overview of your grades, performance on assignments, and detailed + feedback from teachers +

-
-
- {overallScore}% - Overall +
+
+ + Welcome, {auth?.user?.firstName || 'Student Name'}
+
- {/* Teacher Feedback Section */} - - - {/* Overall Performance Section */} - - - {/* Summary Statistics */} - - - {/* Category Filter */} -
-

- - Performance Breakdown -

-
- {['all', ...categories.map(cat => cat.name.toLowerCase())].map(category => ( - - ))} + {/* Overall Performance Summary */} +
+
+

Overall Performance Summary

+
+ {percentageScore}% + Overall Grade +
+
+
+
+
+
+
+ + Total Score: {totalEarnedPoints}/{totalPossiblePoints} points + +
- {/* Category Breakdown Table */} - + {/* Category Performance Table */} +
+

Overall Performance Summary

+
+ + + + + + + + + + + + + + {categories.map(category => ( + + + + + + + + + + ))} + +
CategoryWeightageItemsTotal PointsYour ScorePercentagePerformance
{category.name}{category.weightage}%{category.completedItems}{category.totalMarks}{category.earnedMarks}{Math.round(category.percentage)}% +
+
+
+
+
+
- {/* Detailed Tasks List */} - + {/* Performance Insights */} +
+
+

Performance Insights

+

+ You performed strongly in Assignments (80%),{' '} + Exams (80%), and Participation (80%). You may + improve your performance in Quizzes (60%) - consider reviewing quiz + preparation strategies. +

+
+ + +
+
+
+ + {/* Individual Assignment & Task Results */} +
+

Individual Assignment & Task Results

+
+ + + + + + + + + + + + + {tasks.slice(0, 6).map(task => ( + + + + + + + + + ))} + +
Assignment NameWeightageYour MarksPercentageStatusFeedback
+
+
{task.name}
+
+ Submitted: {new Date(task.submissionDate).toLocaleDateString()} +
+
+
{task.weightage || '8'}% + {task.earnedMarks}/{task.totalMarks} + + + {Math.round(task.percentage)}% + + + + {task.status || 'On time'} + + + +
+
+
+ + {/* Summary Cards */} +
+
+
6
+
Total Assignments
+
+
+
5
+
On Time
+
+
+
1
+
Late Submissions
+
+
+
72%
+
Avg Score
+
+
); diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css index 766f741a80..c25fd035f9 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css @@ -1,273 +1,595 @@ /* ============================================================================ - EVALUATION RESULTS - PREMIUM CSS MODULE - Modern, responsive design with excellent UX + EVALUATION RESULTS - CLEAN ACADEMIC DASHBOARD + Modern table-based design matching the provided mockup ============================================================================ */ -/* CSS Variables for consistent theming */ +/* CSS Variables for clean academic theme */ :root { - --primary-color: #3b82f6; - --success-color: #10b981; - --warning-color: #f59e0b; - --error-color: #ef4444; - --info-color: #6366f1; - --text-primary: #1f2937; - --text-secondary: #6b7280; - --text-light: #9ca3af; - --bg-primary: #ffffff; - --bg-secondary: #f9fafb; - --bg-accent: #f3f4f6; - --border-color: #e5e7eb; - --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); - --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); - --border-radius: 12px; - --border-radius-lg: 16px; - --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + --primary-color: #007bff; + --success-color: #28a745; + --warning-color: #ffc107; + --danger-color: #dc3545; + --info-color: #17a2b8; + --text-dark: #333333; + --text-muted: #6c757d; + --bg-white: #ffffff; + --bg-light: #f8f9fa; + --border-color: #dee2e6; + --table-border: #e9ecef; + --box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --border-radius: 0.375rem; } /* ============================================================================ - MAIN PAGE LAYOUT + MAIN PAGE LAYOUT - Clean White Background ============================================================================ */ .evaluationResultsPage { min-height: 100vh; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - background-attachment: fixed; + background-color: var(--bg-light); + padding: 0; } +/* ============================================================================ + HEADER SECTION - Clean Professional Header + ============================================================================ */ + .headerSection { - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(20px); + background-color: var(--bg-white); border-bottom: 1px solid var(--border-color); padding: 2rem 0; - margin-bottom: 2rem; - box-shadow: var(--shadow-sm); + margin-bottom: 1.5rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } .headerContent { display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 1.5rem; -} - -.studentInfo { - display: flex; - align-items: center; - gap: 1rem; + justify-content: flex-start; + align-items: flex-start; + gap: 2rem; + position: relative; } -.headerIcon { - font-size: 2.5rem; - color: var(--primary-color); - opacity: 0.9; +.headerLeft { + flex: 1; + max-width: 600px; } .pageTitle { - margin: 0; font-size: 2rem; font-weight: 700; - color: var(--text-primary); - background: linear-gradient(135deg, var(--primary-color), var(--info-color)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + color: var(--text-dark); + margin: 0 0 0.75rem 0; + letter-spacing: -0.025em; + text-align: left; } -.studentName { - margin: 0.25rem 0 0.125rem 0; - font-size: 1.125rem; - color: var(--text-secondary); +.pageSubtitle { + font-size: 0.9rem; + color: var(--text-muted); + margin: 0; + line-height: 1.5; + max-width: 500px; + text-align: left; +} + +.headerRight { + position: absolute; + right: 0; + top: 0; + display: flex; + align-items: center; + gap: 1.5rem; +} + +.userWelcome { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.9rem; + color: var(--text-dark); font-weight: 500; + padding: 0.5rem 1rem; + background-color: #f8f9fa; + border-radius: 1.5rem; + border: 1px solid var(--border-color); } -.lastUpdated { - color: var(--text-light); +.userIcon { + color: var(--text-muted); font-size: 0.875rem; } -.overallScoreBadge { - background: linear-gradient(135deg, var(--success-color), #059669); +.notificationIcon { + font-size: 1.25rem; + color: var(--text-muted); + cursor: pointer; + padding: 0.5rem; border-radius: 50%; - padding: 4px; - box-shadow: var(--shadow-lg); + transition: all 0.2s ease; } -.scoreCircle { - background: white; - border-radius: 50%; - width: 120px; - height: 120px; +.notificationIcon:hover { + color: var(--text-dark); + background-color: #f8f9fa; +} + +/* ============================================================================ + MAIN CONTENT CONTAINER + ============================================================================ */ + +.mainContent { + max-width: 1400px; + margin: 0 auto; + padding: 0 0.75rem; +} + +/* ============================================================================ + OVERALL PERFORMANCE SECTION + ============================================================================ */ + +.overallSection { + background-color: var(--bg-white); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: 1.5rem; + margin-bottom: 1.5rem; +} + +.sectionHeader { display: flex; - flex-direction: column; - justify-content: center; + justify-content: space-between; align-items: center; - position: relative; + margin-bottom: 1rem; } -.scoreNumber { - font-size: 2rem; - font-weight: 800; - color: var(--success-color); - line-height: 1; +.sectionHeader h3 { + font-size: 1.125rem; + font-weight: 600; + color: var(--text-dark); + margin: 0; +} + +.overallScore { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.scoreText { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-dark); } .scoreLabel { - font-size: 0.75rem; - color: var(--text-secondary); - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; + font-size: 0.875rem; + color: var(--text-muted); } -.mainContent { - padding-bottom: 4rem; +.progressSection { + margin-top: 1rem; +} + +.progressBar { + width: 100%; + height: 2rem; + background-color: #e0e0e0; + border-radius: 0; + overflow: hidden; + margin-bottom: 0.5rem; +} + +.progressFill { + height: 100%; + background-color: #000000; + transition: width 0.3s ease; +} + +.scoreDetails { + font-size: 0.875rem; + color: var(--text-muted); } /* ============================================================================ - LOADING STATES + SECTION TITLES ============================================================================ */ -.loadingContainer { - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +.sectionTitle { + font-size: 1.125rem; + font-weight: 600; + color: var(--text-dark); + margin: 0 0 1rem 0; } -.loadingSpinner { - text-align: center; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(20px); - padding: 3rem; - border-radius: var(--border-radius-lg); - box-shadow: var(--shadow-xl); - max-width: 400px; - width: 90%; -} +/* ============================================================================ + CATEGORY SECTION & PERFORMANCE TABLE + ============================================================================ */ -.loadingIcon { - font-size: 4rem; - color: var(--primary-color); +.categorySection { + background-color: var(--bg-white); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: 1.5rem; margin-bottom: 1.5rem; - animation: pulse 2s infinite; } -.loadingSpinner h3 { - color: var(--text-primary); - margin-bottom: 2rem; +.tableContainer { + overflow-x: auto; + border-radius: var(--border-radius); + border: 1px solid var(--table-border); +} + +.performanceTable { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; + background-color: var(--bg-white); +} + +.performanceTable th { + background-color: #f8f9fa; + color: var(--text-dark); font-weight: 600; + padding: 1rem 0.75rem; + text-align: left; + border-bottom: 2px solid var(--table-border); + font-size: 0.875rem; } -.loadingBar { +.performanceTable td { + padding: 1rem 0.75rem; + border-bottom: 1px solid var(--table-border); + vertical-align: middle; + font-size: 0.875rem; +} + +.assignmentTable { width: 100%; - height: 8px; - background: var(--bg-accent); - border-radius: 4px; + border-collapse: collapse; + font-size: 0.875rem; + background-color: var(--bg-white); + table-layout: fixed; +} + +.assignmentTable th { + background-color: #f8f9fa; + color: var(--text-dark); + font-weight: 600; + padding: 1.25rem 0.75rem; + text-align: left; + border-bottom: 2px solid var(--table-border); + font-size: 0.875rem; +} + +.assignmentTable th:nth-child(1) { width: 35%; } /* Assignment Name */ +.assignmentTable th:nth-child(2) { width: 12%; text-align: center; } /* Weightage */ +.assignmentTable th:nth-child(3) { width: 15%; text-align: center; } /* Your Marks */ +.assignmentTable th:nth-child(4) { width: 12%; text-align: center; } /* Percentage */ +.assignmentTable th:nth-child(5) { width: 15%; text-align: center; } /* Status */ +.assignmentTable th:nth-child(6) { width: 11%; text-align: center; } /* Feedback */ + +.assignmentTable td { + padding: 1.25rem 0.75rem; + border-bottom: 1px solid var(--table-border); + vertical-align: middle; + font-size: 0.875rem; +} + +.assignmentTable td:nth-child(2), +.assignmentTable td:nth-child(3), +.assignmentTable td:nth-child(4), +.assignmentTable td:nth-child(5), +.assignmentTable td:nth-child(6) { + text-align: center; +} + +.categoryName { + font-weight: 500; + color: var(--text-dark); +} + +.performanceBar { + width: 120px; + height: 0.75rem; + background-color: #e0e0e0; + border-radius: 0.375rem; overflow: hidden; position: relative; } -.loadingProgress { +.performanceFill { height: 100%; - background: linear-gradient(90deg, var(--primary-color), var(--info-color)); - border-radius: 4px; - animation: loading 2s infinite; + border-radius: 0.375rem; + transition: width 0.3s ease; } -@keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } +.performanceFill.excellent { + background-color: #28a745; } -@keyframes loading { - 0% { width: 0%; } - 50% { width: 70%; } - 100% { width: 100%; } +.performanceFill.good { + background-color: #28a745; +} + +.performanceFill.fair { + background-color: #ffc107; +} + +.performanceFill.poor { + background-color: #dc3545; } /* ============================================================================ - SECTION STYLING + PERFORMANCE INSIGHTS SECTION ============================================================================ */ -.sectionTitle { - font-size: 1.5rem; - font-weight: 700; - color: var(--text-primary); +.insightsSection { margin-bottom: 1.5rem; - display: flex; - align-items: center; - gap: 0.5rem; } -.sectionTitle svg { - color: var(--primary-color); +.insightsBox { + background-color: #e3f2fd; + border: 1px solid #90caf9; + border-radius: var(--border-radius); + padding: 1.5rem; } -.filterSection { - background: var(--bg-primary); - padding: 1.5rem; - border-radius: var(--border-radius); - box-shadow: var(--shadow-sm); - margin-bottom: 2rem; - border: 1px solid var(--border-color); +.insightsBox h4 { + font-size: 1rem; + font-weight: 600; + color: var(--text-dark); + margin: 0 0 0.75rem 0; +} + +.insightsBox p { + font-size: 0.875rem; + color: var(--text-dark); + line-height: 1.5; + margin: 0 0 1rem 0; } -.categoryFilter { +.actionButtons { display: flex; gap: 0.75rem; flex-wrap: wrap; - margin-top: 1rem; } -.filterButton { - padding: 0.75rem 1.5rem; - border: 2px solid var(--border-color); - background: var(--bg-primary); - color: var(--text-secondary); - border-radius: 50px; - font-weight: 500; +.actionButton { + background-color: var(--primary-color); + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: var(--border-radius); font-size: 0.875rem; cursor: pointer; - transition: var(--transition); - white-space: nowrap; + transition: background-color 0.2s ease; +} + +.actionButton:hover { + background-color: #0056b3; +} + +/* ============================================================================ + ASSIGNMENT SECTION & TABLE + ============================================================================ */ + +.assignmentSection { + background-color: var(--bg-white); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: 1.5rem; + margin-bottom: 1.5rem; +} + +.assignmentInfo { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.assignmentName { + font-weight: 500; + color: var(--text-dark); + font-size: 0.9rem; + line-height: 1.3; +} + +.assignmentDate { + font-size: 0.75rem; + color: var(--text-muted); +} + +.statusBadge { + display: inline-block; + padding: 0.375rem 0.75rem; + border-radius: 1rem; + font-size: 0.75rem; + font-weight: 500; + text-align: center; + min-width: 80px; +} + +.statusBadge.onTime { + background-color: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; } -.filterButton:hover { - border-color: #4f46e5; - color: #000000; - background: rgba(255, 255, 255, 0.9); - transform: translateY(-2px); +.statusBadge.late { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; +} + +.feedbackButton { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + color: #6c757d; + padding: 0.375rem 0.75rem; + border-radius: 1rem; + font-size: 0.75rem; + cursor: pointer; + display: flex; + align-items: center; + gap: 0.375rem; + transition: all 0.2s ease; + min-width: 100px; + justify-content: center; +} + +.feedbackButton:hover { + background-color: #e9ecef; + color: #495057; + border-color: #adb5bd; +} + +.buttonIcon { + font-size: 0.875rem; +} + +/* Performance percentage colors */ +.excellent { + color: var(--success-color); font-weight: 600; } -.filterButton.active { - background: linear-gradient(135deg, var(--primary-color), var(--info-color)); - border-color: transparent; - color: white; - box-shadow: var(--shadow-md); +.good { + color: var(--warning-color); + font-weight: 600; +} + +.fair { + color: #fd7e14; + font-weight: 600; } -.filterButton.active:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); +.poor { + color: var(--danger-color); + font-weight: 600; } /* ============================================================================ - ALERT STYLING + SUMMARY CARDS ============================================================================ */ -.noDataAlert { - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(20px); - border: 1px solid rgba(245, 158, 11, 0.3); +.summaryCards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin-bottom: 2rem; +} + +.summaryCard { + background-color: var(--bg-white); + border: 1px solid var(--border-color); border-radius: var(--border-radius); - color: var(--text-primary); - font-weight: 500; - box-shadow: var(--shadow-md); + padding: 1.5rem; + text-align: center; + box-shadow: var(--box-shadow); +} + +.cardNumber { + font-size: 2rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.cardNumber.total { + color: var(--text-dark); +} + +.cardNumber.onTime { + color: #28a745; +} + +.cardNumber.late { + color: #dc3545; +} + +.cardNumber.average { + color: #007bff; +} + +.cardLabel { + font-size: 0.875rem; + color: var(--text-muted); + margin: 0; +} + +/* ============================================================================ + LOADING STATES + ============================================================================ */ + +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem; + text-align: center; +} + +.loadingSpinner { + width: 40px; + height: 40px; + border: 4px solid var(--bg-light); + border-top: 4px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; +} + +.loadingIcon { + font-size: 3rem; + color: var(--primary-color); + margin-bottom: 1rem; + animation: pulse 2s infinite; +} + +.loadingSpinner h3 { + color: var(--text-dark); + margin-bottom: 1rem; +} + +.loadingBar { + width: 200px; + height: 4px; + background-color: var(--bg-light); + border-radius: 2px; + overflow: hidden; +} + +.loadingProgress { + width: 30%; + height: 100%; + background-color: var(--primary-color); + animation: progress 2s ease-in-out infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +@keyframes progress { + 0% { transform: translateX(-100%); } + 50% { transform: translateX(0%); } + 100% { transform: translateX(100%); } +} + +/* ============================================================================ + NO DATA STATES + ============================================================================ */ + +.noDataAlert { + text-align: center; + margin: 2rem 0; } /* ============================================================================ @@ -277,77 +599,50 @@ @media (max-width: 768px) { .headerContent { flex-direction: column; - text-align: center; + align-items: flex-start; + gap: 1rem; } - .pageTitle { - font-size: 1.75rem; + .headerRight { + align-self: flex-end; } - .scoreCircle { - width: 100px; - height: 100px; + .sectionHeader { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; } - .scoreNumber { - font-size: 1.5rem; + .overallScore { + align-self: flex-end; + } + + .actionButtons { + flex-direction: column; } - .categoryFilter { - justify-content: center; + .actionButton { + width: 100%; + text-align: center; } - .filterButton { - font-size: 0.8rem; - padding: 0.625rem 1.25rem; + .summaryCards { + grid-template-columns: repeat(2, 1fr); } } @media (max-width: 480px) { - .headerSection { - padding: 1.5rem 0; - } - - .pageTitle { - font-size: 1.5rem; + .summaryCards { + grid-template-columns: 1fr; } - .loadingSpinner { - padding: 2rem; + .mainContent { + padding: 0 0.5rem; } - .loadingIcon { - font-size: 3rem; - } -} - -/* ============================================================================ - ACCESSIBILITY - ============================================================================ */ - -@media (prefers-reduced-motion: reduce) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} - -/* Focus styles for accessibility */ -.filterButton:focus-visible { - outline: 2px solid var(--primary-color); - outline-offset: 2px; -} - -/* High contrast mode support */ -@media (prefers-contrast: high) { - :root { - --primary-color: #0066cc; - --success-color: #008800; - --warning-color: #cc6600; - --error-color: #cc0000; - --text-primary: #000000; - --text-secondary: #333333; - --border-color: #666666; + .overallSection, + .categorySection, + .assignmentSection { + padding: 1rem; } } \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx b/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx index 5c1165a189..43bc8f64c2 100644 --- a/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx +++ b/src/components/EductionPortal/EvaluationResults/TaskDetailsList.jsx @@ -15,7 +15,7 @@ import { faDownload, faEye, } from '@fortawesome/free-solid-svg-icons'; -import { getStatusInfo } from './mockData'; +import { getStatusInfo } from './mockData_new'; import styles from './TaskDetailsList.module.css'; const TaskDetailsList = ({ tasks, selectedCategory = 'all', categories, isLoading }) => { diff --git a/src/components/EductionPortal/EvaluationResults/mockData.js b/src/components/EductionPortal/EvaluationResults/mockData.js deleted file mode 100644 index 72b7f2b929..0000000000 --- a/src/components/EductionPortal/EvaluationResults/mockData.js +++ /dev/null @@ -1,437 +0,0 @@ -// ============================================================================ -// MOCK EVALUATION DATA - Realistic Academic Performance Data -// ============================================================================ - -export const mockEvaluationData = { - student: { - id: 'STU-2024-001', - name: 'Alex Johnson', - email: 'alex.johnson@school.edu', - class: 'Computer Science - Year 3', - semester: 'Fall 2024', - lastUpdated: '2024-09-25T10:30:00Z', - profileImage: '/api/placeholder/60/60', - }, - - // Overall performance score (calculated from all categories) - overallScore: 87.3, - - // Performance categories with detailed breakdown - categories: [ - { - id: 'assignments', - name: 'Assignments', - weightage: 40, - totalItems: 12, - completedItems: 11, - totalMarks: 1200, - earnedMarks: 1050, - percentage: 87.5, - performanceLevel: 'excellent', // excellent, good, fair, poor - color: '#10b981', - icon: 'faClipboardCheck', - description: 'Programming assignments, essays, and individual homework tasks', - dueDate: '2024-10-14T12:00:00', - submissions: { - onTime: 9, - late: 2, - missing: 1, - }, - }, - { - id: 'quizzes', - name: 'Quizzes', - weightage: 25, - totalItems: 8, - completedItems: 8, - totalMarks: 800, - earnedMarks: 720, - percentage: 90.0, - performanceLevel: 'excellent', - color: '#3b82f6', - icon: 'faQuestion', - description: 'Short weekly quizzes and knowledge check assessments', - dueDate: '2024-09-29T12:00:00', - submissions: { - onTime: 7, - late: 1, - missing: 0, - }, - }, - { - id: 'exams', - name: 'Exams', - weightage: 25, - totalItems: 3, - completedItems: 2, - totalMarks: 300, - earnedMarks: 245, - percentage: 81.7, - performanceLevel: 'good', - color: '#f59e0b', - icon: 'faGraduationCap', - description: 'Comprehensive midterm and final examinations', - dueDate: '2024-12-09T12:00:00', - submissions: { - onTime: 2, - late: 0, - missing: 1, - }, - }, - { - id: 'projects', - name: 'Projects', - weightage: 10, - totalItems: 2, - completedItems: 2, - totalMarks: 200, - earnedMarks: 185, - percentage: 92.5, - performanceLevel: 'excellent', - color: '#8b5cf6', - icon: 'faProjectDiagram', - description: 'Collaborative group projects and individual capstone work', - dueDate: '2024-11-19T12:00:00', - submissions: { - onTime: 1, - late: 1, - missing: 0, - }, - }, - ], - - // Detailed task/assignment list - tasks: [ - // Assignments - { - id: 'task-001', - name: 'Data Structures Implementation', - category: 'assignments', - type: 'Programming Assignment', - weightage: 8, - totalMarks: 100, - earnedMarks: 95, - percentage: 95, - status: 'On Time', - statusColor: '#10b981', - submittedDate: '2024-09-10T14:30:00Z', - dueDate: '2024-09-29T23:59:00Z', - teacherFeedback: - 'Excellent implementation! Clean code structure and efficient algorithms. Minor improvement needed in edge case handling.', - rubricScores: { - 'Code Quality': { earned: 18, total: 20 }, - Functionality: { earned: 20, total: 20 }, - Documentation: { earned: 17, total: 20 }, - Testing: { earned: 16, total: 20 }, - Performance: { earned: 19, total: 20 }, - }, - attachments: ['solution.py', 'test_cases.py', 'documentation.pdf'], - }, - { - id: 'task-002', - name: 'Algorithm Analysis Report', - category: 'assignments', - type: 'Research Assignment', - weightage: 6, - totalMarks: 100, - earnedMarks: 88, - percentage: 88, - status: 'On Time', - statusColor: '#10b981', - submittedDate: '2024-09-20T16:45:00Z', - dueDate: '2024-09-22T23:59:00Z', - teacherFeedback: - 'Good analysis of time complexity. Your comparison of different sorting algorithms was thorough. Consider adding more real-world examples.', - rubricScores: { - 'Research Quality': { earned: 17, total: 20 }, - 'Analysis Depth': { earned: 18, total: 20 }, - 'Writing Quality': { earned: 16, total: 20 }, - Citations: { earned: 19, total: 20 }, - Presentation: { earned: 18, total: 20 }, - }, - }, - { - id: 'task-003', - name: 'Database Design Assignment', - category: 'assignments', - type: 'Design Assignment', - weightage: 7, - totalMarks: 100, - earnedMarks: 78, - percentage: 78, - status: 'Late', - statusColor: '#f59e0b', - submittedDate: '2024-09-28T10:15:00Z', - dueDate: '2024-09-25T23:59:00Z', - teacherFeedback: - 'Submitted 3 days late (-10 points). Good ER diagram design, but normalization could be improved. Review 3NF principles.', - rubricScores: { - 'ER Diagram': { earned: 18, total: 20 }, - Normalization: { earned: 14, total: 20 }, - 'SQL Queries': { earned: 17, total: 20 }, - Documentation: { earned: 15, total: 20 }, - Timeliness: { earned: 10, total: 20 }, - }, - }, - - // Quizzes - { - id: 'quiz-001', - name: 'Arrays and Linked Lists Quiz', - category: 'quizzes', - type: 'Online Quiz', - weightage: 3, - totalMarks: 100, - earnedMarks: 92, - percentage: 92, - status: 'On Time', - statusColor: '#10b981', - submittedDate: '2024-09-15T11:30:00Z', - dueDate: '2024-09-15T12:00:00Z', - teacherFeedback: - 'Excellent understanding of data structures. Only minor mistake in time complexity analysis.', - timeSpent: '25 minutes', - attempts: 1, - maxAttempts: 1, - }, - { - id: 'quiz-002', - name: 'Sorting Algorithms Quiz', - category: 'quizzes', - type: 'Online Quiz', - weightage: 3, - totalMarks: 100, - earnedMarks: 85, - percentage: 85, - status: 'On Time', - statusColor: '#10b981', - submittedDate: '2024-09-22T14:45:00Z', - dueDate: '2024-09-22T15:00:00Z', - teacherFeedback: 'Good performance. Review merge sort implementation details.', - timeSpent: '28 minutes', - attempts: 1, - maxAttempts: 1, - }, - - // Exams - { - id: 'exam-001', - name: 'Midterm Examination', - category: 'exams', - type: 'Written Exam', - weightage: 15, - totalMarks: 150, - earnedMarks: 128, - percentage: 85.3, - status: 'On Time', - statusColor: '#10b981', - submittedDate: '2024-10-14T12:00:00Z', - dueDate: '2024-10-14T12:00:00Z', - teacherFeedback: - 'Strong performance across all topics. Excellent problem-solving in dynamic programming section. Minor errors in graph algorithms.', - sections: { - 'Data Structures': { earned: 28, total: 30 }, - Algorithms: { earned: 25, total: 30 }, - 'Dynamic Programming': { earned: 30, total: 30 }, - 'Graph Theory': { earned: 22, total: 30 }, - 'Problem Solving': { earned: 23, total: 30 }, - }, - }, - { - id: 'exam-002', - name: 'Final Examination', - category: 'exams', - type: 'Comprehensive Exam', - weightage: 10, - totalMarks: 150, - earnedMarks: 0, - percentage: 0, - status: 'Missing', - statusColor: '#ef4444', - submittedDate: null, - dueDate: '2024-12-10T12:00:00Z', - teacherFeedback: 'Exam not yet taken. Scheduled for December 10th.', - }, - - // Projects - { - id: 'project-001', - name: 'Social Media Analytics Platform', - category: 'projects', - type: 'Group Project', - weightage: 6, - totalMarks: 100, - earnedMarks: 94, - percentage: 94, - status: 'On Time', - statusColor: '#10b981', - submittedDate: '2024-11-01T18:30:00Z', - dueDate: '2024-11-02T23:59:00Z', - teacherFeedback: - 'Outstanding project! Excellent use of modern technologies, clean architecture, and comprehensive testing. Great teamwork evident.', - teamMembers: ['Alex Johnson', 'Sarah Chen', 'Michael Rodriguez'], - technologies: ['React', 'Node.js', 'MongoDB', 'Python', 'Machine Learning'], - deliverables: ['Source Code', 'Documentation', 'Presentation', 'Demo Video'], - }, - { - id: 'project-002', - name: 'Algorithm Visualization Tool', - category: 'projects', - type: 'Individual Project', - weightage: 4, - totalMarks: 100, - earnedMarks: 91, - percentage: 91, - status: 'Late', - statusColor: '#f59e0b', - submittedDate: '2024-11-22T14:20:00Z', - dueDate: '2024-11-20T23:59:00Z', - teacherFeedback: - 'Great interactive visualizations! Submitted 2 days late (-5 points). Consider adding more algorithms and improving UI responsiveness.', - technologies: ['JavaScript', 'D3.js', 'HTML5 Canvas', 'CSS3'], - features: [ - 'Sorting Visualizations', - 'Graph Algorithms', - 'Interactive Controls', - 'Step-by-step Execution', - ], - }, - ], - - // Summary statistics - summary: { - totalAssignments: 12, - completedAssignments: 11, - onTimeSubmissions: 19, - lateSubmissions: 4, - missingSubmissions: 2, - averageScore: 87.3, - highestScore: 95, - lowestScore: 78, - improvementTrend: '+5.2%', // compared to previous semester - timeManagement: { - excellent: 19, - good: 4, - needsImprovement: 2, - }, - strengths: [ - 'Excellent programming skills', - 'Strong analytical thinking', - 'Good documentation practices', - 'Consistent performance', - ], - areasForImprovement: [ - 'Time management for large assignments', - 'Edge case handling in code', - 'Exam preparation strategies', - ], - }, - - // Teacher's general feedback - teacherFeedback: { - overall: - 'Alex demonstrates exceptional understanding of computer science concepts and consistently produces high-quality work. Strong programming abilities and analytical skills are evident throughout the semester.', - strengths: [ - 'Outstanding problem-solving capabilities', - 'Clean and well-documented code', - 'Excellent participation in class discussions', - 'Strong collaboration skills in group projects', - 'Consistent effort and improvement mindset', - ], - recommendations: [ - 'Focus on time management to avoid late submissions', - 'Practice more complex algorithm implementations', - 'Prepare more thoroughly for comprehensive exams', - 'Consider tutoring other students to reinforce learning', - ], - nextSteps: [ - 'Enroll in Advanced Algorithms course', - 'Consider internship opportunities', - 'Join competitive programming club', - 'Start working on senior capstone project ideas', - ], - overallRating: 'Excellent', - teacherName: 'Dr. Emily Rodriguez', - teacherTitle: 'Professor of Computer Science', - lastUpdated: '2024-09-25T15:45:00Z', - }, - - // Performance trends over time - trends: { - monthly: [ - { month: 'August', score: 82.5, assignments: 3, rank: 15 }, - { month: 'September', score: 87.8, assignments: 5, rank: 12 }, - { month: 'October', score: 89.2, assignments: 4, rank: 8 }, - ], - categoryTrends: { - assignments: [85, 87, 89], - quizzes: [88, 90, 92], - exams: [82, 85, 0], // 0 for not yet taken - projects: [90, 94, 91], - }, - }, - - // Notifications about new results - notifications: [ - { - id: 'notif-001', - type: 'new_grade', - title: 'New Grade Posted', - message: 'Your grade for "Algorithm Visualization Tool" has been posted.', - date: '2024-09-25T09:15:00Z', - read: false, - priority: 'medium', - }, - { - id: 'notif-002', - type: 'feedback_available', - title: 'Teacher Feedback Available', - message: 'Dr. Rodriguez has provided feedback on your midterm exam.', - date: '2024-09-24T14:30:00Z', - read: false, - priority: 'high', - }, - ], - - // Performance analytics - analytics: { - classRank: 8, - totalStudents: 45, - percentile: 82, - gpa: 3.8, - creditHours: 15, - attendanceRate: 95, - participationScore: 92, - }, -}; - -// Helper functions for data processing -export const getPerformanceLevel = percentage => { - if (percentage >= 90) return { level: 'excellent', color: '#10b981', label: 'Excellent' }; - if (percentage >= 80) return { level: 'good', color: '#3b82f6', label: 'Good' }; - if (percentage >= 70) return { level: 'fair', color: '#f59e0b', label: 'Fair' }; - return { level: 'poor', color: '#ef4444', label: 'Needs Improvement' }; -}; - -export const getStatusInfo = status => { - switch (status.toLowerCase()) { - case 'on time': - return { color: '#10b981', icon: 'faCheckCircle', label: 'On Time' }; - case 'late': - return { color: '#f59e0b', icon: 'faClock', label: 'Late Submission' }; - case 'missing': - return { color: '#ef4444', icon: 'faTimesCircle', label: 'Missing' }; - default: - return { color: '#6b7280', icon: 'faQuestion', label: 'Unknown' }; - } -}; - -export const calculateCategoryProgress = category => { - const completionRate = (category.completedItems / category.totalItems) * 100; - const scoreRate = category.percentage; - return { - completionRate: Math.round(completionRate), - scoreRate: Math.round(scoreRate * 10) / 10, - isComplete: category.completedItems === category.totalItems, - }; -}; diff --git a/src/components/EductionPortal/EvaluationResults/mockData_new.js b/src/components/EductionPortal/EvaluationResults/mockData_new.js new file mode 100644 index 0000000000..f97cc9ac1e --- /dev/null +++ b/src/components/EductionPortal/EvaluationResults/mockData_new.js @@ -0,0 +1,252 @@ +// ============================================================================ +// MOCK EVALUATION DATA - Clean Academic Performance Data for New Design +// ============================================================================ + +export const mockEvaluationData = { + student: { + id: 'STU-2024-001', + name: 'Alex Johnson', + email: 'alex.johnson@school.edu', + class: 'Computer Science - Year 3', + semester: 'Fall 2024', + lastUpdated: '2024-09-25T10:30:00Z', + profileImage: '/api/placeholder/60/60', + }, + + // Overall performance score (calculated from all categories) + overallScore: 77.0, + + // Performance categories with detailed breakdown + categories: [ + { + id: 'assignments', + name: 'Assignments', + weightage: 40, + totalItems: 5, + completedItems: 5, + totalMarks: 40, + earnedMarks: 32, + percentage: 80.0, + performanceLevel: 'good', + color: '#28a745', + icon: 'faClipboardCheck', + description: 'Programming assignments, essays, and individual homework tasks', + dueDate: '2024-10-14T12:00:00', + submissions: { + onTime: 4, + late: 1, + missing: 0, + }, + }, + { + id: 'exams', + name: 'Exams', + weightage: 35, + totalItems: 2, + completedItems: 2, + totalMarks: 40, + earnedMarks: 32, + percentage: 80.0, + performanceLevel: 'good', + color: '#28a745', + icon: 'faGraduationCap', + description: 'Comprehensive midterm and final examinations', + dueDate: '2024-12-09T12:00:00', + submissions: { + onTime: 2, + late: 0, + missing: 0, + }, + }, + { + id: 'quizzes', + name: 'Quizzes', + weightage: 15, + totalItems: 8, + completedItems: 8, + totalMarks: 40, + earnedMarks: 24, + percentage: 60.0, + performanceLevel: 'fair', + color: '#ffc107', + icon: 'faQuestion', + description: 'Short weekly quizzes and knowledge check assessments', + dueDate: '2024-09-29T12:00:00', + submissions: { + onTime: 6, + late: 2, + missing: 0, + }, + }, + { + id: 'participation', + name: 'Participation', + weightage: 10, + totalItems: 1, + completedItems: 1, + totalMarks: 40, + earnedMarks: 32, + percentage: 80.0, + performanceLevel: 'good', + color: '#28a745', + icon: 'faUsers', + description: 'Class participation and engagement activities', + dueDate: '2024-11-19T12:00:00', + submissions: { + onTime: 1, + late: 0, + missing: 0, + }, + }, + ], + + // Detailed task/assignment list + tasks: [ + { + id: 'task-001', + name: 'Assignment 1: Essay on Climate Change', + category: 'assignments', + type: 'Essay Assignment', + weightage: 8, + totalMarks: 10, + earnedMarks: 8, + percentage: 80, + status: 'On time', + statusColor: '#28a745', + submissionDate: '2024-08-15T14:30:00Z', + dueDate: '2024-08-15T23:59:00Z', + teacherFeedback: 'Good analysis and structure. Consider adding more supporting evidence.', + }, + { + id: 'task-002', + name: 'Mid-Term Exam', + category: 'exams', + type: 'Examination', + weightage: 20, + totalMarks: 100, + earnedMarks: 75, + percentage: 75, + status: 'On time', + statusColor: '#28a745', + submissionDate: '2024-09-15T14:30:00Z', + dueDate: '2024-09-15T16:00:00Z', + teacherFeedback: 'Solid performance overall. Review concepts from chapter 5.', + }, + { + id: 'task-003', + name: 'Quiz 1: Basic Concepts', + category: 'quizzes', + type: 'Quiz', + weightage: 2, + totalMarks: 20, + earnedMarks: 15, + percentage: 75, + status: 'On time', + statusColor: '#28a745', + submissionDate: '2024-08-28T14:30:00Z', + dueDate: '2024-08-28T23:59:00Z', + teacherFeedback: 'Good understanding of basic concepts.', + }, + { + id: 'task-004', + name: 'Assignment 2: Research Project', + category: 'assignments', + type: 'Research Assignment', + weightage: 12, + totalMarks: 10, + earnedMarks: 6, + percentage: 60, + status: 'Late for 2 days', + statusColor: '#dc3545', + submissionDate: '2024-09-20T14:30:00Z', + dueDate: '2024-09-18T23:59:00Z', + teacherFeedback: 'Late submission. Content needs more depth and analysis.', + }, + { + id: 'task-005', + name: 'Quiz 2: Advanced Topics', + category: 'quizzes', + type: 'Quiz', + weightage: 2, + totalMarks: 15, + earnedMarks: 9, + percentage: 60, + status: 'On time', + statusColor: '#28a745', + submissionDate: '2024-09-25T14:30:00Z', + dueDate: '2024-09-25T23:59:00Z', + teacherFeedback: 'Review advanced concepts. Practice problems recommended.', + }, + { + id: 'task-006', + name: 'Final Exam', + category: 'exams', + type: 'Final Examination', + weightage: 15, + totalMarks: 100, + earnedMarks: 82, + percentage: 82, + status: 'On time', + statusColor: '#28a745', + submissionDate: '2024-10-15T14:30:00Z', + dueDate: '2024-10-15T16:00:00Z', + teacherFeedback: 'Excellent improvement from mid-term. Well prepared.', + }, + ], + + // Summary statistics + summary: { + totalAssignments: 6, + completedAssignments: 6, + onTimeSubmissions: 5, + lateSubmissions: 1, + missingSubmissions: 0, + averageScore: 72, + improvementTrend: '+5%', + lastAssignmentDate: '2024-10-15T16:00:00Z', + }, + + // Teacher feedback (kept for compatibility) + teacherFeedback: { + teacherName: 'Dr. Emily Rodriguez', + teacherTitle: 'Professor of Computer Science', + overallRating: 'Good', + lastUpdated: '2024-10-25T10:30:00Z', + overall: + 'You performed strongly in Assignments (80%), Exams (80%), and Participation (80%). You may improve your performance in Quizzes (60%) - consider reviewing quiz preparation strategies.', + strengths: [ + 'Strong analytical thinking and problem-solving skills', + 'Excellent written communication in assignments', + 'Consistent attendance and participation', + ], + improvements: [ + 'Review quiz preparation strategies', + 'Practice time management during timed assessments', + 'Strengthen foundational concepts', + ], + }, +}; + +// Helper functions for performance calculations +export const getPerformanceLevel = score => { + if (score >= 90) return { level: 'excellent', label: 'Excellent', color: 'success' }; + if (score >= 80) return { level: 'good', label: 'Good', color: 'primary' }; + if (score >= 70) return { level: 'fair', label: 'Fair', color: 'warning' }; + return { level: 'poor', label: 'Needs Improvement', color: 'danger' }; +}; + +export const calculateCategoryProgress = category => { + const completionRate = Math.round((category.completedItems / category.totalItems) * 100); + const isComplete = category.completedItems === category.totalItems; + return { completionRate, isComplete }; +}; + +export const getStatusInfo = status => { + if (status === 'On time' || status === 'completed') { + return { color: 'success', icon: 'check-circle' }; + } + if (status?.toLowerCase().includes('late')) { + return { color: 'danger', icon: 'exclamation-triangle' }; + } + return { color: 'secondary', icon: 'clock' }; +}; From cf3b010e4756ee6a8572d81d13d4fd62cf893a87 Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Fri, 14 Nov 2025 22:46:33 -0600 Subject: [PATCH 4/9] Fix: Implemented Feedback and Mobile responsiveness --- .../EvaluationResults/EvaluationResults.jsx | 215 +++++- .../EvaluationResults.module.css | 724 +++++++++++++++++- .../EvaluationResults/mockData_new.js | 8 +- .../PermissionsManagement.css | 72 +- 4 files changed, 1009 insertions(+), 10 deletions(-) diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx index 4e15c0a9cc..adfc68e1d7 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; -import { Container, Alert } from 'reactstrap'; +import { Container, Alert, Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUser, @@ -8,6 +8,10 @@ import { faEye, faGraduationCap, faExclamationTriangle, + faTimes, + faCalendarAlt, + faPercent, + faAward, } from '@fortawesome/free-solid-svg-icons'; import styles from './EvaluationResults.module.css'; @@ -18,6 +22,10 @@ const EvaluationResults = ({ auth }) => { const [evaluationData, setEvaluationData] = useState(null); const [loading, setLoading] = useState(true); const [selectedCategory, setSelectedCategory] = useState('all'); + const [feedbackModal, setFeedbackModal] = useState({ + isOpen: false, + task: null, + }); useEffect(() => { // Load evaluation data immediately since we're using mock data @@ -128,6 +136,15 @@ const EvaluationResults = ({ auth }) => { return styles.poor; }; + // Feedback modal functions + const openFeedbackModal = task => { + setFeedbackModal({ isOpen: true, task }); + }; + + const closeFeedbackModal = () => { + setFeedbackModal({ isOpen: false, task: null }); + }; + return (
{/* New Clean Header */} @@ -276,7 +293,10 @@ const EvaluationResults = ({ auth }) => { - @@ -286,6 +306,100 @@ const EvaluationResults = ({ auth }) => {
+ + {/* Mobile Cards (shown on small screens) */} +
+ {tasks.slice(0, 6).map(task => ( +
+
+
{task.name}
+ + {task.status || 'On time'} + +
+
+
+ Weightage: {task.weightage || '8'}% +
+
+ Score: {task.earnedMarks}/{task.totalMarks} + + {' '} + ({Math.round(task.percentage)}%) + +
+
+ Submitted: {new Date(task.submissionDate).toLocaleDateString()} +
+
+
+ +
+
+ ))} +
+
+ + {/* Teacher Feedback Section */} +
+
+

+ + Teacher Feedback - Structured feedback display with strengths and recommendations +

+
+ + {evaluationData.teacherFeedback.teacherName} + + + {evaluationData.teacherFeedback.teacherTitle} + +
+
+ +
+ {/* Overall Feedback */} +
+

Overall Assessment

+

{evaluationData.teacherFeedback.overall}

+
+ + {/* Strengths and Improvements */} +
+
+

+ + Strengths +

+
    + {evaluationData.teacherFeedback.strengths.map((strength, index) => ( +
  • + + {strength} +
  • + ))} +
+
+ +
+

+ + Areas for Improvement +

+
    + {evaluationData.teacherFeedback.improvements.map((improvement, index) => ( +
  • + + {improvement} +
  • + ))} +
+
+
+
{/* Summary Cards */} @@ -308,6 +422,103 @@ const EvaluationResults = ({ auth }) => {
+ + {/* Feedback Detail Modal */} + + + + Assignment Feedback Details + + + {feedbackModal.task && ( +
+ {/* Assignment Header */} +
+

{feedbackModal.task.name}

+
+ + + {feedbackModal.task.type} + + + + Due: {new Date(feedbackModal.task.dueDate).toLocaleDateString()} + +
+
+ + {/* Performance Summary */} +
+
+
Your Score
+
+ {feedbackModal.task.earnedMarks}/{feedbackModal.task.totalMarks} + ({feedbackModal.task.percentage}%) +
+
+
+
Weightage
+
+ + {feedbackModal.task.weightage}% +
+
+
+
Status
+
+ {feedbackModal.task.status} +
+
+
+ + {/* Teacher Feedback */} +
+
+ + Teacher Feedback +
+
{feedbackModal.task.teacherFeedback}
+
+ + {/* Additional Details */} +
+
+ Submission Date: + + {new Date(feedbackModal.task.submissionDate).toLocaleDateString()} at{' '} + {new Date(feedbackModal.task.submissionDate).toLocaleTimeString()} + +
+
+ Grade Category: + {feedbackModal.task.category} +
+
+
+ )} +
+ + + +
); }; diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css index c25fd035f9..2c40882fcf 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css @@ -382,6 +382,45 @@ margin-bottom: 1.5rem; } +/* Mobile assignment cards (hidden by default; enabled on small screens) */ +.mobileAssignments { + display: none; +} + +.assignmentCard { + background-color: var(--bg-white); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: 1rem; + margin-bottom: 0.75rem; + box-shadow: var(--box-shadow); +} + +.cardHeader { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 0.75rem; + margin-bottom: 0.5rem; +} + +.cardMeta { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.5rem 0.75rem; + margin-bottom: 0.75rem; +} + +.cardMetaItem { + font-size: 0.85rem; + color: var(--text-dark); +} + +.cardActions { + display: flex; + justify-content: flex-end; +} + .assignmentInfo { display: flex; flex-direction: column; @@ -393,6 +432,8 @@ color: var(--text-dark); font-size: 0.9rem; line-height: 1.3; + word-break: break-word; + overflow-wrap: anywhere; } .assignmentDate { @@ -604,7 +645,19 @@ } .headerRight { - align-self: flex-end; + position: static; + align-self: flex-start; + width: 100%; + justify-content: space-between; + } + + .pageTitle { + font-size: 1.75rem; + } + + .pageSubtitle { + font-size: 0.85rem; + max-width: 100%; } .sectionHeader { @@ -614,26 +667,146 @@ } .overallScore { - align-self: flex-end; + align-self: flex-start; + width: 100%; } .actionButtons { flex-direction: column; + width: 100%; } .actionButton { width: 100%; text-align: center; + padding: 0.75rem 1rem; + margin-bottom: 0.5rem; } .summaryCards { grid-template-columns: repeat(2, 1fr); + gap: 1rem; + } + + /* Make tables responsive by allowing horizontal scroll */ + .tableContainer { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + margin: 0 -1rem; + padding: 0 1rem; + } + + /* Keep headers visible when scrolling horizontally */ + .assignmentTable thead th { + position: sticky; + top: 0; + background: #f8f9fa; + z-index: 1; + } + + .categoryTable, + .assignmentTable { + min-width: 600px; + font-size: 0.9rem; + } + + .categoryTable th, + .categoryTable td, + .assignmentTable th, + .assignmentTable td { + padding: 0.75rem 0.5rem; + white-space: nowrap; + } + + /* Teacher Feedback responsive */ + .feedbackColumns { + grid-template-columns: 1fr; + gap: 1.5rem; + } + + .teacherFeedbackSection { + padding: 1.5rem; + } + + .teacherInfo { + align-items: flex-start; + margin-top: 1rem; + } + + /* Modal mobile styles */ + .feedbackModal .modal-dialog { + margin: 0.25rem; + max-width: calc(100% - 0.5rem); + } + + .modalBody { + padding: 1.25rem; + max-height: 65vh; + } + + .modalHeader { + padding: 1.25rem; + text-align: center; + } + + .modalHeader h4 { + font-size: 1.1rem; + line-height: 1.3; + } + + .modalFooter { + padding: 1rem; + flex-direction: column; + } + + .modalFooter button { + width: 100%; + margin: 0; + } + + .assignmentHeader, + .performanceSummary, + .teacherFeedbackDetail, + .additionalDetails { + padding: 1.25rem; + } + + .assignmentMeta { + flex-direction: column; + gap: 0.75rem; + align-items: flex-start; + } + + .performanceSummary { + grid-template-columns: repeat(2, 1fr); + gap: 0.75rem; + } + + .performanceItem { + padding: 0.75rem; + min-height: 70px; + } + + .performanceValue { + font-size: 1.1rem; + } + + .detailItem { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + padding: 0.5rem 0; } } @media (max-width: 480px) { + /* Hide table, show cards */ + .assignmentSection .tableContainer { display: none; } + .mobileAssignments { display: block; } + .summaryCards { grid-template-columns: 1fr; + gap: 0.75rem; } .mainContent { @@ -643,6 +816,553 @@ .overallSection, .categorySection, .assignmentSection { + padding: 1rem 0.75rem; + margin: 0 -0.25rem 1rem; + } + + .pageTitle { + font-size: 1.5rem; + text-align: center; + } + + .pageSubtitle { + font-size: 0.8rem; + text-align: center; + } + + .headerSection { + padding: 1.5rem 0; + } + + .userWelcome { + padding: 0.4rem 0.8rem; + font-size: 0.8rem; + } + + .notificationIcon { + font-size: 1rem; + } + + .categoryTable, + .assignmentTable { + min-width: 500px; + font-size: 0.8rem; + } + + .categoryTable th, + .categoryTable td, + .assignmentTable th, + .assignmentTable td { + padding: 0.6rem 0.4rem; + } + + .actionButton { + padding: 0.6rem 1rem; + font-size: 0.9rem; + } + + .insightsSection { + margin: 0 -0.25rem 1rem; + } + + .insightsBox { + padding: 1rem; + } + + /* Teacher Feedback mobile styles */ + .teacherFeedbackSection { + padding: 1rem; + margin: 0 -0.25rem 1rem; + } + + /* Improve table usability on very small screens */ + .tableContainer { + margin: 0 -0.5rem; + padding: 0 0.5rem; + } + + .assignmentTable { + min-width: 560px; + } + + .assignmentTable th, + .assignmentTable td { + padding: 0.75rem 0.5rem; + } + + .statusBadge { + min-width: auto; + padding: 0.25rem 0.5rem; + font-size: 0.7rem; + } + + .feedbackButton { + width: 100%; + font-size: 0.85rem; + } + + .overallFeedback, + .strengthsSection, + .improvementsSection { + padding: 1rem; + } + + .feedbackSubtitle { + font-size: 1rem; + } + + /* Modal responsive styles for small screens */ + .feedbackModal .modal-dialog { + margin: 0.125rem; + max-width: calc(100% - 0.25rem); + height: calc(100vh - 0.25rem); + } + + .feedbackModal .modal-content { + height: 100%; + display: flex; + flex-direction: column; + border-radius: 8px; + } + + .modalBody { + padding: 1rem; + max-height: none; + flex: 1; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + + .modalHeader { + padding: 1rem; + flex-shrink: 0; + } + + .modalHeader h4 { + font-size: 1rem; + margin: 0; + } + + .modalIcon { + font-size: 1rem; + margin-right: 0.5rem; + } + + .modalFooter { + padding: 0.75rem; + flex-shrink: 0; + } + + .feedbackModalContent { + gap: 1rem; + } + + .assignmentHeader, + .performanceSummary, + .teacherFeedbackDetail, + .additionalDetails { padding: 1rem; } + + .assignmentTitle { + font-size: 1.1rem; + line-height: 1.3; + } + + .performanceSummary { + grid-template-columns: 1fr; + gap: 0.5rem; + } + + .performanceItem { + padding: 0.75rem 0.5rem; + min-height: 60px; + } + + .performanceValue { + font-size: 1rem; + flex-direction: column; + gap: 0.25rem; + } + + .percentage { + font-size: 0.9rem; + } + + .detailItem { + padding: 0.4rem 0; + } + + .detailLabel, + .detailValue { + font-size: 0.9rem; + } + + .feedbackText { + font-size: 0.9rem; + padding: 0.75rem; + } +} + +/* ============================================================================ + TEACHER FEEDBACK SECTION - Structured feedback display + ============================================================================ */ + +.teacherFeedbackSection { + background: var(--bg-white); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + padding: 2rem; + margin-bottom: 1.5rem; + border: 1px solid var(--border-color); +} + +.teacherInfo { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.25rem; +} + +.teacherName { + font-weight: 600; + color: var(--primary-color); + font-size: 1rem; +} + +.teacherTitle { + font-size: 0.875rem; + color: var(--text-muted); +} + +.feedbackContent { + margin-top: 1.5rem; +} + +.overallFeedback { + margin-bottom: 2rem; + padding: 1.25rem; + background: var(--bg-light); + border-radius: var(--border-radius); + border-left: 4px solid var(--primary-color); +} + +.feedbackSubtitle { + font-size: 1.1rem; + font-weight: 600; + color: var(--text-dark); + margin-bottom: 0.75rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.feedbackIcon { + font-size: 1rem; +} + +.feedbackText { + color: var(--text-dark); + line-height: 1.6; + margin: 0; +} + +.feedbackColumns { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + margin-top: 1.5rem; +} + +.strengthsSection, +.improvementsSection { + padding: 1.25rem; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); +} + +.strengthsSection { + background: linear-gradient(135deg, #f8fffe 0%, #e8f7f5 100%); + border-left: 4px solid var(--success-color); +} + +.improvementsSection { + background: linear-gradient(135deg, #fffcf8 0%, #fef5e7 100%); + border-left: 4px solid var(--warning-color); +} + +.feedbackList { + list-style: none; + padding: 0; + margin: 0; +} + +.feedbackItem { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.5rem 0; + color: var(--text-dark); + line-height: 1.5; +} + +.checkmark { + color: var(--success-color); + font-weight: bold; + font-size: 1.1rem; + margin-top: 0.1rem; + flex-shrink: 0; +} + +.arrow { + color: var(--warning-color); + font-weight: bold; + font-size: 1.1rem; + margin-top: 0.1rem; + flex-shrink: 0; +} + +/* ============================================================================ + FEEDBACK MODAL - Detailed assignment feedback display + ============================================================================ */ + +.feedbackModal { + font-family: inherit; +} + +.feedbackModal .modal-dialog { + margin: 0.5rem; + max-width: calc(100% - 1rem); +} + +.modalHeader { + background: linear-gradient(135deg, var(--primary-color) 0%, #0056b3 100%); + color: white; + border-bottom: none; + padding: 1.5rem 2rem; + position: relative; +} + +.modalHeader .btn-close { + color: white; + opacity: 0.8; + font-size: 1.2rem; +} + +.modalHeader .btn-close:hover { + opacity: 1; +} + +.modalIcon { + margin-right: 0.75rem; + font-size: 1.2rem; +} + +.modalTitle { + font-weight: 600; + font-size: 1.25rem; +} + +.modalBody { + padding: 2rem; + background: var(--bg-light); + max-height: 70vh; + overflow-y: auto; +} + +.modalFooter { + border-top: 1px solid var(--border-color); + background: var(--bg-white); + padding: 1rem 2rem; + justify-content: center; +} + +.modalFooter button { + min-width: 120px; + padding: 0.75rem 1.5rem; + font-weight: 500; +} + +.feedbackModalContent { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +/* Assignment Header */ +.assignmentHeader { + background: var(--bg-white); + padding: 1.5rem; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + box-shadow: var(--box-shadow); +} + +.assignmentTitle { + font-size: 1.4rem; + font-weight: 600; + color: var(--text-dark); + margin-bottom: 1rem; +} + +.assignmentMeta { + display: flex; + gap: 2rem; + flex-wrap: wrap; +} + +.assignmentType, +.assignmentDate { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--text-muted); + font-size: 0.9rem; +} + +.assignmentType svg, +.assignmentDate svg { + color: var(--primary-color); +} + +/* Performance Summary */ +.performanceSummary { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 1rem; + background: var(--bg-white); + padding: 1.5rem; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + box-shadow: var(--box-shadow); +} + +.performanceItem { + text-align: center; + padding: 1rem; + border-radius: var(--border-radius); + background: var(--bg-light); + min-height: 80px; + display: flex; + flex-direction: column; + justify-content: center; + transition: transform 0.2s ease; +} + +.performanceItem:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.performanceLabel { + font-size: 0.875rem; + color: var(--text-muted); + margin-bottom: 0.5rem; + font-weight: 500; +} + +.performanceValue { + font-size: 1.25rem; + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.percentage { + font-size: 1rem; + font-weight: 400; +} + +.statusValue { + padding: 0.25rem 0.75rem; + border-radius: 20px; + font-size: 0.875rem; + font-weight: 500; +} + +/* Teacher Feedback Detail */ +.teacherFeedbackDetail { + background: var(--bg-white); + padding: 1.5rem; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + box-shadow: var(--box-shadow); +} + +.feedbackTitle { + font-size: 1.1rem; + font-weight: 600; + color: var(--text-dark); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.feedbackTitle svg { + color: var(--primary-color); +} + +.feedbackText { + color: var(--text-dark); + line-height: 1.6; + padding: 1rem; + background: var(--bg-light); + border-radius: var(--border-radius); + border-left: 4px solid var(--primary-color); +} + +/* Additional Details */ +.additionalDetails { + background: var(--bg-white); + padding: 1.5rem; + border-radius: var(--border-radius); + border: 1px solid var(--border-color); + box-shadow: var(--box-shadow); +} + +.detailItem { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 0; + border-bottom: 1px solid var(--border-color); +} + +.detailItem:last-child { + border-bottom: none; +} + +.detailLabel { + font-weight: 500; + color: var(--text-muted); +} + +.detailValue { + color: var(--text-dark); + font-weight: 400; +} + +/* ============================================================================ + MODAL RESPONSIVE BREAKPOINTS + ============================================================================ */ + +/* Large screens - limit modal width for better readability */ +@media (min-width: 1200px) { + .feedbackModal .modal-dialog { + max-width: 900px; + } +} + +/* Medium screens - optimize modal layout */ +@media (min-width: 992px) and (max-width: 1199px) { + .feedbackModal .modal-dialog { + max-width: 800px; + } + + .performanceSummary { + grid-template-columns: repeat(3, 1fr); + } } \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResults/mockData_new.js b/src/components/EductionPortal/EvaluationResults/mockData_new.js index f97cc9ac1e..cc21d6d3fd 100644 --- a/src/components/EductionPortal/EvaluationResults/mockData_new.js +++ b/src/components/EductionPortal/EvaluationResults/mockData_new.js @@ -79,8 +79,8 @@ export const mockEvaluationData = { }, }, { - id: 'participation', - name: 'Participation', + id: 'projects', + name: 'Projects', weightage: 10, totalItems: 1, completedItems: 1, @@ -90,7 +90,7 @@ export const mockEvaluationData = { performanceLevel: 'good', color: '#28a745', icon: 'faUsers', - description: 'Class participation and engagement activities', + description: 'Group projects and collaborative work assignments', dueDate: '2024-11-19T12:00:00', submissions: { onTime: 1, @@ -213,7 +213,7 @@ export const mockEvaluationData = { overallRating: 'Good', lastUpdated: '2024-10-25T10:30:00Z', overall: - 'You performed strongly in Assignments (80%), Exams (80%), and Participation (80%). You may improve your performance in Quizzes (60%) - consider reviewing quiz preparation strategies.', + 'You performed strongly in Assignments (80%), Exams (80%), and Projects (80%). You may improve your performance in Quizzes (60%) - consider reviewing quiz preparation strategies.', strengths: [ 'Strong analytical thinking and problem-solving skills', 'Excellent written communication in assignments', diff --git a/src/components/PermissionsManagement/PermissionsManagement.css b/src/components/PermissionsManagement/PermissionsManagement.css index 449d782c2b..d412fa2e22 100644 --- a/src/components/PermissionsManagement/PermissionsManagement.css +++ b/src/components/PermissionsManagement/PermissionsManagement.css @@ -1,7 +1,8 @@ .permissions-management { padding: 0.5rem 1rem; width: 40%; - min-width: 350px; + min-width: 320px; + max-width: 95vw; margin: 1.5rem auto; box-shadow: 1px 1px 8px rgb(158, 158, 158); display: flex; @@ -83,6 +84,8 @@ display: flex; justify-content: space-between; flex-direction: row-reverse; + flex-wrap: wrap; + gap: 1rem; } .buttons-container { @@ -96,7 +99,9 @@ flex-direction: column; border: solid 1px lightgrey; border-radius: 8px; - min-width: 300px; + min-width: 280px; + width: 100%; + max-width: 320px; height: 450px; max-height: 480px; overflow: auto; @@ -223,6 +228,69 @@ @media only screen and (max-width: 1110px) { .permissions-management__header { flex-direction: column; + gap: 1rem; + } +} + +@media only screen and (max-width: 768px) { + .permissions-management { + width: 95%; + margin: 1rem auto; + padding: 1rem; + min-width: auto; + } + + .permissions-management--flex { + flex-direction: column; + align-items: center; + } + + .role-name-container { + max-width: 100%; + min-width: 100%; + margin-bottom: 1rem; + } + + .buttons-container { + width: 100%; + align-items: center; + } + + .user-role-tab__permission { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + + .permission-label { + max-width: 100%; + margin-right: 0; + } +} + +@media only screen and (max-width: 480px) { + .permissions-management { + width: 98%; + padding: 0.75rem; + margin: 0.5rem auto; + } + + .permissions-management__title { + font-size: 1.5rem; + text-align: center; + } + + .user-permissions-pop-up__title { + font-size: 1.1rem; + } + + .role-name-container { + height: 300px; + max-height: 350px; + } + + .dropdown__user-perms { + max-height: 300px; } } From 8cedb1268154d4f9ce88a610b64026d5831d9147 Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Fri, 14 Nov 2025 23:47:26 -0600 Subject: [PATCH 5/9] fix: add webpack dependency to resolve ESLint error in config-overrides.js --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a25baa1c4e..536f0f6c9c 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,8 @@ "stream-browserify": "^3.0.0", "tinymce": "^7.2.0", "util": "^0.12.5", - "uuid": "^9.0.1" + "uuid": "^9.0.1", + "webpack": "^5.102.1" }, "resolutions": { "react": "18.3.1", From 7b1d2e4ed190a20b658d7ee23fafb5bafee1b67c Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Sat, 15 Nov 2025 12:51:42 -0600 Subject: [PATCH 6/9] Fix: Implemented Feedback and Mobile responsiveness-2 --- .../EvaluationResults/EvaluationResults.jsx | 4 +- .../EvaluationResults.module.css | 327 +++++++++++------- .../PermissionsManagement.css | 72 +--- 3 files changed, 207 insertions(+), 196 deletions(-) diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx index adfc68e1d7..9dd54a67a6 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -378,7 +378,7 @@ const EvaluationResults = ({ auth }) => { {evaluationData.teacherFeedback.strengths.map((strength, index) => (
  • - {strength} + {strength}
  • ))} @@ -393,7 +393,7 @@ const EvaluationResults = ({ auth }) => { {evaluationData.teacherFeedback.improvements.map((improvement, index) => (
  • - {improvement} + {improvement}
  • ))} diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css index 2c40882fcf..678dbb5a82 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css @@ -638,10 +638,20 @@ ============================================================================ */ @media (max-width: 768px) { + .evaluationResultsPage { + padding: 0.5rem; + } + + .headerSection { + padding: 1.5rem 1rem; + margin-bottom: 1rem; + } + .headerContent { flex-direction: column; align-items: flex-start; - gap: 1rem; + gap: 1.25rem; + max-width: 100%; } .headerRight { @@ -649,38 +659,63 @@ align-self: flex-start; width: 100%; justify-content: space-between; + flex-wrap: wrap; } .pageTitle { font-size: 1.75rem; + line-height: 1.2; + margin-bottom: 0.5rem; } .pageSubtitle { - font-size: 0.85rem; + font-size: 0.9rem; max-width: 100%; + line-height: 1.4; + } + + .mainContent { + padding: 0 0.75rem; + } + + .overallSection, + .categorySection, + .assignmentSection, + .insightsSection { + margin: 0 0 1.25rem 0; + padding: 1.25rem 1rem; + border-radius: 0.5rem; } .sectionHeader { flex-direction: column; align-items: flex-start; - gap: 0.75rem; + gap: 1rem; + margin-bottom: 1rem; } .overallScore { align-self: flex-start; width: 100%; + font-size: 1.5rem; + padding: 0.75rem 1rem; + text-align: center; } .actionButtons { flex-direction: column; width: 100%; + gap: 0.75rem; } .actionButton { width: 100%; text-align: center; - padding: 0.75rem 1rem; - margin-bottom: 0.5rem; + padding: 0.875rem 1rem; + margin-bottom: 0; + font-size: 0.95rem; + border-radius: 0.5rem; + min-height: 44px; /* Better touch target */ } .summaryCards { @@ -718,19 +753,62 @@ white-space: nowrap; } - /* Teacher Feedback responsive */ + .teacherFeedbackSection { + padding: 1.25rem; + margin: 0 0 1.25rem 0; + } + .feedbackColumns { - grid-template-columns: 1fr; - gap: 1.5rem; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-top: 1rem; + align-items: start; } - .teacherFeedbackSection { - padding: 1.5rem; + .overallFeedback { + padding: 1.25rem; + margin-bottom: 1.5rem; + border-radius: 0.5rem; } .teacherInfo { align-items: flex-start; + margin-bottom: 1rem; + padding: 1rem; + background: var(--bg-light); + border-radius: 0.5rem; + border: 1px solid var(--border-color); + } + + .teacherName { + font-size: 1rem; + margin-bottom: 0.25rem; + } + + .teacherTitle { + font-size: 0.875rem; + } + + .feedbackColumns { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; margin-top: 1rem; + align-items: stretch; /* Make cards same height */ + } + + .strengthsSection, + .improvementsSection { + padding: 1rem; + border-radius: 0.5rem; + margin-bottom: 0; + min-width: 0; + overflow-wrap: break-word; + word-wrap: break-word; + height: fit-content; + display: flex; + flex-direction: column; } /* Modal mobile styles */ @@ -800,13 +878,17 @@ } @media (max-width: 480px) { + .evaluationResultsPage { + padding: 0.25rem; + } + /* Hide table, show cards */ .assignmentSection .tableContainer { display: none; } .mobileAssignments { display: block; } .summaryCards { grid-template-columns: 1fr; - gap: 0.75rem; + gap: 0.875rem; } .mainContent { @@ -815,32 +897,43 @@ .overallSection, .categorySection, - .assignmentSection { - padding: 1rem 0.75rem; - margin: 0 -0.25rem 1rem; + .assignmentSection, + .insightsSection { + padding: 1rem; + margin: 0 0 1rem 0; + border-radius: 0.5rem; } .pageTitle { font-size: 1.5rem; - text-align: center; + text-align: left; + line-height: 1.2; + margin-bottom: 0.75rem; } .pageSubtitle { - font-size: 0.8rem; - text-align: center; + font-size: 0.85rem; + text-align: left; + line-height: 1.4; } .headerSection { - padding: 1.5rem 0; + padding: 1.25rem 0.75rem; } .userWelcome { - padding: 0.4rem 0.8rem; - font-size: 0.8rem; + padding: 0.5rem 0.75rem; + font-size: 0.85rem; + border-radius: 1.25rem; } .notificationIcon { font-size: 1rem; + min-height: 44px; + min-width: 44px; + display: flex; + align-items: center; + justify-content: center; } .categoryTable, @@ -857,150 +950,121 @@ } .actionButton { - padding: 0.6rem 1rem; + padding: 0.875rem 1rem; font-size: 0.9rem; + min-height: 48px; + border-radius: 0.5rem; + font-weight: 500; } - .insightsSection { - margin: 0 -0.25rem 1rem; - } - - .insightsBox { - padding: 1rem; + .feedbackButton { + width: 100%; + font-size: 0.875rem; + min-height: 44px; + padding: 0.75rem 1rem; + border-radius: 0.375rem; } - /* Teacher Feedback mobile styles */ - .teacherFeedbackSection { - padding: 1rem; - margin: 0 -0.25rem 1rem; - } - - /* Improve table usability on very small screens */ - .tableContainer { - margin: 0 -0.5rem; - padding: 0 0.5rem; - } - - .assignmentTable { - min-width: 560px; - } - - .assignmentTable th, - .assignmentTable td { - padding: 0.75rem 0.5rem; - } - .statusBadge { min-width: auto; - padding: 0.25rem 0.5rem; - font-size: 0.7rem; + padding: 0.375rem 0.75rem; + font-size: 0.75rem; + border-radius: 0.375rem; + font-weight: 500; } - - .feedbackButton { - width: 100%; + + .categoryTable, + .assignmentTable { + min-width: 500px; font-size: 0.85rem; } - .overallFeedback, - .strengthsSection, - .improvementsSection { + .categoryTable th, + .categoryTable td, + /* Teacher Feedback mobile styles */ + .teacherFeedbackSection { padding: 1rem; + margin: 0 0 1rem 0; + border-radius: 0.5rem; + width: 100%; + box-sizing: border-box; } - .feedbackSubtitle { - font-size: 1rem; - } - - /* Modal responsive styles for small screens */ - .feedbackModal .modal-dialog { - margin: 0.125rem; - max-width: calc(100% - 0.25rem); - height: calc(100vh - 0.25rem); - } - - .feedbackModal .modal-content { - height: 100%; - display: flex; - flex-direction: column; - border-radius: 8px; - } - - .modalBody { - padding: 1rem; - max-height: none; - flex: 1; - overflow-y: auto; - -webkit-overflow-scrolling: touch; + .feedbackColumns { + grid-template-columns: 1fr; + gap: 1rem; + margin-top: 1rem; } - .modalHeader { - padding: 1rem; - flex-shrink: 0; + .teacherInfo { + padding: 0.875rem; + margin-bottom: 1rem; + background: var(--bg-light); + border-radius: 0.5rem; + align-items: flex-start; } - .modalHeader h4 { + .teacherName { font-size: 1rem; - margin: 0; + margin-bottom: 0.25rem; } - .modalIcon { - font-size: 1rem; - margin-right: 0.5rem; + .teacherTitle { + font-size: 0.875rem; } - .modalFooter { - padding: 0.75rem; - flex-shrink: 0; + .overallFeedback { + padding: 1rem; + margin-bottom: 1.25rem; + border-radius: 0.5rem; } - .feedbackModalContent { + .feedbackColumns { + display: grid; + grid-template-columns: 1fr; gap: 1rem; + margin-top: 1rem; + align-items: stretch; } - .assignmentHeader, - .performanceSummary, - .teacherFeedbackDetail, - .additionalDetails { + .strengthsSection, + .improvementsSection { padding: 1rem; + margin: 0; + border-radius: 0.5rem; + min-width: 0; + overflow-wrap: break-word; + word-wrap: break-word; + display: flex; + flex-direction: column; + width: 100%; + box-sizing: border-box; } - .assignmentTitle { - font-size: 1.1rem; + .feedbackSubtitle { + font-size: 0.95rem; + margin-bottom: 0.75rem; line-height: 1.3; + word-wrap: break-word; + overflow-wrap: break-word; + flex-wrap: wrap; } - .performanceSummary { - grid-template-columns: 1fr; - gap: 0.5rem; - } - - .performanceItem { - padding: 0.75rem 0.5rem; - min-height: 60px; - } - - .performanceValue { - font-size: 1rem; - flex-direction: column; - gap: 0.25rem; - } - - .percentage { + .feedbackText { font-size: 0.9rem; + line-height: 1.5; + word-wrap: break-word; + overflow-wrap: break-word; } - .detailItem { - padding: 0.4rem 0; - } - - .detailLabel, - .detailValue { + .feedbackIcon { font-size: 0.9rem; + flex-shrink: 0; } - - .feedbackText { - font-size: 0.9rem; + .overallScore { + font-size: 1.375rem; padding: 0.75rem; + text-align: center; } } @@ -1055,6 +1119,10 @@ display: flex; align-items: center; gap: 0.5rem; + word-wrap: break-word; + overflow-wrap: break-word; + line-height: 1.3; + flex-wrap: wrap; } .feedbackIcon { @@ -1072,6 +1140,7 @@ grid-template-columns: 1fr 1fr; gap: 2rem; margin-top: 1.5rem; + align-items: start; } .strengthsSection, @@ -1104,6 +1173,8 @@ padding: 0.5rem 0; color: var(--text-dark); line-height: 1.5; + width: 100%; + flex-wrap: wrap; } .checkmark { @@ -1122,6 +1193,14 @@ flex-shrink: 0; } +/* Ensure list text wraps within card and doesn't overflow */ +.feedbackTextItem { + flex: 1 1 auto; + min-width: 0; + overflow-wrap: break-word; + word-break: break-word; +} + /* ============================================================================ FEEDBACK MODAL - Detailed assignment feedback display ============================================================================ */ diff --git a/src/components/PermissionsManagement/PermissionsManagement.css b/src/components/PermissionsManagement/PermissionsManagement.css index d412fa2e22..449d782c2b 100644 --- a/src/components/PermissionsManagement/PermissionsManagement.css +++ b/src/components/PermissionsManagement/PermissionsManagement.css @@ -1,8 +1,7 @@ .permissions-management { padding: 0.5rem 1rem; width: 40%; - min-width: 320px; - max-width: 95vw; + min-width: 350px; margin: 1.5rem auto; box-shadow: 1px 1px 8px rgb(158, 158, 158); display: flex; @@ -84,8 +83,6 @@ display: flex; justify-content: space-between; flex-direction: row-reverse; - flex-wrap: wrap; - gap: 1rem; } .buttons-container { @@ -99,9 +96,7 @@ flex-direction: column; border: solid 1px lightgrey; border-radius: 8px; - min-width: 280px; - width: 100%; - max-width: 320px; + min-width: 300px; height: 450px; max-height: 480px; overflow: auto; @@ -228,69 +223,6 @@ @media only screen and (max-width: 1110px) { .permissions-management__header { flex-direction: column; - gap: 1rem; - } -} - -@media only screen and (max-width: 768px) { - .permissions-management { - width: 95%; - margin: 1rem auto; - padding: 1rem; - min-width: auto; - } - - .permissions-management--flex { - flex-direction: column; - align-items: center; - } - - .role-name-container { - max-width: 100%; - min-width: 100%; - margin-bottom: 1rem; - } - - .buttons-container { - width: 100%; - align-items: center; - } - - .user-role-tab__permission { - flex-direction: column; - align-items: flex-start; - gap: 0.5rem; - } - - .permission-label { - max-width: 100%; - margin-right: 0; - } -} - -@media only screen and (max-width: 480px) { - .permissions-management { - width: 98%; - padding: 0.75rem; - margin: 0.5rem auto; - } - - .permissions-management__title { - font-size: 1.5rem; - text-align: center; - } - - .user-permissions-pop-up__title { - font-size: 1.1rem; - } - - .role-name-container { - height: 300px; - max-height: 350px; - } - - .dropdown__user-perms { - max-height: 300px; } } From f5bb9c9600d1ee438fc79e087b7294a8297e68a6 Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:38:44 -0600 Subject: [PATCH 7/9] Fix: Implemented Feedback and Mobile responsiveness-3 --- .../EducationPortalSideNav.jsx | 90 +++ .../EducationPortalSideNav.module.css | 447 ++++++++++++ .../EvaluationResults/EvaluationResults.jsx | 676 +++++++++--------- .../EvaluationResults.module.css | 295 +++++++- .../EvaluationResultsWrapper.jsx | 13 + .../EductionPortal/SideBar/SideBar.jsx | 90 +++ .../EductionPortal/SideBar/SideBar.module.css | 447 ++++++++++++ .../EductionPortal/SidebarContext.jsx | 21 + src/components/Header/HeaderRenderer.jsx | 8 +- src/routes.jsx | 4 +- 10 files changed, 1744 insertions(+), 347 deletions(-) create mode 100644 src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.jsx create mode 100644 src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.module.css create mode 100644 src/components/EductionPortal/EvaluationResultsWrapper.jsx create mode 100644 src/components/EductionPortal/SideBar/SideBar.jsx create mode 100644 src/components/EductionPortal/SideBar/SideBar.module.css create mode 100644 src/components/EductionPortal/SidebarContext.jsx diff --git a/src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.jsx b/src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.jsx new file mode 100644 index 0000000000..2352e5969c --- /dev/null +++ b/src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { useSidebar } from '../SidebarContext'; +import styles from './EducationPortalSideNav.module.css'; + +const EducationPortalSideNav = () => { + const location = useLocation(); + const authUser = useSelector(state => state.auth?.user); + const { isMinimized, setIsMinimized } = useSidebar(); + + const menuItems = [ + { icon: '🏠', label: 'Homepage', path: '/educationportal' }, + { icon: '📊', label: 'Knowledge Evaluation', path: '#' }, + { icon: '📋', label: 'Past Lesson Plans', path: '#' }, + { icon: '⭐', label: 'My Saved Interests', path: '#' }, + { icon: '📈', label: 'Evaluation results', path: '/educationportal/evaluation-results' }, + { icon: '🏗️', label: 'Build Lesson Plan', path: '#' }, + ]; + + const isActive = path => path !== '#' && location.pathname === path; + + return ( + + ); +}; + +export default EducationPortalSideNav; diff --git a/src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.module.css b/src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.module.css new file mode 100644 index 0000000000..5ae4c501b8 --- /dev/null +++ b/src/components/EductionPortal/EducationPortalSideNav/EducationPortalSideNav.module.css @@ -0,0 +1,447 @@ +/* ===== SIDEBAR CONTAINER ===== */ +.sidebar { + position: fixed; + left: 0; + top: 0; + width: 200px; + height: 100vh; + background-color: #f5f5f5; + border-right: 1px solid #ddd; + display: flex; + flex-direction: column; + overflow-y: auto; + z-index: 1000; + transition: width 0.3s ease; +} + +.sidebar.minimized { + width: 70px; +} + +.sidebar.minimized .label, +.sidebar.minimized .welcomeText, +.sidebar.minimized .toggleButtonContainer { + display: none; +} + +/* ===== USER SECTION ===== */ +.userSection { + display: flex; + align-items: center; + gap: 8px; + padding: 12px; + border-bottom: 1px solid #ddd; + background-color: #fff; +} + +.welcomeIcon { + font-size: 20px; + flex-shrink: 0; +} + +.welcomeText { + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.welcomeLabel { + font-size: 11px; + color: #999; + font-weight: 500; + text-transform: uppercase; +} + +.userName { + font-size: 12px; + font-weight: 600; + color: #333; + word-break: break-word; +} + +.notifyIcon { + font-size: 16px; + flex-shrink: 0; + cursor: pointer; +} + +/* ===== NAVIGATION ===== */ +.nav { + flex: 1; + overflow-y: auto; + padding: 8px 0; +} + +.menuList { + list-style: none; + margin: 0; + padding: 0; +} + +.menuItem { + margin: 0; + padding: 0; +} + +.menuLink { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + color: #333; + text-decoration: none; + background: transparent; + border: none; + cursor: pointer; + width: 100%; + text-align: left; + font-size: 13px; + font-weight: 500; + transition: background-color 0.2s ease; + min-height: 44px; +} + +.menuLink:hover { + background-color: #efefef; +} + +.menuLink.active { + background-color: #e0e7ff; + color: #4f46e5; +} + +.icon { + font-size: 14px; + flex-shrink: 0; + width: 18px; + text-align: center; +} + +.label { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* ===== TOGGLE BUTTONS ===== */ +.toggleButtonContainer { + display: flex; + justify-content: center; + padding: 8px; + border-top: 1px solid #ddd; + background-color: #fff; +} + +.minimizeBtn { + background: none; + border: 1px solid #ddd; + padding: 8px 12px; + cursor: pointer; + border-radius: 4px; + font-size: 14px; + transition: all 0.2s ease; + width: 100%; +} + +.minimizeBtn:hover { + background-color: #f0f0f0; + border-color: #999; +} + +.toggleButton { + background: none; + border: none; + padding: 8px; + cursor: pointer; + font-size: 18px; + transition: all 0.2s ease; + width: 100%; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; +} + +.toggleButton:hover { + background-color: #efefef; +} + +/* ===== RESPONSIVE - DESKTOP (≥992px) ===== */ +@media (min-width: 992px) { + /* Desktop: sidebar visible by default */ +} + +/* ===== RESPONSIVE - TABLET (768px - 991px) ===== */ +@media (max-width: 991px) { + .sidebar { + width: 180px; + } + + .sidebar.minimized { + width: 70px; + } + + .userSection { + padding: 10px; + gap: 6px; + } + + .welcomeIcon { + font-size: 16px; + } + + .welcomeLabel { + font-size: 10px; + } + + .userName { + font-size: 11px; + } + + .notifyIcon { + font-size: 14px; + } + + .menuLink { + padding: 8px 10px; + font-size: 12px; + gap: 6px; + } + + .icon { + font-size: 12px; + width: 16px; + } +} + +/* ===== RESPONSIVE - MOBILE (≤767px) ===== */ +@media (max-width: 767px) { + .sidebar { + position: fixed; + left: 0; + top: 0; + width: 240px; + height: 100vh; + background-color: #f5f5f5; + border-right: 1px solid #ddd; + display: flex; + flex-direction: column; + overflow-y: auto; + z-index: 1000; + transition: width 0.3s ease; + } + + .sidebar.minimized { + width: 70px; + } + + .sidebar.minimized .label, + .sidebar.minimized .welcomeText, + .sidebar.minimized .toggleButtonContainer { + display: none; + } + + .userSection { + display: flex; + align-items: center; + gap: 10px; + padding: 14px 12px; + border-bottom: 1px solid #ddd; + background-color: #fff; + } + + .welcomeIcon { + font-size: 18px; + flex-shrink: 0; + } + + .welcomeText { + flex: 1; + display: flex; + flex-direction: column; + gap: 3px; + min-width: 0; + } + + .welcomeLabel { + font-size: 10px; + color: #999; + font-weight: 500; + text-transform: uppercase; + } + + .userName { + font-size: 12px; + font-weight: 600; + color: #333; + word-break: break-word; + } + + .notifyIcon { + font-size: 16px; + flex-shrink: 0; + cursor: pointer; + padding: 6px; + border-radius: 4px; + transition: background-color 0.2s ease; + } + + .notifyIcon:active { + background-color: #e0e0e0; + } + + .nav { + flex: 1; + overflow-y: auto; + padding: 8px 0; + } + + .menuList { + list-style: none; + margin: 0; + padding: 0; + } + + .menuItem { + margin: 0; + padding: 0; + } + + .menuLink { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 14px; + color: #333; + text-decoration: none; + background: transparent; + border: none; + cursor: pointer; + width: 100%; + text-align: left; + font-size: 13px; + font-weight: 500; + transition: all 0.2s ease; + min-height: 48px; + } + + .menuLink:active { + background-color: #f0f0f0; + } + + .menuLink:hover { + background-color: #efefef; + } + + .menuLink.active { + background-color: #e0e7ff; + color: #4f46e5; + border-left: 3px solid #4f46e5; + padding-left: 11px; + } + + .icon { + font-size: 16px; + flex-shrink: 0; + width: 20px; + text-align: center; + } + + .label { + flex: 1; + white-space: normal; + overflow: visible; + text-overflow: clip; + word-wrap: break-word; + } +} + +/* ===== RESPONSIVE - SMALL PHONES (≤480px) ===== */ +@media (max-width: 480px) { + .sidebar { + width: 220px; + } + + .sidebar.minimized { + width: 70px; + } + + .sidebar.minimized .label, + .sidebar.minimized .welcomeText, + .sidebar.minimized .toggleButtonContainer { + display: none; + } + + .userSection { + padding: 12px 10px; + } + + .welcomeIcon { + font-size: 16px; + } + + .userName { + font-size: 11px; + } + + .menuLink { + padding: 11px 12px; + font-size: 12px; + gap: 8px; + } + + .icon { + font-size: 14px; + width: 18px; + } +} + +/* ===== RESPONSIVE - EXTRA SMALL (≤360px) ===== */ +@media (max-width: 359px) { + .sidebar { + width: 200px; + } + + .sidebar.minimized { + width: 70px; + } + + .sidebar.minimized .label, + .sidebar.minimized .welcomeText, + .sidebar.minimized .toggleButtonContainer { + display: none; + } + + .userSection { + padding: 10px 8px; + gap: 6px; + } + + .welcomeIcon { + font-size: 14px; + } + + .welcomeLabel { + font-size: 9px; + } + + .userName { + font-size: 10px; + } + + .menuLink { + padding: 10px 10px; + font-size: 11px; + gap: 6px; + min-height: 44px; + } + + .icon { + font-size: 12px; + width: 16px; + } +} diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx index 9dd54a67a6..b7f4d8ef77 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -15,10 +15,13 @@ import { } from '@fortawesome/free-solid-svg-icons'; import styles from './EvaluationResults.module.css'; +import SideBar from '../SideBar/SideBar'; +import { useSidebar } from '../SidebarContext'; import EvaluationNotificationService from './evaluationNotificationService'; import { mockEvaluationData } from './mockData_new'; const EvaluationResults = ({ auth }) => { + const { isMinimized } = useSidebar(); const [evaluationData, setEvaluationData] = useState(null); const [loading, setLoading] = useState(true); const [selectedCategory, setSelectedCategory] = useState('all'); @@ -146,380 +149,389 @@ const EvaluationResults = ({ auth }) => { }; return ( -
    - {/* New Clean Header */} -
    - -
    -
    -

    Evaluation Results

    -

    - Comprehensive overview of your grades, performance on assignments, and detailed - feedback from teachers -

    -
    -
    -
    - - Welcome, {auth?.user?.firstName || 'Student Name'} + <> + +
    + {/* New Clean Header */} +
    + +
    +
    +

    Evaluation Results

    +

    + Comprehensive overview of your grades, performance on assignments, and detailed + feedback from teachers +

    +
    +
    +
    + + Welcome, {auth?.user?.firstName || 'Student Name'} +
    +
    -
    -
    - -
    + +
    - - {/* Overall Performance Summary */} -
    -
    -

    Overall Performance Summary

    -
    - {percentageScore}% - Overall Grade -
    -
    -
    -
    -
    + + {/* Overall Performance Summary */} +
    +
    +

    Overall Performance Summary

    +
    + {percentageScore}% + Overall Grade +
    -
    - - Total Score: {totalEarnedPoints}/{totalPossiblePoints} points - +
    +
    +
    +
    +
    + + Total Score: {totalEarnedPoints}/{totalPossiblePoints} points + +
    -
    - {/* Category Performance Table */} -
    -

    Overall Performance Summary

    -
    - - - - - - - - - - - - - - {categories.map(category => ( - - - - - - - - + {/* Category Performance Table */} +
    +

    Overall Performance Summary

    +
    +
    CategoryWeightageItemsTotal PointsYour ScorePercentagePerformance
    {category.name}{category.weightage}%{category.completedItems}{category.totalMarks}{category.earnedMarks}{Math.round(category.percentage)}% -
    -
    -
    -
    + + + + + + + + + - ))} - -
    CategoryWeightageItemsTotal PointsYour ScorePercentagePerformance
    + + + {categories.map(category => ( + + {category.name} + {category.weightage}% + {category.completedItems} + {category.totalMarks} + {category.earnedMarks} + {Math.round(category.percentage)}% + +
    +
    +
    + + + ))} + + +
    -
    - {/* Performance Insights */} -
    -
    -

    Performance Insights

    -

    - You performed strongly in Assignments (80%),{' '} - Exams (80%), and Participation (80%). You may - improve your performance in Quizzes (60%) - consider reviewing quiz - preparation strategies. -

    -
    - - + {/* Performance Insights */} +
    +
    +

    Performance Insights

    +

    + You performed strongly in Assignments (80%),{' '} + Exams (80%), and Participation (80%). You may + improve your performance in Quizzes (60%) - consider reviewing quiz + preparation strategies. +

    +
    + + +
    -
    - {/* Individual Assignment & Task Results */} -
    -

    Individual Assignment & Task Results

    -
    - - - - - - - - - - - - - {tasks.slice(0, 6).map(task => ( - - - - - - - + {/* Individual Assignment & Task Results */} +
    +

    Individual Assignment & Task Results

    +
    +
    Assignment NameWeightageYour MarksPercentageStatusFeedback
    -
    -
    {task.name}
    -
    - Submitted: {new Date(task.submissionDate).toLocaleDateString()} -
    -
    -
    {task.weightage || '8'}% - {task.earnedMarks}/{task.totalMarks} - - - {Math.round(task.percentage)}% - - - - {task.status || 'On time'} - - - -
    + + + + + + + + - ))} - -
    Assignment NameWeightageYour MarksPercentageStatusFeedback
    -
    + + + {tasks.slice(0, 6).map(task => ( + + +
    +
    {task.name}
    +
    + Submitted: {new Date(task.submissionDate).toLocaleDateString()} +
    +
    + + {task.weightage || '8'}% + + {task.earnedMarks}/{task.totalMarks} + + + + {Math.round(task.percentage)}% + + + + + {task.status || 'On time'} + + + + + + + ))} + + +
    - {/* Mobile Cards (shown on small screens) */} -
    - {tasks.slice(0, 6).map(task => ( -
    -
    -
    {task.name}
    - - {task.status || 'On time'} - -
    -
    -
    - Weightage: {task.weightage || '8'}% -
    -
    - Score: {task.earnedMarks}/{task.totalMarks} - - {' '} - ({Math.round(task.percentage)}%) + {/* Mobile Cards (shown on small screens) */} +
    + {tasks.slice(0, 6).map(task => ( +
    +
    +
    {task.name}
    + + {task.status || 'On time'}
    -
    - Submitted: {new Date(task.submissionDate).toLocaleDateString()} +
    +
    + Weightage: {task.weightage || '8'}% +
    +
    + Score: {task.earnedMarks}/{task.totalMarks} + + {' '} + ({Math.round(task.percentage)}%) + +
    +
    + Submitted:{' '} + {new Date(task.submissionDate).toLocaleDateString()} +
    +
    +
    +
    -
    - -
    -
    - ))} -
    -
    - - {/* Teacher Feedback Section */} -
    -
    -

    - - Teacher Feedback - Structured feedback display with strengths and recommendations -

    -
    - - {evaluationData.teacherFeedback.teacherName} - - - {evaluationData.teacherFeedback.teacherTitle} - + ))}
    -
    - {/* Overall Feedback */} -
    -

    Overall Assessment

    -

    {evaluationData.teacherFeedback.overall}

    + {/* Teacher Feedback Section */} +
    +
    +

    + + Teacher Feedback - Structured feedback display with strengths and recommendations +

    +
    + + {evaluationData.teacherFeedback.teacherName} + + + {evaluationData.teacherFeedback.teacherTitle} + +
    - {/* Strengths and Improvements */} -
    -
    -

    - - Strengths -

    -
      - {evaluationData.teacherFeedback.strengths.map((strength, index) => ( -
    • - - {strength} -
    • - ))} -
    +
    + {/* Overall Feedback */} +
    +

    Overall Assessment

    +

    {evaluationData.teacherFeedback.overall}

    -
    -

    - - Areas for Improvement -

    -
      - {evaluationData.teacherFeedback.improvements.map((improvement, index) => ( -
    • - - {improvement} -
    • - ))} -
    + {/* Strengths and Improvements */} +
    +
    +

    + + Strengths +

    +
      + {evaluationData.teacherFeedback.strengths.map((strength, index) => ( +
    • + + {strength} +
    • + ))} +
    +
    + +
    +

    + + Areas for Improvement +

    +
      + {evaluationData.teacherFeedback.improvements.map((improvement, index) => ( +
    • + + {improvement} +
    • + ))} +
    +
    -
    - {/* Summary Cards */} -
    -
    -
    6
    -
    Total Assignments
    -
    -
    -
    5
    -
    On Time
    -
    -
    -
    1
    -
    Late Submissions
    -
    -
    -
    72%
    -
    Avg Score
    + {/* Summary Cards */} +
    +
    +
    6
    +
    Total Assignments
    +
    +
    +
    5
    +
    On Time
    +
    +
    +
    1
    +
    Late Submissions
    +
    +
    +
    72%
    +
    Avg Score
    +
    -
    - + - {/* Feedback Detail Modal */} - - - - Assignment Feedback Details - - - {feedbackModal.task && ( -
    - {/* Assignment Header */} -
    -

    {feedbackModal.task.name}

    -
    - - - {feedbackModal.task.type} - - - - Due: {new Date(feedbackModal.task.dueDate).toLocaleDateString()} - + {/* Feedback Detail Modal */} + + + + Assignment Feedback Details + + + {feedbackModal.task && ( +
    + {/* Assignment Header */} +
    +

    {feedbackModal.task.name}

    +
    + + + {feedbackModal.task.type} + + + + Due: {new Date(feedbackModal.task.dueDate).toLocaleDateString()} + +
    -
    - {/* Performance Summary */} -
    -
    -
    Your Score
    -
    - {feedbackModal.task.earnedMarks}/{feedbackModal.task.totalMarks} - ({feedbackModal.task.percentage}%) + {/* Performance Summary */} +
    +
    +
    Your Score
    +
    + {feedbackModal.task.earnedMarks}/{feedbackModal.task.totalMarks} + ({feedbackModal.task.percentage}%) +
    -
    -
    -
    Weightage
    -
    - - {feedbackModal.task.weightage}% +
    +
    Weightage
    +
    + + {feedbackModal.task.weightage}% +
    -
    -
    -
    Status
    -
    - {feedbackModal.task.status} +
    +
    Status
    +
    + {feedbackModal.task.status} +
    -
    - {/* Teacher Feedback */} -
    -
    - - Teacher Feedback -
    -
    {feedbackModal.task.teacherFeedback}
    -
    - - {/* Additional Details */} -
    -
    - Submission Date: - - {new Date(feedbackModal.task.submissionDate).toLocaleDateString()} at{' '} - {new Date(feedbackModal.task.submissionDate).toLocaleTimeString()} - + {/* Teacher Feedback */} +
    +
    + + Teacher Feedback +
    +
    {feedbackModal.task.teacherFeedback}
    -
    - Grade Category: - {feedbackModal.task.category} + + {/* Additional Details */} +
    +
    + Submission Date: + + {new Date(feedbackModal.task.submissionDate).toLocaleDateString()} at{' '} + {new Date(feedbackModal.task.submissionDate).toLocaleTimeString()} + +
    +
    + Grade Category: + {feedbackModal.task.category} +
    -
    - )} - - - - - -
    + )} + + + + + +
    + ); }; diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css index 678dbb5a82..529758454d 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.module.css @@ -28,6 +28,53 @@ min-height: 100vh; background-color: var(--bg-light); padding: 0; + margin-left: 200px; + transition: margin-left 0.3s ease; +} + +/* When sidebar is minimized, expand the content */ +.evaluationResultsPage[data-sidebar-minimized='true'] { + margin-left: 70px; +} + +@media (max-width: 991px) { + .evaluationResultsPage { + margin-left: 180px; + } + + .evaluationResultsPage[data-sidebar-minimized='true'] { + margin-left: 70px; + } +} + +@media (max-width: 767px) { + .evaluationResultsPage { + margin-left: 240px; + } + + .evaluationResultsPage[data-sidebar-minimized='true'] { + margin-left: 70px; + } +} + +@media (max-width: 480px) { + .evaluationResultsPage { + margin-left: 220px; + } + + .evaluationResultsPage[data-sidebar-minimized='true'] { + margin-left: 70px; + } +} + +@media (max-width: 359px) { + .evaluationResultsPage { + margin-left: 200px; + } + + .evaluationResultsPage[data-sidebar-minimized='true'] { + margin-left: 70px; + } } /* ============================================================================ @@ -1425,23 +1472,247 @@ } /* ============================================================================ - MODAL RESPONSIVE BREAKPOINTS + MOBILE RESPONSIVE BREAKPOINTS ============================================================================ */ -/* Large screens - limit modal width for better readability */ -@media (min-width: 1200px) { - .feedbackModal .modal-dialog { - max-width: 900px; +/* Tablets (768px and below) */ +@media (max-width: 991px) and (min-width: 768px) { + .evaluationResultsPage { + margin-left: 180px; } } -/* Medium screens - optimize modal layout */ -@media (min-width: 992px) and (max-width: 1199px) { - .feedbackModal .modal-dialog { - max-width: 800px; +/* Mobile phones (767px and below) */ +@media (max-width: 767px) { + .evaluationResultsPage { + margin-left: 240px; } - - .performanceSummary { - grid-template-columns: repeat(3, 1fr); + + .headerSection { + padding: 1.5rem 0; + margin-bottom: 1rem; + } + + .headerContent { + flex-direction: column; + gap: 1rem; + } + + .headerRight { + position: relative; + right: auto; + top: auto; + margin-top: 1rem; + } + + .pageTitle { + font-size: 1.5rem; + } + + .pageSubtitle { + font-size: 0.85rem; + } + + .mainContent { + padding: 0 1rem; + } + + .overallSection { + padding: 1rem; + margin-bottom: 1rem; + } + + .sectionHeader { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + } + + .overallScore { + width: 100%; + } + + .categorySection, + .assignmentSection, + .insightsSection, + .teacherFeedbackSection { + margin-bottom: 1rem; + } + + .tableContainer { + overflow-x: auto; + border-radius: 0; + } + + .performanceTable { + font-size: 0.75rem; + } + + .performanceTable th, + .performanceTable td { + padding: 0.5rem; + } + + .mobileAssignments { + display: block; + } + + .assignmentTable { + display: none; + } + + .assignmentCard { + margin-bottom: 1rem; + padding: 1rem; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + } + + .feedbackColumns { + flex-direction: column; + gap: 1rem; + } + + .summaryCards { + grid-template-columns: repeat(2, 1fr); + gap: 0.75rem; + } + + .summaryCard { + padding: 0.75rem; + } + + .cardNumber { + font-size: 1.5rem; + } + + .cardLabel { + font-size: 0.75rem; + } +} + +/* Mobile phones (480px and below) */ +@media (max-width: 480px) { + .evaluationResultsPage { + margin-left: 220px; + } + + .headerSection { + padding: 1rem 0; + margin-bottom: 0.75rem; + } + + .pageTitle { + font-size: 1.25rem; + margin-bottom: 0.5rem; + } + + .pageSubtitle { + font-size: 0.8rem; + } + + .mainContent { + padding: 0 0.75rem; + } + + .overallSection { + padding: 0.75rem; + } + + .userWelcome { + flex-wrap: wrap; + font-size: 0.75rem; + padding: 0.35rem 0.75rem; + } + + .performanceTable { + font-size: 0.65rem; + } + + .performanceTable th, + .performanceTable td { + padding: 0.35rem; + } + + .sectionTitle { + font-size: 1rem; + } + + .feedbackColumns { + gap: 0.75rem; + } + + .strengthsSection, + .improvementsSection { + padding: 0.75rem; + } + + .feedbackList { + padding-left: 1rem; + } + + .feedbackItem { + padding: 0.5rem 0; + font-size: 0.8rem; + } + + .summaryCards { + grid-template-columns: 1fr; + gap: 0.5rem; + } + + .summaryCard { + padding: 0.5rem; + } + + .cardNumber { + font-size: 1.25rem; + } + + .cardLabel { + font-size: 0.7rem; + } + + .actionButtons { + flex-direction: column; + } + + .actionButton { + width: 100%; + padding: 0.5rem; + font-size: 0.75rem; + } + + .feedbackButton { + padding: 0.5rem 1rem; + font-size: 0.75rem; + } +} + +/* Small phones (below 360px) */ +@media (max-width: 359px) { + .evaluationResultsPage { + margin-left: 200px; + } + + .pageTitle { + font-size: 1rem; + } + + .mainContent { + padding: 0 0.5rem; + } + + .overallScore .scoreText { + font-size: 1.5rem; + } + + .performanceTable { + font-size: 0.6rem; + } + + .performanceTable th, + .performanceTable td { + padding: 0.25rem; } } \ No newline at end of file diff --git a/src/components/EductionPortal/EvaluationResultsWrapper.jsx b/src/components/EductionPortal/EvaluationResultsWrapper.jsx new file mode 100644 index 0000000000..db7046e553 --- /dev/null +++ b/src/components/EductionPortal/EvaluationResultsWrapper.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { SidebarProvider } from './SidebarContext'; +import EvaluationResults from './EvaluationResults/EvaluationResults'; + +const EvaluationResultsWithSidebar = props => { + return ( + + + + ); +}; + +export default EvaluationResultsWithSidebar; diff --git a/src/components/EductionPortal/SideBar/SideBar.jsx b/src/components/EductionPortal/SideBar/SideBar.jsx new file mode 100644 index 0000000000..309e8f951a --- /dev/null +++ b/src/components/EductionPortal/SideBar/SideBar.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { useSidebar } from '../SidebarContext'; +import styles from './SideBar.module.css'; + +const SideBar = () => { + const location = useLocation(); + const authUser = useSelector(state => state.auth?.user); + const { isMinimized, setIsMinimized } = useSidebar(); + + const menuItems = [ + { icon: '🏠', label: 'Homepage', path: '/educationportal' }, + { icon: '📊', label: 'Knowledge Evaluation', path: '#' }, + { icon: '📋', label: 'Past Lesson Plans', path: '#' }, + { icon: '⭐', label: 'My Saved Interests', path: '#' }, + { icon: '📈', label: 'Evaluation results', path: '/educationportal/evaluation-results' }, + { icon: '🏗️', label: 'Build Lesson Plan', path: '#' }, + ]; + + const isActive = path => path !== '#' && location.pathname === path; + + return ( + + ); +}; + +export default SideBar; diff --git a/src/components/EductionPortal/SideBar/SideBar.module.css b/src/components/EductionPortal/SideBar/SideBar.module.css new file mode 100644 index 0000000000..5ae4c501b8 --- /dev/null +++ b/src/components/EductionPortal/SideBar/SideBar.module.css @@ -0,0 +1,447 @@ +/* ===== SIDEBAR CONTAINER ===== */ +.sidebar { + position: fixed; + left: 0; + top: 0; + width: 200px; + height: 100vh; + background-color: #f5f5f5; + border-right: 1px solid #ddd; + display: flex; + flex-direction: column; + overflow-y: auto; + z-index: 1000; + transition: width 0.3s ease; +} + +.sidebar.minimized { + width: 70px; +} + +.sidebar.minimized .label, +.sidebar.minimized .welcomeText, +.sidebar.minimized .toggleButtonContainer { + display: none; +} + +/* ===== USER SECTION ===== */ +.userSection { + display: flex; + align-items: center; + gap: 8px; + padding: 12px; + border-bottom: 1px solid #ddd; + background-color: #fff; +} + +.welcomeIcon { + font-size: 20px; + flex-shrink: 0; +} + +.welcomeText { + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.welcomeLabel { + font-size: 11px; + color: #999; + font-weight: 500; + text-transform: uppercase; +} + +.userName { + font-size: 12px; + font-weight: 600; + color: #333; + word-break: break-word; +} + +.notifyIcon { + font-size: 16px; + flex-shrink: 0; + cursor: pointer; +} + +/* ===== NAVIGATION ===== */ +.nav { + flex: 1; + overflow-y: auto; + padding: 8px 0; +} + +.menuList { + list-style: none; + margin: 0; + padding: 0; +} + +.menuItem { + margin: 0; + padding: 0; +} + +.menuLink { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + color: #333; + text-decoration: none; + background: transparent; + border: none; + cursor: pointer; + width: 100%; + text-align: left; + font-size: 13px; + font-weight: 500; + transition: background-color 0.2s ease; + min-height: 44px; +} + +.menuLink:hover { + background-color: #efefef; +} + +.menuLink.active { + background-color: #e0e7ff; + color: #4f46e5; +} + +.icon { + font-size: 14px; + flex-shrink: 0; + width: 18px; + text-align: center; +} + +.label { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* ===== TOGGLE BUTTONS ===== */ +.toggleButtonContainer { + display: flex; + justify-content: center; + padding: 8px; + border-top: 1px solid #ddd; + background-color: #fff; +} + +.minimizeBtn { + background: none; + border: 1px solid #ddd; + padding: 8px 12px; + cursor: pointer; + border-radius: 4px; + font-size: 14px; + transition: all 0.2s ease; + width: 100%; +} + +.minimizeBtn:hover { + background-color: #f0f0f0; + border-color: #999; +} + +.toggleButton { + background: none; + border: none; + padding: 8px; + cursor: pointer; + font-size: 18px; + transition: all 0.2s ease; + width: 100%; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; +} + +.toggleButton:hover { + background-color: #efefef; +} + +/* ===== RESPONSIVE - DESKTOP (≥992px) ===== */ +@media (min-width: 992px) { + /* Desktop: sidebar visible by default */ +} + +/* ===== RESPONSIVE - TABLET (768px - 991px) ===== */ +@media (max-width: 991px) { + .sidebar { + width: 180px; + } + + .sidebar.minimized { + width: 70px; + } + + .userSection { + padding: 10px; + gap: 6px; + } + + .welcomeIcon { + font-size: 16px; + } + + .welcomeLabel { + font-size: 10px; + } + + .userName { + font-size: 11px; + } + + .notifyIcon { + font-size: 14px; + } + + .menuLink { + padding: 8px 10px; + font-size: 12px; + gap: 6px; + } + + .icon { + font-size: 12px; + width: 16px; + } +} + +/* ===== RESPONSIVE - MOBILE (≤767px) ===== */ +@media (max-width: 767px) { + .sidebar { + position: fixed; + left: 0; + top: 0; + width: 240px; + height: 100vh; + background-color: #f5f5f5; + border-right: 1px solid #ddd; + display: flex; + flex-direction: column; + overflow-y: auto; + z-index: 1000; + transition: width 0.3s ease; + } + + .sidebar.minimized { + width: 70px; + } + + .sidebar.minimized .label, + .sidebar.minimized .welcomeText, + .sidebar.minimized .toggleButtonContainer { + display: none; + } + + .userSection { + display: flex; + align-items: center; + gap: 10px; + padding: 14px 12px; + border-bottom: 1px solid #ddd; + background-color: #fff; + } + + .welcomeIcon { + font-size: 18px; + flex-shrink: 0; + } + + .welcomeText { + flex: 1; + display: flex; + flex-direction: column; + gap: 3px; + min-width: 0; + } + + .welcomeLabel { + font-size: 10px; + color: #999; + font-weight: 500; + text-transform: uppercase; + } + + .userName { + font-size: 12px; + font-weight: 600; + color: #333; + word-break: break-word; + } + + .notifyIcon { + font-size: 16px; + flex-shrink: 0; + cursor: pointer; + padding: 6px; + border-radius: 4px; + transition: background-color 0.2s ease; + } + + .notifyIcon:active { + background-color: #e0e0e0; + } + + .nav { + flex: 1; + overflow-y: auto; + padding: 8px 0; + } + + .menuList { + list-style: none; + margin: 0; + padding: 0; + } + + .menuItem { + margin: 0; + padding: 0; + } + + .menuLink { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 14px; + color: #333; + text-decoration: none; + background: transparent; + border: none; + cursor: pointer; + width: 100%; + text-align: left; + font-size: 13px; + font-weight: 500; + transition: all 0.2s ease; + min-height: 48px; + } + + .menuLink:active { + background-color: #f0f0f0; + } + + .menuLink:hover { + background-color: #efefef; + } + + .menuLink.active { + background-color: #e0e7ff; + color: #4f46e5; + border-left: 3px solid #4f46e5; + padding-left: 11px; + } + + .icon { + font-size: 16px; + flex-shrink: 0; + width: 20px; + text-align: center; + } + + .label { + flex: 1; + white-space: normal; + overflow: visible; + text-overflow: clip; + word-wrap: break-word; + } +} + +/* ===== RESPONSIVE - SMALL PHONES (≤480px) ===== */ +@media (max-width: 480px) { + .sidebar { + width: 220px; + } + + .sidebar.minimized { + width: 70px; + } + + .sidebar.minimized .label, + .sidebar.minimized .welcomeText, + .sidebar.minimized .toggleButtonContainer { + display: none; + } + + .userSection { + padding: 12px 10px; + } + + .welcomeIcon { + font-size: 16px; + } + + .userName { + font-size: 11px; + } + + .menuLink { + padding: 11px 12px; + font-size: 12px; + gap: 8px; + } + + .icon { + font-size: 14px; + width: 18px; + } +} + +/* ===== RESPONSIVE - EXTRA SMALL (≤360px) ===== */ +@media (max-width: 359px) { + .sidebar { + width: 200px; + } + + .sidebar.minimized { + width: 70px; + } + + .sidebar.minimized .label, + .sidebar.minimized .welcomeText, + .sidebar.minimized .toggleButtonContainer { + display: none; + } + + .userSection { + padding: 10px 8px; + gap: 6px; + } + + .welcomeIcon { + font-size: 14px; + } + + .welcomeLabel { + font-size: 9px; + } + + .userName { + font-size: 10px; + } + + .menuLink { + padding: 10px 10px; + font-size: 11px; + gap: 6px; + min-height: 44px; + } + + .icon { + font-size: 12px; + width: 16px; + } +} diff --git a/src/components/EductionPortal/SidebarContext.jsx b/src/components/EductionPortal/SidebarContext.jsx new file mode 100644 index 0000000000..51664b0fd1 --- /dev/null +++ b/src/components/EductionPortal/SidebarContext.jsx @@ -0,0 +1,21 @@ +import React, { createContext, useState } from 'react'; + +export const SidebarContext = createContext(); + +export const SidebarProvider = ({ children }) => { + const [isMinimized, setIsMinimized] = useState(false); + + return ( + + {children} + + ); +}; + +export const useSidebar = () => { + const context = React.useContext(SidebarContext); + if (!context) { + throw new Error('useSidebar must be used within a SidebarProvider'); + } + return context; +}; diff --git a/src/components/Header/HeaderRenderer.jsx b/src/components/Header/HeaderRenderer.jsx index ed70df12b2..6df0a5c63d 100644 --- a/src/components/Header/HeaderRenderer.jsx +++ b/src/components/Header/HeaderRenderer.jsx @@ -10,7 +10,13 @@ import hasPermission from '../../utils/permissions'; export function HeaderRenderer(props) { const location = useLocation(); const isCommunityPortal = location.pathname.startsWith('/communityportal'); - + const isEducationEvaluation = location.pathname.startsWith('/educationportal/evaluation-results'); + + // Hide header for education portal evaluation results page + if (isEducationEvaluation) { + return null; + } + // eslint-disable-next-line react/jsx-props-no-spreading return isCommunityPortal ? :
    ; } diff --git a/src/routes.jsx b/src/routes.jsx index 5b0ae0da3c..a123734caa 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -138,7 +138,7 @@ import CommunityCalendar from './components/CommunityPortal/Calendar/CommunityCa import EPProtectedRoute from './components/common/EPDashboard/EPProtectedRoute'; import EPLogin from './components/EductionPortal/Login'; import EPDashboard from './components/EductionPortal'; -import EvaluationResults from './components/EductionPortal/EvaluationResults/EvaluationResults'; +import EvaluationResultsWrapper from './components/EductionPortal/EvaluationResultsWrapper'; import MostSusceptibleTools from './components/MostSusceptible/toolBreakdownChart'; @@ -685,7 +685,7 @@ export default ( From 3e274d0f7e23b00162f7d11deae3176d0aaee76c Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Fri, 5 Dec 2025 11:42:19 -0600 Subject: [PATCH 8/9] fix: Suggestions through --- .../EvaluationResults/EvaluationResults.jsx | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx index b7f4d8ef77..92a2b52e29 100644 --- a/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx +++ b/src/components/EductionPortal/EvaluationResults/EvaluationResults.jsx @@ -139,7 +139,45 @@ const EvaluationResults = ({ auth }) => { return styles.poor; }; - // Feedback modal functions + // Generate dynamic performance insights based on actual data + const getPerformanceInsights = () => { + const strongAreas = categories.filter(cat => cat.percentage >= 80); + const improvementAreas = categories.filter(cat => cat.percentage < 70); + + const strongText = + strongAreas.length > 0 + ? strongAreas + .slice(0, 3) + .map(cat => `${cat.name} (${Math.round(cat.percentage)}%)`) + .join(', ') + : 'various areas'; + + const improvementText = + improvementAreas.length > 0 && improvementAreas[0] + ? `${improvementAreas[0].name} (${Math.round(improvementAreas[0].percentage)}%)` + : 'certain areas'; + + return { strongText, improvementText, hasImprovementAreas: improvementAreas.length > 0 }; + }; + + const { strongText, improvementText, hasImprovementAreas } = getPerformanceInsights(); + + // Calculate summary statistics dynamically + const calculateSummaryStats = () => { + const totalAssignments = tasks.length; + const onTimeCount = tasks.filter( + task => task.status === 'On time' || task.status === 'completed', + ).length; + const lateCount = tasks.filter(task => task.status?.toLowerCase().includes('late')).length; + const averageScore = Math.round( + tasks.reduce((sum, task) => sum + task.percentage, 0) / tasks.length, + ); + + return { totalAssignments, onTimeCount, lateCount, averageScore }; + }; + + const { totalAssignments, onTimeCount, lateCount, averageScore } = calculateSummaryStats(); + const openFeedbackModal = task => { setFeedbackModal({ isOpen: true, task }); }; @@ -243,13 +281,17 @@ const EvaluationResults = ({ auth }) => {

    Performance Insights

    - You performed strongly in Assignments (80%),{' '} - Exams (80%), and Participation (80%). You may - improve your performance in Quizzes (60%) - consider reviewing quiz - preparation strategies. + You performed strongly in {strongText}. + {hasImprovementAreas && ( + <> + {' '} + You may improve your performance in {improvementText} - + consider reviewing preparation strategies. + + )}

    - +
    @@ -416,19 +458,19 @@ const EvaluationResults = ({ auth }) => { {/* Summary Cards */}
    -
    6
    +
    {totalAssignments}
    Total Assignments
    -
    5
    +
    {onTimeCount}
    On Time
    -
    1
    +
    {lateCount}
    Late Submissions
    -
    72%
    +
    {averageScore}%
    Avg Score
    From d1f141f1187c5de2798484390fdf835fcf3a2eed Mon Sep 17 00:00:00 2001 From: boppanapraveen <61085397+boppanapraveen@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:39:54 -0600 Subject: [PATCH 9/9] fix:Merge Conflicts --- src/routes.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes.jsx b/src/routes.jsx index da1ffc5d99..4c571574ed 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -166,6 +166,7 @@ import EPProtectedRoute from './components/common/EPDashboard/EPProtectedRoute'; import EPLogin from './components/EductionPortal/Login'; import EPDashboard from './components/EductionPortal'; import GroupList from './components/EductionPortal/GroupList/GroupList'; +import EvaluationResultsWrapper from './components/EductionPortal/EvaluationResultsWrapper'; import PRReviewTeamAnalytics from './components/HGNPRDashboard/PRReviewTeamAnalytics'; import PRDashboardOverview from './components/HGNPRDashboard/PRDashboardOverview';