Spaces:
Running
Running
"use client" | |
import { useState } from "react" | |
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" | |
import { Button } from "@/components/ui/button" | |
import { Badge } from "@/components/ui/badge" | |
import { Progress } from "@/components/ui/progress" | |
import { CategoryEvaluation } from "@/components/category-evaluation" | |
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs" | |
import type { CategoryScore } from "@/components/ai-evaluation-dashboard" | |
import { CheckCircle, Circle, ArrowRight, ArrowLeft } from "lucide-react" | |
interface Category { | |
id: string | |
name: string | |
type: "capability" | "risk" | |
description: string | |
detailedGuidance?: string | |
} | |
interface EvaluationFormProps { | |
categories: Category[] | |
selectedCategories: string[] | |
categoryScores: Record<string, CategoryScore> | |
onScoreUpdate: (categoryId: string, score: CategoryScore) => void | |
onSaveDetailed?: (categoryId: string, data: any) => void | |
onComplete: () => void | |
} | |
export function EvaluationForm({ | |
categories, | |
selectedCategories, | |
categoryScores, | |
onScoreUpdate, | |
onSaveDetailed, | |
onComplete, | |
}: EvaluationFormProps) { | |
const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0) | |
const selectedCategoryObjects = categories.filter((c) => selectedCategories.includes(c.id)) | |
const currentCategory = selectedCategoryObjects[currentCategoryIndex] | |
const completedCount = Object.keys(categoryScores).length | |
const totalCount = selectedCategories.length | |
const progress = totalCount > 0 ? (completedCount / totalCount) * 100 : 0 | |
const handleNext = () => { | |
if (currentCategoryIndex < selectedCategoryObjects.length - 1) { | |
setCurrentCategoryIndex((prev) => prev + 1) | |
} | |
} | |
const handlePrevious = () => { | |
if (currentCategoryIndex > 0) { | |
setCurrentCategoryIndex((prev) => prev - 1) | |
} | |
} | |
const handleCategorySelect = (categoryId: string) => { | |
const index = selectedCategoryObjects.findIndex((c) => c.id === categoryId) | |
if (index !== -1) { | |
setCurrentCategoryIndex(index) | |
} | |
} | |
const isCurrentCategoryCompleted = currentCategory && categoryScores[currentCategory.id] | |
const allCompleted = completedCount === totalCount | |
return ( | |
<div className="space-y-6 h-full min-h-0 flex flex-col"> | |
{/* Progress Header */} | |
<Card> | |
<CardHeader> | |
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3"> | |
<div> | |
<CardTitle className="text-lg sm:text-xl font-heading">Detailed Evaluation</CardTitle> | |
<CardDescription>Complete the evaluation for each selected category</CardDescription> | |
</div> | |
<div className="text-left sm:text-right w-full sm:w-auto"> | |
<div className="flex items-center gap-2 mb-2"> | |
<span className="text-sm font-medium">Progress:</span> | |
<Badge variant="secondary"> | |
{completedCount}/{totalCount} | |
</Badge> | |
</div> | |
<Progress value={progress} className="w-full sm:w-32" /> | |
</div> | |
</div> | |
</CardHeader> | |
</Card> | |
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 flex-1 min-h-0"> | |
{/* Category Navigation Sidebar as Tabs */} | |
<Card className="lg:col-span-1 h-full"> | |
<CardHeader> | |
<CardTitle className="text-lg">Categories</CardTitle> | |
</CardHeader> | |
<CardContent className="h-full min-h-0 flex flex-col"> | |
<Tabs defaultValue={selectedCategoryObjects[0]?.id || ""} orientation="vertical" value={selectedCategoryObjects[currentCategoryIndex]?.id || ""} onValueChange={(val) => handleCategorySelect(val)}> | |
<TabsList className="flex-col w-full flex-1 min-h-0 overflow-auto"> | |
{selectedCategoryObjects.map((category, index) => { | |
const isCompleted = categoryScores[category.id] | |
return ( | |
<TabsTrigger | |
key={category.id} | |
value={category.id} | |
className={`w-full text-left p-3 rounded-md ${ | |
category.type === "capability" | |
? "bg-purple-50 data-[state=active]:bg-purple-200 data-[state=active]:border-l-4 data-[state=active]:border-l-purple-600" | |
: "bg-red-50 data-[state=active]:bg-red-200 data-[state=active]:border-l-4 data-[state=active]:border-l-red-600" | |
}`} | |
> | |
<div className="flex items-start gap-3 w-full"> | |
<div className="flex items-center"> | |
{isCompleted ? <CheckCircle className="h-4 w-4 text-green-600" /> : <Circle className="h-4 w-4 text-muted-foreground" />} | |
</div> | |
<div className="flex-1 min-w-0"> | |
<div className="flex flex-col"> | |
<span className="text-sm font-medium leading-snug truncate">{category.name}</span> | |
<span | |
className={`inline-block text-xs font-medium mt-1 px-2 py-0.5 rounded-full w-max ${ | |
category.type === "capability" ? "bg-purple-100 text-purple-800" : "bg-red-100 text-red-800" | |
}`} | |
> | |
{category.type} | |
</span> | |
</div> | |
</div> | |
{isCompleted && ( | |
<div className="flex items-center"> | |
<span className="text-xs text-muted-foreground"> | |
{categoryScores[category.id].totalScore}/{categoryScores[category.id].totalApplicable || categoryScores[category.id].totalQuestions || 0} | |
</span> | |
</div> | |
)} | |
</div> | |
</TabsTrigger> | |
) | |
})} | |
</TabsList> | |
</Tabs> | |
</CardContent> | |
</Card> | |
{/* Current Category Evaluation */} | |
<div className="lg:col-span-3"> | |
{currentCategory && ( | |
<CategoryEvaluation | |
key={currentCategory.id} | |
category={currentCategory} | |
score={categoryScores[currentCategory.id]} | |
onScoreUpdate={(score) => onScoreUpdate(currentCategory.id, score)} | |
onSaveDetailed={(catId, data) => onSaveDetailed?.(catId, data)} | |
/> | |
)} | |
{/* Navigation Controls */} | |
<Card className="mt-6"> | |
<CardContent className="pt-6"> | |
<div className="flex items-center justify-between"> | |
<Button variant="outline" onClick={handlePrevious} disabled={currentCategoryIndex === 0}> | |
<ArrowLeft className="h-4 w-4 mr-2" /> | |
Previous | |
</Button> | |
<div className="text-center"> | |
<p className="text-sm text-muted-foreground"> | |
Category {currentCategoryIndex + 1} of {totalCount} | |
</p> | |
{currentCategory && <p className="font-medium">{currentCategory.name}</p>} | |
</div> | |
{currentCategoryIndex < selectedCategoryObjects.length - 1 ? ( | |
<Button onClick={handleNext} disabled={!isCurrentCategoryCompleted}> | |
Next | |
<ArrowRight className="h-4 w-4 ml-2" /> | |
</Button> | |
) : ( | |
<Button onClick={onComplete} disabled={!allCompleted} className="bg-green-600 hover:bg-green-700"> | |
View Results | |
<ArrowRight className="h-4 w-4 ml-2" /> | |
</Button> | |
)} | |
</div> | |
</CardContent> | |
</Card> | |
</div> | |
</div> | |
</div> | |
) | |
} | |