'use client'; import React, { useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { DataTable } from '@/components/data-table'; import { ConfirmDialog } from '@/components/confirm-dialog'; import { processGrade, calculateWeightedGrade, getStatistics, type GradingConfig, type GradeOutput, } from '@/lib/grading-engine'; import { useApiGet, useApiMutation } from '@/lib/hooks/useApi'; export interface GradingPanelProps { assessmentId: string; studentId?: string; // if provided, show single student; otherwise show class config: GradingConfig; onGradeSaved?: (grade: any) => void; } export function GradingPanel({ assessmentId, studentId, config, onGradeSaved, }: GradingPanelProps) { const [selectedStudent, setSelectedStudent] = useState(studentId || null); const [showConfirm, setShowConfirm] = useState(false); const [pendingGrade, setPendingGrade] = useState(null); // Fetch assessment and results const { data: assessment } = useApiGet( `/api/v1/assessments/${assessmentId}` ); const { data: results, mutate: refetchResults } = useApiGet( studentId ? `/api/v1/assessments/${assessmentId}/results?studentId=${studentId}` : `/api/v1/assessments/${assessmentId}/results` ); const { mutate: saveGrade, isLoading: isSaving } = useApiMutation( `/api/v1/assessments/${assessmentId}/grades`, { method: 'POST', onSuccess: () => { setShowConfirm(false); setPendingGrade(null); refetchResults(); onGradeSaved?.(pendingGrade); }, } ); const handleGradeChange = (grade: number | string, studentId: string) => { setPendingGrade({ studentId, score: grade }); setShowConfirm(true); }; const handleConfirmGrade = async () => { await saveGrade(pendingGrade); }; // Statistics const stats = results?.length ? getStatistics( results.map((r: any) => r.score), config ) : null; return (
{/* Assessment Header */} {assessment && ( {assessment.title} {assessment.type} ยท Max Score: {assessment.maxScore} )} Students Analytics {studentId && Details} {/* Student List */} {results && ( `${row.student?.firstName} ${row.student?.lastName}`, }, { key: 'score' as any, label: 'Score', sortable: true, render: (_, row) => ( handleGradeChange(e.target.value, row.studentId)} className="w-20" /> ), }, { key: 'feedback' as any, label: 'Feedback', render: (_, row) => { const processed = processGrade( { score: row.score, studentId: row.studentId, assessmentId: row.assessmentId, timestamp: new Date(), }, config ); return ( {processed.isPassing ? 'Pass' : 'Fail'} ); }, }, ]} data={results} isLoading={!results} /> )} {/* Analytics */} {stats && (
Average
{stats.average.toFixed(1)}
Median
{stats.median.toFixed(1)}
Passing
{stats.passingPercentage.toFixed(0)}%
Std Dev
{stats.stdDev.toFixed(1)}
)}
{/* Single Student Details */} {studentId && results && ( {results[0] && ( {results[0].student?.firstName} {results[0].student?.lastName}
handleGradeChange(e.target.value, results[0].studentId)} />
)}
)}
{/* Confirm Dialog */} setShowConfirm(false)} isLoading={isSaving} confirmText="Save Grade" />
); }