"use client"; import { useMemo, Suspense } from "react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { Target, ShieldCheck, Cpu, Code2, User } from "lucide-react"; import { metricsData } from "@/components/metricsData"; import { getMockStudentById } from "@/lib/mockApi"; function AnalysisDashboardContent() { const searchParams = useSearchParams(); const studentId = searchParams.get("student"); const student = studentId ? getMockStudentById(studentId) : null; // Helper function to colorize cells like a thermal heatmap based on parsed average score const getHeatmapClass = (scoreStr: string | number) => { const score = typeof scoreStr === 'string' ? parseInt(scoreStr) : scoreStr; if (isNaN(score)) return "bg-surface-variant text-on-surface-variant border-outline-variant"; if (score >= 90) return "bg-metric-high dark:bg-[#004d40] text-white border-metric-high/30 dark:border-white/10 shadow-[0_0_15px_rgba(var(--color-metric-high),0.3)] dark:shadow-none"; if (score >= 80) return "bg-metric-mid dark:bg-[#01579b] text-white border-metric-mid/30 dark:border-white/10"; if (score >= 70) return "bg-metric-warn dark:bg-[#d84315] text-white border-metric-warn/30 dark:border-white/10"; return "bg-metric-low dark:bg-[#b71c1c] text-white border-metric-low/30 dark:border-white/10"; }; // When viewing a specific student, override each metric's avgScore // with the pre-generated score from the database. No random jitter. const displayData = useMemo(() => { if (!student) return metricsData; // Default class average view const personalizedData = JSON.parse(JSON.stringify(metricsData)) as typeof metricsData; Object.keys(personalizedData).forEach((catKey) => { personalizedData[catKey].forEach(item => { // Read the pre-generated score from the student's metricScores const storedScore = student.metricScores?.[item.id]; if (storedScore !== undefined) { item.avgScore = storedScore.toString(); } }); }); return personalizedData; }, [student]); // Pre-calculate dimensional averages for the top macro view // When viewing a specific student, use their EXACT scores from the database // to ensure consistency with the roster table. const categoryStats = useMemo(() => { const stats: Record = {}; Object.keys(displayData).forEach((cat) => { const items = displayData[cat]; if (student) { // Use the student's EXACT macro score (same number shown in the roster) stats[cat] = { avg: student[cat as keyof typeof student] as number, total: items.length }; } else { // Class average view: compute from metricsData sub-items let sum = 0; let count = 0; items.forEach((item) => { const itemScore = parseInt(item.avgScore) || 0; sum += itemScore; count++; }); stats[cat] = { avg: count > 0 ? Math.round(sum / count) : 0, total: count }; } }); return stats; }, [displayData, student]); const icons = { K: , A: , S: , D: }; const titles = { K: "知识掌握", A: "AI 辅助", S: "软件工程", D: "态度与协作" }; return (
{/* Header Container */}

能力分析雷达 {student && }

{student ? "个体学生多维度能力结构深度诊断" : "多维度能力结构诊断热力图 (大盘基准)"}

{/* Dynamic Context Banner if Drilling Down */} {student && (
{student.name[0]}
正在深入诊断
{student.name} {student.id}
)}
{/* Top Row: Macro View (4 Big Cards) */}
{Object.keys(displayData).map((catKey) => { const firstItem = displayData[catKey][0]; const stat = categoryStats[catKey]; return (
{/* Ambient Background Gradient */}
{icons[catKey as keyof typeof icons]}

{titles[catKey as keyof typeof titles]}

{stat.total} 项考核指标

{stat.avg}% {stat.avg >= 85 ? '优秀' : stat.avg >= 75 ? '良好' : '需改进'}
); })}
{/* Main Area: The Bento Box Heatmap Grid */}

{student ? `${student.name} 的全景弱点追踪` : "能力全景分布"}

{Object.keys(displayData).map((catKey) => (

{catKey} {titles[catKey as keyof typeof titles]} 拆解项

{/* The Micro-Tiles Grid Layer */}
{displayData[catKey].map((metric) => ( {/* Glossy Overlay for that Glassmorphism feel */}
{metric.id} {metric.avgScore}

{metric.name}

))}
))}
); } // Wrap in suspense to handle useSearchParams appropriately in Next.js 14+ client components export default function AnalysisDashboard() { return (
正在提取分析晶元数据... }>
); }