Skip to content

Conversation

@GowthamDhanaraju
Copy link
Contributor

@GowthamDhanaraju GowthamDhanaraju commented Jun 21, 2025

Summary by CodeRabbit

  • New Features

    • Introduced multiple new chart components for enhanced visualization of student and teacher results, including performance trends, distribution, frequency, and question-wise analysis.
    • Added interactive layout toggles and course visibility controls for performance graphs.
    • Student results now display a performance trends chart in the overview section.
    • Teacher results now show detailed performance graphs with absolute marks and submission statistics.
  • Bug Fixes

    • Improved clarity and maintainability of answer rendering in detailed test results.
  • Chores

    • Expanded and updated mock data to include more courses, tests, and detailed statistics for more realistic results.

GowthamDhanaraju and others added 18 commits June 12, 2025 19:48
- Added icons to question and explanation titles in Match Following, MCQ, and True/False question components for better visual representation.
- Updated the MCQ question component to allow clickable options for selecting correct answers, enhancing user experience.
- Improved the Tiptap editor to accept a height prop for better customization.
- Refactored select components for cleaner code and improved styling.
- Created a new test page for editing questions with sample data to facilitate testing.
…anagement

- Created types for student overall results, recent test results, course results, test summaries, detailed test results, question results, and results filters.
- Implemented ResultsAPI class with methods to fetch student overview, course results, test summaries, detailed test results, and recent results.
- Added mock data for development and testing purposes for both student and teacher results.
feat: implement StudentResultsTable component for detailed student results

feat: define types for teacher-specific results in types.ts

refactor: remove deprecated TestSummaries component and related types

refactor: delete unused types from results/types.ts

fix: update results-api to use new types and mock data structure

feat: create utility functions for consistent score color display

chore: add stub for useRecentTestResults to resolve import errors
- Removed `question-stats-table` component and its export from `index.ts`.
- Added `marks-frequency-bar-chart`, `question-wise-bar-chart`, and `student-performance-pie-chart` components for improved data visualization.
- Updated mock data in `results-api.ts` to reflect more realistic test results and added new courses and tests.
- Increased the number of mock students and questions for better testing scenarios.
…e colors; update performance distribution comments
- Fix SSR compatibility in student-performance-pie-chart by adding window check for getComputedStyle
- Fix rounding error in QuestionWiseBarChart calculation to ensure correct + wrong = attemptedCount
- Rename QuestionStat to QuestionStatChart in question-wise-bar-chart to avoid type conflicts
- Add useThemeColors hook for SSR-safe theme detection in chart components
- Switch from sonner to useToast hook for toast messages (partial implementation)
Copilot AI review requested due to automatic review settings June 21, 2025 11:10
@coderabbitai
Copy link

coderabbitai bot commented Jun 21, 2025

Walkthrough

This update introduces several new chart components for both student and teacher results pages, enhancing data visualization with performance trends, score distributions, and question-wise analytics. The mock API data is expanded for realism, and layouts are refactored to accommodate the new components and provide more detailed statistical summaries.

Changes

File(s) Change Summary
src/app/(test)/student-results/page.tsx Added StudentPerformanceTrendsChart to the student results overview section; removed placeholder comments.
src/app/(test)/teacher-results/page.tsx Replaced old charts/tables with new chart components, added layout toggling, and updated statistics display.
src/components/results/common/detailed-test-result.tsx Refactored MCQ answer rendering logic for clarity; added comment about chart removal for students; whitespace cleanup.
src/components/results/common/marks-pie-chart.tsx Introduced new MarksPieChart component for marks distribution visualization.
src/components/results/common/question-bar-chart.tsx Introduced new QuestionBarChart component for question-wise results as a stacked bar chart.
src/components/results/student/performance-distribution-chart.tsx Added new StudentPerformanceDistributionChart for student test score distribution.
src/components/results/student/performance-trends-chart.tsx Added new StudentPerformanceTrendsChart for visualizing performance across courses.
src/components/results/teacher/index.ts Removed export for question-stats-table.
src/components/results/teacher/marks-frequency-bar-chart.tsx Added new MarksFrequencyBarChart for displaying marks frequency distribution.
src/components/results/teacher/question-wise-bar-chart.tsx Added new QuestionWiseBarChart for question-wise correct/wrong analytics.
src/components/results/teacher/student-performance-pie-chart.tsx Added new StudentPerformancePieChart for student performance category distribution.
src/lib/results-api.ts Expanded and updated mock data: more courses, tests, and statistics for both student and teacher APIs.

Sequence Diagram(s)

sequenceDiagram
    participant Student as StudentResultsPage
    participant TrendsChart as StudentPerformanceTrendsChart
    participant API as ResultsAPI

    Student->>API: Fetch courseResults
    Student->>TrendsChart: Pass courseResults as courses prop
    TrendsChart->>TrendsChart: Render line chart of performance trends
