Spaces:
Running
Running
| "use client" | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" | |
| import { Badge } from "@/components/ui/badge" | |
| import { Button } from "@/components/ui/button" | |
| import type { SystemInfo, CategoryScore } from "@/components/ai-evaluation-dashboard" | |
| import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from "recharts" | |
| import { Download, AlertTriangle, CheckCircle, AlertCircle, XCircle, Globe, Calendar, User, Building, Cpu, MonitorSpeaker, Database, Hash, Tags, Clock, Activity, Headphones, Settings } from "lucide-react" | |
| interface Category { | |
| id: string | |
| name: string | |
| type: "capability" | "risk" | |
| } | |
| interface ResultsDashboardProps { | |
| systemInfo: SystemInfo | null | |
| categories: Category[] | |
| selectedCategories: string[] | |
| categoryScores: Record<string, CategoryScore> | |
| excludedCategoryReasons?: Record<string, string> | |
| } | |
| const STATUS_COLORS = { | |
| strong: "#22c55e", | |
| adequate: "#3b82f6", | |
| weak: "#f59e0b", | |
| insufficient: "#ef4444", | |
| } | |
| const STATUS_ICONS = { | |
| strong: CheckCircle, | |
| adequate: CheckCircle, | |
| weak: AlertCircle, | |
| insufficient: XCircle, | |
| } | |
| export function ResultsDashboard({ | |
| systemInfo, | |
| categories, | |
| selectedCategories, | |
| categoryScores, | |
| excludedCategoryReasons, | |
| }: ResultsDashboardProps) { | |
| const safeCategories = categories || [] | |
| const safeSelectedCategories = selectedCategories || [] | |
| const safeCategoryScores = categoryScores || {} | |
| console.log("[v0] ResultsDashboard rendering with:", { | |
| systemInfo, | |
| categoriesCount: safeCategories.length, | |
| selectedCount: safeSelectedCategories.length, | |
| scoresCount: Object.keys(safeCategoryScores).length, | |
| scores: safeCategoryScores, | |
| }) | |
| const selectedCategoryObjects = safeCategories.filter((c) => safeSelectedCategories.includes(c.id)) | |
| const getStatusCounts = () => { | |
| const scores = Object.values(safeCategoryScores) | |
| return { | |
| strong: scores.filter((s) => s.status === "strong").length, | |
| adequate: scores.filter((s) => s.status === "adequate").length, | |
| weak: scores.filter((s) => s.status === "weak").length, | |
| insufficient: scores.filter((s) => s.status === "insufficient").length, | |
| } | |
| } | |
| const getCapabilityRiskBreakdown = () => { | |
| const capability = selectedCategoryObjects.filter((c) => c.type === "capability") | |
| const risk = selectedCategoryObjects.filter((c) => c.type === "risk") | |
| const capabilityScores = capability.map((c) => safeCategoryScores[c.id]).filter(Boolean) | |
| const riskScores = risk.map((c) => safeCategoryScores[c.id]).filter(Boolean) | |
| const avgCapability = | |
| capabilityScores.length > 0 | |
| ? capabilityScores.reduce((sum, s) => sum + (s.totalScore || 0), 0) / capabilityScores.length | |
| : 0 | |
| const avgRisk = | |
| riskScores.length > 0 ? riskScores.reduce((sum, s) => sum + (s.totalScore || 0), 0) / riskScores.length : 0 | |
| const safeCapability = isNaN(avgCapability) || !isFinite(avgCapability) ? 0 : avgCapability | |
| const safeRisk = isNaN(avgRisk) || !isFinite(avgRisk) ? 0 : avgRisk | |
| console.log("[v0] Capability/Risk breakdown:", { | |
| capabilityScores: capabilityScores.length, | |
| riskScores: riskScores.length, | |
| avgCapability, | |
| avgRisk, | |
| safeCapability, | |
| safeRisk, | |
| }) | |
| return { | |
| capability: safeCapability, | |
| risk: safeRisk, | |
| } | |
| } | |
| const getChartData = () => { | |
| return selectedCategoryObjects | |
| .map((category) => { | |
| const score = safeCategoryScores[category.id] | |
| return { | |
| name: category.name.length > 20 ? category.name.substring(0, 20) + "..." : category.name, | |
| fullName: category.name, | |
| benchmarkScore: score?.benchmarkScore || 0, | |
| processScore: score?.processScore || 0, | |
| totalScore: score?.totalScore || 0, | |
| type: category.type, | |
| } | |
| }) | |
| .sort((a, b) => b.totalScore - a.totalScore) | |
| } | |
| const getPieData = () => { | |
| const counts = getStatusCounts() | |
| return [ | |
| { name: "Strong (12-15)", value: counts.strong, color: STATUS_COLORS.strong }, | |
| { name: "Adequate (8-11)", value: counts.adequate, color: STATUS_COLORS.adequate }, | |
| { name: "Weak (4-7)", value: counts.weak, color: STATUS_COLORS.weak }, | |
| { name: "Insufficient (0-3)", value: counts.insufficient, color: STATUS_COLORS.insufficient }, | |
| ].filter((item) => item.value > 0) | |
| } | |
| const statusCounts = getStatusCounts() | |
| const breakdown = getCapabilityRiskBreakdown() | |
| const chartData = getChartData() | |
| const pieData = getPieData() | |
| const totalEvaluated = Object.keys(safeCategoryScores).length | |
| const overallAverage = | |
| totalEvaluated > 0 | |
| ? Object.values(safeCategoryScores).reduce((sum, s) => sum + (s.totalScore || 0), 0) / totalEvaluated | |
| : 0 | |
| const safeOverallAverage = isNaN(overallAverage) || !isFinite(overallAverage) ? 0 : overallAverage | |
| console.log("[v0] Overall average calculation:", { | |
| totalEvaluated, | |
| overallAverage, | |
| safeOverallAverage, | |
| }) | |
| const safeToFixed = (value: number, digits = 1): string => { | |
| if (isNaN(value) || !isFinite(value)) { | |
| console.log("[v0] Warning: Invalid value for toFixed:", value) | |
| return "0.0" | |
| } | |
| return value.toFixed(digits) | |
| } | |
| const exportResults = () => { | |
| const results = { | |
| systemInfo, | |
| evaluationDate: new Date().toISOString(), | |
| summary: { | |
| totalCategories: safeSelectedCategories.length, | |
| evaluatedCategories: totalEvaluated, | |
| overallAverage: safeOverallAverage.toFixed(1), | |
| statusBreakdown: statusCounts, | |
| }, | |
| categoryResults: safeCategories.map((category) => ({ | |
| ...category, | |
| score: safeCategoryScores[category.id] || null, | |
| excludedReason: !safeSelectedCategories.includes(category.id) | |
| ? excludedCategoryReasons?.[category.id] || null | |
| : null, | |
| })), | |
| } | |
| const blob = new Blob([JSON.stringify(results, null, 2)], { type: "application/json" }) | |
| const url = URL.createObjectURL(blob) | |
| const a = document.createElement("a") | |
| a.href = url | |
| a.download = `ai-evaluation-${systemInfo?.name || "system"}-${new Date().toISOString().split("T")[0]}.json` | |
| a.click() | |
| URL.revokeObjectURL(url) | |
| } | |
| return ( | |
| <div className="space-y-6"> | |
| {/* System Overview */} | |
| <Card> | |
| <CardHeader> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <CardTitle className="font-heading">Evaluation Results</CardTitle> | |
| <CardDescription>Comprehensive assessment results for {systemInfo?.name}</CardDescription> | |
| </div> | |
| <Button onClick={exportResults} variant="outline"> | |
| <Download className="h-4 w-4 mr-2" /> | |
| Export Results | |
| </Button> | |
| </div> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> | |
| <div className="text-center"> | |
| <div className="text-2xl font-bold text-foreground">{totalEvaluated}</div> | |
| <div className="text-sm text-muted-foreground">Categories Evaluated</div> | |
| </div> | |
| <div className="text-center"> | |
| <div className="text-2xl font-bold text-foreground">{safeToFixed(safeOverallAverage)}/15</div> | |
| <div className="text-sm text-muted-foreground">Overall Average</div> | |
| </div> | |
| <div className="text-center"> | |
| <div className="text-2xl font-bold text-foreground">{safeToFixed(breakdown.capability)}/15</div> | |
| <div className="text-sm text-muted-foreground">Capability Average</div> | |
| </div> | |
| <div className="text-center"> | |
| <div className="text-2xl font-bold text-foreground">{safeToFixed(breakdown.risk)}/15</div> | |
| <div className="text-sm text-muted-foreground">Risk Average</div> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| {/* System Information (detailed) */} | |
| <Card className="mb-6"> | |
| <CardHeader> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <CardTitle className="font-heading flex items-center gap-2"> | |
| <Database className="h-5 w-5 text-blue-600" /> | |
| System Information | |
| </CardTitle> | |
| <CardDescription>A summary of metadata provided for the evaluated system</CardDescription> | |
| </div> | |
| </div> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-8"> | |
| {/* Left Column */} | |
| <div className="space-y-6"> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-blue-50 dark:bg-blue-950 rounded-lg"> | |
| <Cpu className="h-4 w-4 text-blue-600 dark:text-blue-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">System Name</div> | |
| <div className="text-lg font-semibold text-foreground mt-1"> | |
| {(systemInfo as any)?.systemName || systemInfo?.name || "—"} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-green-50 dark:bg-green-950 rounded-lg"> | |
| <Tags className="h-4 w-4 text-green-600 dark:text-green-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">System Version</div> | |
| <div className="text-lg font-semibold text-foreground mt-1"> | |
| {systemInfo?.version || (systemInfo as any)?.modelTag || "—"} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-purple-50 dark:bg-purple-950 rounded-lg"> | |
| <Building className="h-4 w-4 text-purple-600 dark:text-purple-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Provider</div> | |
| <div className="text-lg font-semibold text-foreground mt-1">{systemInfo?.provider || "—"}</div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-indigo-50 dark:bg-indigo-950 rounded-lg"> | |
| <Globe className="h-4 w-4 text-indigo-600 dark:text-indigo-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">URL</div> | |
| <div className="text-lg font-semibold text-foreground mt-1 break-all"> | |
| {systemInfo?.url ? ( | |
| <a href={systemInfo.url} target="_blank" rel="noreferrer" className="text-primary hover:text-primary/80 underline decoration-primary/30 hover:decoration-primary/60 transition-colors"> | |
| {systemInfo.url} | |
| </a> | |
| ) : ( | |
| "—" | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-orange-50 dark:bg-orange-950 rounded-lg"> | |
| <Settings className="h-4 w-4 text-orange-600 dark:text-orange-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Model Type</div> | |
| <div className="text-lg font-semibold text-foreground mt-1"> | |
| {systemInfo?.modelType ? ( | |
| <Badge variant="secondary" className="text-sm"> | |
| {systemInfo.modelType === "foundational" ? "Foundational Model" : | |
| systemInfo.modelType === "fine-tuned" ? "Fine-tuned Model" : | |
| "Not Applicable"} | |
| </Badge> | |
| ) : "—"} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Right Column */} | |
| <div className="space-y-6"> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-cyan-50 dark:bg-cyan-950 rounded-lg"> | |
| <Globe className="h-4 w-4 text-cyan-600 dark:text-cyan-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Deployment Context</div> | |
| <div className="text-lg font-semibold text-foreground mt-1"> | |
| {(systemInfo as any)?.deploymentContext || (systemInfo?.deploymentContexts && systemInfo.deploymentContexts.join(", ")) || "—"} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-emerald-50 dark:bg-emerald-950 rounded-lg"> | |
| <Activity className="h-4 w-4 text-emerald-600 dark:text-emerald-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Input Modalities</div> | |
| <div className="text-lg font-semibold text-foreground mt-1"> | |
| {systemInfo?.inputModalities?.length ? ( | |
| <div className="flex flex-wrap gap-1"> | |
| {systemInfo.inputModalities.map((modality: string, idx: number) => ( | |
| <Badge key={idx} variant="outline" className="text-xs"> | |
| {modality} | |
| </Badge> | |
| ))} | |
| </div> | |
| ) : "—"} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-rose-50 dark:bg-rose-950 rounded-lg"> | |
| <MonitorSpeaker className="h-4 w-4 text-rose-600 dark:text-rose-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Output Modalities</div> | |
| <div className="text-lg font-semibold text-foreground mt-1"> | |
| {systemInfo?.outputModalities?.length ? ( | |
| <div className="flex flex-wrap gap-1"> | |
| {systemInfo.outputModalities.map((modality: string, idx: number) => ( | |
| <Badge key={idx} variant="outline" className="text-xs"> | |
| {modality} | |
| </Badge> | |
| ))} | |
| </div> | |
| ) : "—"} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-amber-50 dark:bg-amber-950 rounded-lg"> | |
| <Clock className="h-4 w-4 text-amber-600 dark:text-amber-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Knowledge Cutoff</div> | |
| <div className="text-lg font-semibold text-foreground mt-1">{systemInfo?.knowledgeCutoff || "—"}</div> | |
| </div> | |
| </div> | |
| <div className="flex items-start gap-3"> | |
| <div className="p-2 bg-violet-50 dark:bg-violet-950 rounded-lg"> | |
| <User className="h-4 w-4 text-violet-600 dark:text-violet-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="text-sm font-medium text-muted-foreground">Evaluator</div> | |
| <div className="text-lg font-semibold text-foreground mt-1">{(systemInfo as any)?.evaluator || "—"}</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Bottom Row - Metadata Cards */} | |
| <div className="mt-8 grid grid-cols-1 md:grid-cols-3 gap-4"> | |
| <div className="p-4 bg-gradient-to-r from-blue-50 to-blue-100 dark:from-blue-950/50 dark:to-blue-900/50 rounded-lg border border-blue-200 dark:border-blue-800"> | |
| <div className="flex items-center gap-2 mb-2"> | |
| <Hash className="h-4 w-4 text-blue-600 dark:text-blue-400" /> | |
| <div className="text-sm font-medium text-blue-700 dark:text-blue-300">System ID</div> | |
| </div> | |
| <div className="font-mono text-sm text-blue-900 dark:text-blue-100 break-all">{(systemInfo as any)?.id || "—"}</div> | |
| </div> | |
| <div className="p-4 bg-gradient-to-r from-green-50 to-green-100 dark:from-green-950/50 dark:to-green-900/50 rounded-lg border border-green-200 dark:border-green-800"> | |
| <div className="flex items-center gap-2 mb-2"> | |
| <Tags className="h-4 w-4 text-green-600 dark:text-green-400" /> | |
| <div className="text-sm font-medium text-green-700 dark:text-green-300">System Types</div> | |
| </div> | |
| <div className="font-medium text-green-900 dark:text-green-100">{(systemInfo as any)?.systemTypes?.length ? (systemInfo as any).systemTypes.join(", ") : "—"}</div> | |
| </div> | |
| <div className="p-4 bg-gradient-to-r from-purple-50 to-purple-100 dark:from-purple-950/50 dark:to-purple-900/50 rounded-lg border border-purple-200 dark:border-purple-800"> | |
| <div className="flex items-center gap-2 mb-2"> | |
| <Calendar className="h-4 w-4 text-purple-600 dark:text-purple-400" /> | |
| <div className="text-sm font-medium text-purple-700 dark:text-purple-300">Evaluation Date</div> | |
| </div> | |
| <div className="font-medium text-purple-900 dark:text-purple-100">{(systemInfo as any)?.evaluationDate || "—"}</div> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| {/* Status Overview */} | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> | |
| {Object.entries(statusCounts).map(([status, count]) => { | |
| const key = (status as string) as keyof typeof STATUS_ICONS | |
| const Icon = STATUS_ICONS[key] ?? AlertTriangle | |
| return ( | |
| <Card key={status}> | |
| <CardContent className="pt-6"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <div className="text-2xl font-bold">{count}</div> | |
| <div className="text-sm text-muted-foreground capitalize">{status}</div> | |
| </div> | |
| <Icon className="h-8 w-8" style={{ color: STATUS_COLORS[key] ?? STATUS_COLORS.insufficient }} /> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ) | |
| })} | |
| </div> | |
| {/* Charts */} | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {/* Score Distribution */} | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Score Distribution</CardTitle> | |
| <CardDescription>Categories by evaluation status</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <ResponsiveContainer width="100%" height={300}> | |
| <PieChart> | |
| <Pie | |
| data={pieData} | |
| cx="50%" | |
| cy="50%" | |
| outerRadius={80} | |
| dataKey="value" | |
| label={({ name, value }) => `${name}: ${value}`} | |
| > | |
| {pieData.map((entry, index) => ( | |
| <Cell key={`cell-${index}`} fill={entry.color} /> | |
| ))} | |
| </Pie> | |
| <Tooltip /> | |
| </PieChart> | |
| </ResponsiveContainer> | |
| </CardContent> | |
| </Card> | |
| {/* Category Scores */} | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Category Scores</CardTitle> | |
| <CardDescription>Benchmark vs Process scores by category</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <ResponsiveContainer width="100%" height={300}> | |
| <BarChart data={chartData.slice(0, 8)} layout="horizontal"> | |
| <CartesianGrid strokeDasharray="3 3" /> | |
| <XAxis type="number" domain={[0, 15]} /> | |
| <YAxis dataKey="name" type="category" width={100} /> | |
| <Tooltip labelFormatter={(label) => chartData.find((d) => d.name === label)?.fullName || label} /> | |
| <Bar dataKey="benchmarkScore" stackId="a" fill="#3b82f6" name="Benchmark" /> | |
| <Bar dataKey="processScore" stackId="a" fill="#8b5cf6" name="Process" /> | |
| </BarChart> | |
| </ResponsiveContainer> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| {/* Detailed Results */} | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Detailed Category Results</CardTitle> | |
| <CardDescription>Complete breakdown of all evaluated categories</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="space-y-4"> | |
| {selectedCategoryObjects.map((category) => { | |
| const score = safeCategoryScores[category.id] | |
| if (!score) return null | |
| const key = (score.status as string) as keyof typeof STATUS_ICONS | |
| const Icon = (STATUS_ICONS as any)[key] ?? AlertTriangle | |
| const color = (STATUS_COLORS as any)[key] ?? STATUS_COLORS.insufficient | |
| return ( | |
| <div key={category.id} className="flex items-center justify-between p-4 border rounded-lg"> | |
| <div className="flex items-center gap-3"> | |
| <Icon className="h-5 w-5" style={{ color }} /> | |
| <div> | |
| <div className="font-medium">{category.name}</div> | |
| <div className="flex items-center gap-2 mt-1"> | |
| <Badge | |
| variant={category.type === "capability" ? "secondary" : "destructive"} | |
| className="text-xs" | |
| > | |
| {category.type} | |
| </Badge> | |
| <span className="text-sm text-muted-foreground"> | |
| Benchmark: {score.benchmarkScore}/7 | Process: {score.processScore}/8 | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="text-right"> | |
| <div className="text-lg font-bold">{score.totalScore}/15</div> | |
| <Badge | |
| variant={ | |
| score.status === "strong" | |
| ? "default" | |
| : score.status === "adequate" | |
| ? "secondary" | |
| : score.status === "weak" | |
| ? "outline" | |
| : "destructive" | |
| } | |
| className="text-xs" | |
| > | |
| {score.status} | |
| </Badge> | |
| </div> | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| {/* Excluded Categories with Reasons */} | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Excluded Categories & Reasons</CardTitle> | |
| <CardDescription>Categories you marked as not applicable and the rationale provided</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="space-y-3"> | |
| {safeCategories | |
| .filter((c) => !safeSelectedCategories.includes(c.id)) | |
| .map((c) => ( | |
| <div key={c.id} className="p-3 border rounded-md"> | |
| <div className="font-medium">{c.name}</div> | |
| <div className="text-sm text-muted-foreground mt-1"> | |
| {excludedCategoryReasons?.[c.id] || "No reason provided"} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| {/* Priority Actions */} | |
| <Card> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2"> | |
| <AlertTriangle className="h-5 w-5 text-amber-500" /> | |
| Priority Action Areas | |
| </CardTitle> | |
| <CardDescription>Categories requiring immediate attention</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="space-y-4"> | |
| {statusCounts.insufficient > 0 && ( | |
| <div> | |
| <h4 className="font-medium text-destructive mb-2"> | |
| Critical - Insufficient Categories ({statusCounts.insufficient}) | |
| </h4> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-2"> | |
| {selectedCategoryObjects | |
| .filter((c) => safeCategoryScores[c.id]?.status === "insufficient") | |
| .map((category) => ( | |
| <Badge key={category.id} variant="destructive" className="justify-start"> | |
| {category.name} | |
| </Badge> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| {statusCounts.weak > 0 && ( | |
| <div> | |
| <h4 className="font-medium text-amber-600 mb-2"> | |
| High Priority - Weak Categories ({statusCounts.weak}) | |
| </h4> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-2"> | |
| {selectedCategoryObjects | |
| .filter((c) => safeCategoryScores[c.id]?.status === "weak") | |
| .map((category) => ( | |
| <Badge key={category.id} variant="outline" className="justify-start"> | |
| {category.name} | |
| </Badge> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| {statusCounts.insufficient === 0 && statusCounts.weak === 0 && ( | |
| <div className="text-center py-8 text-muted-foreground"> | |
| <CheckCircle className="h-12 w-12 mx-auto mb-2 text-green-600" /> | |
| <p>No critical priority areas identified. All categories scored adequate or above.</p> | |
| </div> | |
| )} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| ) | |
| } | |