Loading
sequenceDiagram
    participant Teacher as TeacherResultsPage
    participant API as TeacherResultsAPI
    participant PieChart as StudentPerformancePieChart
    participant BarChart as MarksFrequencyBarChart
    participant QBarChart as QuestionWiseBarChart

    Teacher->>API: Fetch test statistics
    Teacher->>PieChart: Pass scores
    Teacher->>BarChart: Pass scores
    Teacher->>QBarChart: Pass questionStats
    PieChart->>PieChart: Render performance pie chart
    BarChart->>BarChart: Render marks frequency bar chart
    QBarChart->>QBarChart: Render question-wise bar chart
Loading

Possibly related PRs

  • evalify/evalify-frontend#43: Introduces the initial setup of charting libraries and foundational chart components, which this PR extends with new charts and integrations.

Suggested reviewers

  • Aksaykanthan

Poem

In the garden of charts, new colors bloom bright,
Bars and pies twirl in the soft data light.
Trends rise and fall like a bunny’s quick hop,
With questions and marks, the insights don’t stop!
🥕📊
Hop along, dear dev, and see data take flight!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-24T14_22_26_399Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances both teacher and student results pages by introducing interactive chart components for visualizing performance data and updating the UI layout.

  • Added new chart components (pie, bar, line) to display distributions, question stats, and performance trends.
  • Updated teacher and student result pages to integrate charts, including a layout toggle on the teacher page.
  • Removed deprecated exports and cleaned up detailed result view by removing redundant charts.

Reviewed Changes

Copilot reviewed 11 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/components/results/teacher/student-performance-pie-chart.tsx New pie chart for student performance distribution
src/components/results/teacher/question-wise-bar-chart.tsx Bar chart for question-wise correct/wrong stats
src/components/results/teacher/marks-frequency-bar-chart.tsx Line chart showing marks frequency distribution
src/components/results/teacher/index.ts Removed obsolete export of question-stats-table
src/components/results/student/performance-trends-chart.tsx Student-side line chart for performance trends
src/components/results/student/performance-distribution-chart.tsx Student-side bar chart for distribution with grouping selector
src/components/results/common/question-bar-chart.tsx Common bar chart component for question results
src/components/results/common/marks-pie-chart.tsx Common pie chart for marks breakdown
src/components/results/common/detailed-test-result.tsx Refactored MCQ rendering; removed embedded charts
src/app/(test)/teacher-results/page.tsx Integrated new teacher charts and layout toggle
src/app/(test)/student-results/page.tsx Added performance trends chart to student page
Comments suppressed due to low confidence (2)

src/components/results/teacher/marks-frequency-bar-chart.tsx:38

  • [nitpick] This component renders a line chart (using Line) but is named MarksFrequencyBarChart. Consider renaming it to MarksFrequencyLineChart for clarity.
export const MarksFrequencyBarChart: React.FC<MarksFrequencyBarChartProps> = ({

src/components/results/teacher/student-performance-pie-chart.tsx:1

  • This new chart component lacks associated unit or snapshot tests. Consider adding tests to verify that the chart renders correctly for different score distributions.
import React from "react";

scores={testStatistics.studentResults.map(
(s) => s.percentage,
)}
totalMarks={testStatistics.totalMarks}
Copy link

Copilot AI Jun 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MarksFrequencyBarChart component does not accept a totalMarks prop in its interface. Either remove this prop from the JSX or update the component signature to accept and use it.

Suggested change
totalMarks={testStatistics.totalMarks}

Copilot uses AI. Check for mistakes.
const chart = (
<Bar
data={{
labels: visibleStats.map((q, i) => `Q${i + 1}`),
Copy link

Copilot AI Jun 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already have a question property in visibleStats. To ensure the labels reflect the actual question identifiers, consider using visibleStats.map(q => q.question) instead of hard-coding Q${i + 1}.

Suggested change
labels: visibleStats.map((q, i) => `Q${i + 1}`),
labels: visibleStats.map((q) => q.question),

Copilot uses AI. Check for mistakes.
Rows3,
} from "lucide-react";
import { Separator } from "@/components/ui/separator";
import { toast } from "sonner";
Copy link

Copilot AI Jun 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per project conventions, use the useToast hook from src/hooks/use-toast.ts instead of importing toast directly from sonner.

Suggested change
import { toast } from "sonner";
import { useToast } from "@/hooks/use-toast";

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
src/app/(test)/teacher-results/page.tsx (1)

500-556: Extract repeated score calculations to avoid duplication

The calculation (percentage / 100) * totalMarks is repeated multiple times. Consider extracting it to a utility function.

Add a utility function:

const calculateAbsoluteScore = (percentage: number, totalMarks: number): string => {
  return ((percentage / 100) * totalMarks).toFixed(1);
};

Then use it throughout:

-{testStatistics.averageScore.toFixed(1)}% (
-{(
-  (testStatistics.averageScore / 100) *
-  testStatistics.totalMarks
-).toFixed(1)}
-/{testStatistics.totalMarks})
+{testStatistics.averageScore.toFixed(1)}% (
+{calculateAbsoluteScore(testStatistics.averageScore, testStatistics.totalMarks)}
+/{testStatistics.totalMarks})
🧹 Nitpick comments (14)
src/components/results/common/marks-pie-chart.tsx (1)

13-23: Consider adding validation for empty data sets.

The component correctly processes question results but doesn't handle the edge case where the questions array might be empty, which could result in a pie chart with all zero values.

Consider adding a guard clause to handle empty data:

export const MarksPieChart: React.FC<MarksPieChartProps> = ({ questions }) => {
+  if (!questions.length) {
+    return (
+      <div className="w-full h-64 flex items-center justify-center text-muted-foreground">
+        No question data available
+      </div>
+    );
+  }
+
  const correctMarks = questions
    .filter((q) => q.isCorrect === true)
    .reduce((sum, q) => sum + (q.marks || 0), 0);
src/components/results/common/question-bar-chart.tsx (1)

23-26: Consider visualizing unanswered questions.

The component only shows correct and wrong answers but doesn't visualize unanswered questions (where isCorrect === undefined). This might provide incomplete information to users.

Consider adding a third dataset for unanswered questions:

export const QuestionBarChart: React.FC<QuestionBarChartProps> = ({
  questions,
}) => {
  const labels = questions.map((q, i) => `Q${i + 1}`);
  const correct = questions.map((q) => (q.isCorrect === true ? 1 : 0));
  const wrong = questions.map((q) => (q.isCorrect === false ? 1 : 0));
+  const unanswered = questions.map((q) => (q.isCorrect === undefined ? 1 : 0));
src/components/results/teacher/marks-frequency-bar-chart.tsx (2)

23-23: Component name doesn't match implementation.

The component is named MarksFrequencyBarChart but actually renders a Line chart. This could cause confusion for developers using this component.

Consider renaming to better reflect the implementation:

-export interface MarksFrequencyBarChartProps {
+export interface MarksFrequencyLineChartProps {
-export const MarksFrequencyBarChart: React.FC<MarksFrequencyBarChartProps> = ({
+export const MarksFrequencyLineChart: React.FC<MarksFrequencyLineChartProps> = ({

63-70: Reusable SSR-safe utility function.

The getCSSVariable function is well-implemented and could potentially be extracted to a shared utilities file for reuse across other chart components.

Consider extracting this utility to a shared location:

// utils/chart-utils.ts
export const getCSSVariable = (variable: string, fallback: string) => {
  if (typeof window === "undefined") return fallback;
  return (
    getComputedStyle(document.documentElement).getPropertyValue(variable) ||
    fallback
  );
};
src/components/results/student/performance-trends-chart.tsx (3)

77-77: Use a more robust approach for color opacity

Using string concatenation with hex colors for opacity is fragile and may not work correctly with all color formats.

Consider using a utility function or CSS rgba values:

-backgroundColor: palette[idx % palette.length] + "33", // 20% opacity fill
+backgroundColor: `${palette[idx % palette.length]}33`, // 20% opacity fill

Or better yet, use a color manipulation library or convert to rgba:

// Example utility function
const hexToRgba = (hex: string, opacity: number) => {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  return `rgba(${r}, ${g}, ${b}, ${opacity})`;
};

91-96: Extract dark mode detection as a utility function

The isDarkMode function doesn't depend on component props or state and could be reused elsewhere.

Consider extracting this to a shared utility file:

// In @/lib/utils/theme.ts
export const isDarkMode = (): boolean => {
  if (typeof window === "undefined" || typeof document === "undefined")
    return false;
  return document.documentElement.classList.contains("dark");
};

Then import and use it in the component.


113-113: Extract complex className logic for better maintainability

The className string is complex and hard to read. Consider using a utility like clsx or extracting the logic.

import { clsx } from 'clsx';

const buttonClassName = clsx(
  "flex items-center gap-2 px-3 py-1 rounded-full text-xs font-semibold shadow border-2 transition-colors duration-150 focus:outline-none",
  {
    "text-white border-transparent": isActive,
    "text-gray-400 border-dashed border-gray-300 opacity-60": !isActive,
  }
);
src/components/results/student/performance-distribution-chart.tsx (2)

60-60: Simplify the empty array check

The condition can be simplified since empty arrays are already falsy in the first check.

-if (!results || results.length === 0) return [];
+if (!results?.length) return [];

70-78: Clarify the special handling for 100% scores

The logic for handling perfect scores could be more explicit with a comment or clearer structure.

 results.forEach((result) => {
   const scorePercentage = result.percentage;
+  // Place perfect scores (100%) in the last bin
   if (scorePercentage === 100) {
     bins[bins.length - 1].count++;
   } else {
     const binIndex = Math.floor(scorePercentage / groupSize);
     if (binIndex >= 0 && binIndex < bins.length) {
       bins[binIndex].count++;
     }
   }
 });
src/app/(test)/teacher-results/page.tsx (2)

630-639: Extract complex question stats mapping logic

The mapping logic for question statistics is complex and could be extracted for better readability and testability.

Extract to a utility function:

const mapQuestionStatsToChartData = (questionStats: QuestionStat[]) => {
  return questionStats.map((q, i) => {
    const correctCount = Math.round(
      (q.correctPercentage / 100) * q.attemptedCount
    );
    return {
      question: `Q${i + 1}`,
      correct: correctCount,
      wrong: q.attemptedCount - correctCount,
    };
  });
};

// Usage:
questionStats={mapQuestionStatsToChartData(testStatistics.questionStats)}

575-591: Enhance accessibility for layout toggle buttons

The toggle buttons could benefit from better accessibility attributes.

Add role and aria-label attributes:

 <div className="inline-flex rounded-md shadow-sm bg-muted border border-border">
   <button
     type="button"
-    aria-label="Vertical layout"
+    aria-label="Switch to vertical layout"
+    aria-pressed={chartsStacked}
+    role="radio"
     className={`px-2 py-1 flex items-center justify-center rounded-l-md focus:outline-none transition-colors duration-150 ${chartsStacked ? "bg-primary text-primary-foreground" : "bg-transparent text-muted-foreground hover:bg-accent"} border-r border-border`}
     onClick={() => setChartsStacked(true)}
   >
src/components/results/teacher/question-wise-bar-chart.tsx (3)

27-42: Consider enhancing the theme colors hook for better chart integration.

The hook is well-implemented and SSR-safe, but it only retrieves the foreground color. Consider expanding it to include other theme colors that could enhance the chart's visual integration.

 const useThemeColors = () => {
-  const [colors, setColors] = useState({ foreground: "#222" });
+  const [colors, setColors] = useState({ 
+    foreground: "#222",
+    background: "#fff",
+    muted: "#666",
+    accent: "#0066cc"
+  });

   useEffect(() => {
     const updateColors = () => {
       const style = getComputedStyle(document.documentElement);
       setColors({
         foreground: style.getPropertyValue("--foreground") || "#222",
+        background: style.getPropertyValue("--background") || "#fff",
+        muted: style.getPropertyValue("--muted") || "#666",
+        accent: style.getPropertyValue("--accent") || "#0066cc",
       });
     };
     updateColors();
   }, []);

   return colors;
 };

86-100: Remove redundant Chart.js configuration.

The stacked: false configuration is redundant since bars are not stacked by default in Chart.js.

 scales: {
   x: {
-    stacked: false,
     ticks: {
       color: themeColors.foreground,
     },
   },
   y: {
     beginAtZero: true,
-    stacked: false,
     ticks: {
       color: themeColors.foreground,
     },
   },
 },

102-102: Consider responsive height instead of fixed height.

The fixed height of 220px might not work well across different screen sizes and could cause layout issues.

-      height={220}
+      height="220px"

Or better yet, use CSS classes for responsive height:

-      height={220}
+      className="h-[220px] sm:h-[280px] lg:h-[320px]"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24c1382 and 1aa2c0e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • src/app/(test)/student-results/page.tsx (2 hunks)
  • src/app/(test)/teacher-results/page.tsx (7 hunks)
  • src/components/results/common/detailed-test-result.tsx (3 hunks)
  • src/components/results/common/marks-pie-chart.tsx (1 hunks)
  • src/components/results/common/question-bar-chart.tsx (1 hunks)
  • src/components/results/student/performance-distribution-chart.tsx (1 hunks)
  • src/components/results/student/performance-trends-chart.tsx (1 hunks)
  • src/components/results/teacher/index.ts (0 hunks)
  • src/components/results/teacher/marks-frequency-bar-chart.tsx (1 hunks)
  • src/components/results/teacher/question-wise-bar-chart.tsx (1 hunks)
  • src/components/results/teacher/student-performance-pie-chart.tsx (1 hunks)
  • src/lib/results-api.ts (16 hunks)
💤 Files with no reviewable changes (1)
  • src/components/results/teacher/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/components/results/teacher/marks-frequency-bar-chart.tsx (2)
src/components/ui/tooltip.tsx (1)
  • Tooltip (61-61)
src/components/ui/card.tsx (4)
  • Card (85-85)
  • CardHeader (86-86)
  • CardTitle (88-88)
  • CardContent (91-91)
src/components/results/student/performance-trends-chart.tsx (1)
src/components/ui/card.tsx (4)
  • Card (85-85)
  • CardHeader (86-86)
  • CardTitle (88-88)
  • CardContent (91-91)
src/components/results/teacher/question-wise-bar-chart.tsx (2)
src/components/ui/tooltip.tsx (1)
  • Tooltip (61-61)
src/components/ui/card.tsx (4)
  • Card (85-85)
  • CardHeader (86-86)
  • CardTitle (88-88)
  • CardContent (91-91)
src/lib/results-api.ts (1)
src/components/results/teacher/types.ts (2)
  • StudentTestResult (39-54)
  • QuestionStat (76-86)
🔇 Additional comments (14)
src/components/results/common/marks-pie-chart.tsx (1)

24-46: LGTM! Clean chart implementation with good responsive design.

The Chart.js configuration is well-structured with appropriate responsive settings and tooltip functionality. The color scheme provides good visual distinction between categories.

src/components/results/common/question-bar-chart.tsx (1)

47-66: Excellent chart configuration with proper stacking and grid styling.

The Chart.js options are well-configured with appropriate scales, legend positioning, and visual styling. The stacked configuration correctly represents the question-wise results.

src/lib/results-api.ts (3)

699-891: Well-structured expansion with consistent data patterns.

The addition of two new courses ("Computer Networks" and "Software Engineering") follows the existing data structure patterns correctly. The test data includes realistic score distributions and appropriate difficulty progressions.


1441-1504: Good expansion of mock data for better chart visualization.

Increasing the student count from 15 to 30 and questions from 10 to 20 provides more realistic data for testing the new chart components. The addition of the totalStudents field addresses a missing data point.


169-172: Verify average score calculation for Data Structures course.

The updated course shows averageScore: 81.1 and highestScore: 98, but I need to verify this matches the actual test scores in the expanded data.

Let me verify the average score calculation:

#!/bin/bash
# Description: Calculate the actual average score from the test results to verify accuracy

# Extract test scores for course-1 and calculate average
echo "Calculating average score for Data Structures course..."
echo "Test scores: 78, 85, 72, 95, 88, 81, 98, 67"
python3 -c "
scores = [78, 85, 72, 95, 88, 81, 98, 67]
average = sum(scores) / len(scores)
print(f'Calculated average: {average:.1f}')
print(f'Declared average: 81.1')
print(f'Match: {abs(average - 81.1) < 0.1}')
"
src/components/results/teacher/student-performance-pie-chart.tsx (3)

12-25: Clean and well-documented performance categorization logic.

The performance thresholds are clearly defined and the distribution function is pure and easily testable. The categorization ranges are sensible for academic performance evaluation.


58-64: Excellent SSR-safe CSS variable handling.

The implementation properly handles server-side rendering by checking for window availability before accessing getComputedStyle. This prevents SSR hydration issues.


71-81: Good use of Card components for consistent UI structure.

The component properly uses the Card UI components to maintain consistent styling and layout across the application.

src/components/results/teacher/marks-frequency-bar-chart.tsx (2)

28-36: Solid binning logic with proper boundary handling.

The binning function correctly handles edge cases by using Math.min to prevent index out-of-bounds when scores equal the maximum value (100).


109-118: Well-structured chart layout with proper responsive design.

The component uses the Card UI pattern consistently and includes appropriate height configuration for the chart container.

src/app/(test)/student-results/page.tsx (1)

15-15: Chart integration looks good!

The StudentPerformanceTrendsChart is properly imported and integrated into the student results page with appropriate data passing.

Also applies to: 174-178

src/components/results/common/detailed-test-result.tsx (1)

139-177: Good refactoring for improved readability

The explicit isCorrect and isSelected boolean variables make the conditional logic much clearer and easier to understand.

src/components/results/teacher/question-wise-bar-chart.tsx (2)

1-13: LGTM! Proper Chart.js setup.

The imports and Chart.js registration are correctly configured for a bar chart implementation.


15-25: Well-defined interfaces with clear naming strategy.

The interfaces appropriately define the component's data structure and props. The naming strategy to avoid conflicts is a good practice.

Comment on lines +66 to +68
const sortedTests = [...course.testResults].sort((a, b) =>
a.completedAt.localeCompare(b.completedAt),
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null/undefined handling for date sorting

The sorting logic doesn't handle cases where completedAt might be null or undefined.

Add defensive checks:

-const sortedTests = [...course.testResults].sort((a, b) =>
-  a.completedAt.localeCompare(b.completedAt),
-);
+const sortedTests = [...course.testResults].sort((a, b) => {
+  if (!a.completedAt || !b.completedAt) return 0;
+  return a.completedAt.localeCompare(b.completedAt);
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sortedTests = [...course.testResults].sort((a, b) =>
a.completedAt.localeCompare(b.completedAt),
);
const sortedTests = [...course.testResults].sort((a, b) => {
if (!a.completedAt || !b.completedAt) return 0;
return a.completedAt.localeCompare(b.completedAt);
});
🤖 Prompt for AI Agents
In src/components/results/student/performance-trends-chart.tsx around lines 66
to 68, the sorting logic for testResults does not handle cases where completedAt
might be null or undefined. Update the sort comparator to first check if either
a.completedAt or b.completedAt is null or undefined, and handle those cases by
placing null/undefined dates either at the start or end consistently. Then, if
both dates are valid, proceed with localeCompare as before.

Comment on lines +49 to +53
// Only show first 15 questions, rest scrollable in horizontal mode; for vertical, scroll if >30
const scrollThreshold = horizontalMode ? 15 : 30;
const isScrollable = questionStats.length > scrollThreshold;
const visibleStats =
isScrollable && horizontalMode ? questionStats.slice(0, 15) : questionStats;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clarify the scrolling implementation.

The scrolling logic is incomplete. The code mentions "rest scrollable" but doesn't implement actual scrolling functionality. The vertical mode doesn't limit questions despite the comment suggesting it should scroll if >30 questions.

Consider either:

  1. Implementing actual horizontal scrolling for the chart container
  2. Simplifying the logic to just limit the number of displayed questions
  3. Adding a "Show More" button to load additional questions
- // Only show first 15 questions, rest scrollable in horizontal mode; for vertical, scroll if >30
- const scrollThreshold = horizontalMode ? 15 : 30;
- const isScrollable = questionStats.length > scrollThreshold;
- const visibleStats =
-   isScrollable && horizontalMode ? questionStats.slice(0, 15) : questionStats;
+ // Limit questions to prevent overcrowding
+ const maxQuestions = horizontalMode ? 15 : 30;
+ const visibleStats = questionStats.slice(0, maxQuestions);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Only show first 15 questions, rest scrollable in horizontal mode; for vertical, scroll if >30
const scrollThreshold = horizontalMode ? 15 : 30;
const isScrollable = questionStats.length > scrollThreshold;
const visibleStats =
isScrollable && horizontalMode ? questionStats.slice(0, 15) : questionStats;
// Limit questions to prevent overcrowding
const maxQuestions = horizontalMode ? 15 : 30;
const visibleStats = questionStats.slice(0, maxQuestions);
🤖 Prompt for AI Agents
In src/components/results/teacher/question-wise-bar-chart.tsx around lines 49 to
53, the current code sets thresholds for scrolling but does not implement actual
scrolling or question limiting for vertical mode as the comment suggests. To fix
this, either add CSS or container logic to enable horizontal scrolling when
horizontalMode is true and question count exceeds 15, or simplify by limiting
the number of displayed questions in both modes. Alternatively, implement a
"Show More" button to load additional questions dynamically. Ensure the UI
behavior matches the intended scrolling or question limiting described in the
comments.

Comment on lines +44 to +53
export const QuestionWiseBarChart: React.FC<QuestionWiseBarChartProps> = ({
questionStats,
horizontalMode = false,
}) => {
const themeColors = useThemeColors();
// Only show first 15 questions, rest scrollable in horizontal mode; for vertical, scroll if >30
const scrollThreshold = horizontalMode ? 15 : 30;
const isScrollable = questionStats.length > scrollThreshold;
const visibleStats =
isScrollable && horizontalMode ? questionStats.slice(0, 15) : questionStats;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for edge cases.

The component doesn't handle edge cases like empty data or malformed question stats, which could cause runtime errors.

 export const QuestionWiseBarChart: React.FC<QuestionWiseBarChartProps> = ({
   questionStats,
   horizontalMode = false,
 }) => {
+  // Handle empty data
+  if (!questionStats || questionStats.length === 0) {
+    return (
+      <Card className="w-full h-full">
+        <CardHeader>
+          <CardTitle>Question-wise Correct/Wrong</CardTitle>
+        </CardHeader>
+        <CardContent>
+          <div className="flex items-center justify-center h-[220px] text-muted-foreground">
+            No question data available
+          </div>
+        </CardContent>
+      </Card>
+    );
+  }
+
   const themeColors = useThemeColors();
🤖 Prompt for AI Agents
In src/components/results/teacher/question-wise-bar-chart.tsx around lines 44 to
53, the component lacks error handling for cases when questionStats is empty or
malformed. Add checks to verify questionStats is a valid array with expected
structure before processing. If invalid or empty, render a fallback UI or return
null to prevent runtime errors. This ensures the component handles edge cases
gracefully.

Comment on lines +57 to +71
labels: visibleStats.map((q, i) => `Q${i + 1}`),
datasets: [
{
label: "Correct",
data: visibleStats.map((q) => q.correct),
backgroundColor: "#22c55e",
borderRadius: 4,
},
{
label: "Wrong",
data: visibleStats.map((q) => q.wrong),
backgroundColor: "#ef4444",
borderRadius: 4,
},
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Address color inconsistency and consider question labeling.

Two concerns with the chart data configuration:

  1. Color inconsistency: Hard-coded colors (#22c55e, #ef4444) are used for bars while theme colors are used elsewhere. This breaks theme consistency.

  2. Question labeling: Using Q${i + 1} instead of actual question text may reduce readability for users who want to identify specific questions.

 data={{
   labels: visibleStats.map((q, i) => `Q${i + 1}`),
   datasets: [
     {
       label: "Correct",
       data: visibleStats.map((q) => q.correct),
-      backgroundColor: "#22c55e",
+      backgroundColor: "hsl(var(--success))",
       borderRadius: 4,
     },
     {
       label: "Wrong",
       data: visibleStats.map((q) => q.wrong),
-      backgroundColor: "#ef4444",
+      backgroundColor: "hsl(var(--destructive))",
       borderRadius: 4,
     },
   ],
 }}

Consider adding a tooltip to show the actual question text when hovering over bars.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
labels: visibleStats.map((q, i) => `Q${i + 1}`),
datasets: [
{
label: "Correct",
data: visibleStats.map((q) => q.correct),
backgroundColor: "#22c55e",
borderRadius: 4,
},
{
label: "Wrong",
data: visibleStats.map((q) => q.wrong),
backgroundColor: "#ef4444",
borderRadius: 4,
},
],
labels: visibleStats.map((q, i) => `Q${i + 1}`),
datasets: [
{
label: "Correct",
data: visibleStats.map((q) => q.correct),
backgroundColor: "hsl(var(--success))",
borderRadius: 4,
},
{
label: "Wrong",
data: visibleStats.map((q) => q.wrong),
backgroundColor: "hsl(var(--destructive))",
borderRadius: 4,
},
],
🤖 Prompt for AI Agents
In src/components/results/teacher/question-wise-bar-chart.tsx around lines 57 to
71, replace the hard-coded bar colors (#22c55e and #ef4444) with colors derived
from the current theme to maintain visual consistency. Also, update the labels
array to use the actual question text from visibleStats instead of generic "Q1",
"Q2", etc., to improve readability. Additionally, implement a tooltip feature on
the chart bars that displays the full question text when hovered over, enhancing
user understanding.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
src/components/results/common/detailed-test-result.tsx (1)

139-203: Fix the parsing error and incomplete refactoring in MCQ rendering logic.

The code contains both the new refactored version (lines 139-166) and remnants of the old version (lines 167-203), causing a parsing error. The JSX structure is malformed with duplicate mapping logic.

Apply this diff to remove the old code and fix the parsing error:

             {question.options?.map((option, index) => {
               const isCorrect = option.isCorrect;
               const isSelected = question.studentAnswer === option.id;
               return (
                 <div
                   key={option.id}
                   className={`p-2 rounded border ${
                     isCorrect
                       ? "bg-emerald-100 border-emerald-300 dark:bg-green-900/20 dark:border-green-800"
                       : isSelected
                         ? "bg-red-100 border-red-300 dark:bg-red-900/20 dark:border-red-800"
                         : "bg-slate-100 border-slate-300 dark:bg-gray-900/20 dark:border-gray-800"
                   }`}
                 >
                   <div className="flex items-center gap-2">
                     <span className="font-medium">
                       {String.fromCharCode(65 + index)}.
                     </span>
                     <span>{option.text}</span>
                     {isCorrect && (
                       <Badge
                         variant="secondary"
                         className="ml-auto bg-emerald-100 border border-emerald-300 text-emerald-800"
                       >
                         Correct
                       </Badge>
                     )}
                     {isSelected && !isCorrect && (
-            {question.options?.map((option, index) => (
-              <div
-                key={option.id}
-                className={`p-2 rounded border ${
-                  option.isCorrect
-                    ? "bg-emerald-100 border-emerald-300 dark:bg-green-900/20 dark:border-green-800"
-                    : question.studentAnswer === option.id
-                      ? "bg-red-100 border-red-300 dark:bg-red-900/20 dark:border-red-800"
-                      : "bg-slate-100 border-slate-300 dark:bg-gray-900/20 dark:border-gray-800"
-                }`}
-              >
-                <div className="flex items-center gap-2">
-                  <span className="font-medium">
-                    {String.fromCharCode(65 + index)}.
-                  </span>
-                  <span>{option.text}</span>
-                  {option.isCorrect && (
-                    <Badge
-                      variant="secondary"
-                      className="ml-auto bg-emerald-100 border border-emerald-300 text-emerald-800"
-                    >
-                      Correct
-                    </Badge>
-                  )}
-                  {question.studentAnswer === option.id &&
-                    !option.isCorrect && (
-                      <Badge
-                        variant="secondary"
-                        className="ml-auto bg-red-100 border border-red-300 text-red-800"
-                      >
-                        Your Answer
-                      </Badge>
-                    )}
+                       <Badge
+                         variant="secondary"
+                         className="ml-auto bg-red-100 border border-red-300 text-red-800"
+                       >
+                         Your Answer
+                       </Badge>
+                     )}
                   </div>
-                </div>
-              );
-            })}
+                 </div>
+               );
+             })}
♻️ Duplicate comments (2)
src/app/(test)/teacher-results/page.tsx (2)

34-34: Replace toast import with project's useToast hook.

Per project conventions, use the useToast hook from src/hooks/use-toast.ts instead of importing toast directly from sonner.

-import { toast } from "sonner";
+import { useToast } from "@/hooks/use-toast";

Then update the toast usage in the component to use the hook pattern.


621-621: Verify MarksFrequencyBarChart component accepts totalMarks prop.

The MarksFrequencyBarChart component is receiving a totalMarks prop that may not be defined in its interface.

#!/bin/bash
# Check the MarksFrequencyBarChart component interface
ast-grep --pattern 'interface $_ {
  $$$
}'  src/components/results/teacher/marks-frequency-bar-chart.tsx

# Also check the component props definition
ast-grep --pattern 'const MarksFrequencyBarChart: React.FC<$_> = ($_) => {
  $$$
}' src/components/results/teacher/marks-frequency-bar-chart.tsx
🧹 Nitpick comments (1)
src/components/results/common/detailed-test-result.tsx (1)

401-401: Consider removing the comment or implementing the mentioned change.

The comment mentions removing charts for students, but no actual code changes accompany this comment. If charts were indeed removed, consider removing this comment as it may cause confusion.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1aa2c0e and a97d660.

📒 Files selected for processing (2)
  • src/app/(test)/teacher-results/page.tsx (7 hunks)
  • src/components/results/common/detailed-test-result.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{js,jsx,ts,tsx}`: Use shadcn/ui components for UI elements. Use Tailwind CSS for styling. Ensure all UI components are responsive and work well on both desktop and mobile dev...

**/*.{js,jsx,ts,tsx}: Use shadcn/ui components for UI elements.
Use Tailwind CSS for styling.
Ensure all UI components are responsive and work well on both desktop and mobile devices.
Ensure all components work well with both light and dark mode.
Where possible, use useQuery and useMutation from @tanstack/react-query for API calls.
Use Server Components in Next.js where possible.
Strictly use TypeScript for type safety.

  • src/app/(test)/teacher-results/page.tsx
  • src/components/results/common/detailed-test-result.tsx
🧠 Learnings (1)
src/app/(test)/teacher-results/page.tsx (1)
Learnt from: CR
PR: evalify/evalify-frontend#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-24T12:27:53.281Z
Learning: For toast messages, always use the useToast hook from src/hooks/use-toast.ts instead of native alert or error messages to provide a better user experience.
🪛 Biome (1.9.4)
src/components/results/common/detailed-test-result.tsx

[error] 203-203: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 203-203: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)

🔇 Additional comments (6)
src/components/results/common/detailed-test-result.tsx (1)

336-339: LGTM! Improved code formatting.

The multi-line formatting of the getScoreTextColor function call improves readability.

src/app/(test)/teacher-results/page.tsx (5)

30-31: LGTM! Well-organized chart component imports.

The new imports for layout icons and chart components are properly organized and follow the project's import conventions.

Also applies to: 35-39


63-63: LGTM! Clear and descriptive state variable.

The chartsStacked state variable clearly indicates its purpose for the layout toggle functionality.


500-505: LGTM! Enhanced score displays with absolute marks.

The addition of absolute marks alongside percentages provides more context and improves the user experience. The calculations are correct and the formatting is consistent.

Also applies to: 516-519, 530-540, 553-558


565-645: LGTM! Well-implemented performance graphs section.

The new performance graphs section with layout toggle functionality is well-structured:

  • Clear section heading and layout controls
  • Responsive grid/flex layout based on toggle state
  • Proper chart component integration
  • Good use of conditional styling for different layouts

684-707: LGTM! Improved student detail view organization.

Moving the charts to the top of the student detail view improves visibility and user experience. The grid layout for the charts and clear section headings enhance the overall presentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants