102 lines
4.8 KiB
TypeScript
102 lines
4.8 KiB
TypeScript
"use client"
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Flame, Info, CheckCircle2 } from "lucide-react"
|
|
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
|
|
|
|
const HEATMAP_DATA = [
|
|
{ topic: "Rotational Dynamics", attempts: [80, 90, 40, 20, 10] }, // 10 is critical error rate
|
|
{ topic: "Electromagnetism", attempts: [30, 25, 20, 15, 10] },
|
|
{ topic: "Thermodynamics", attempts: [5, 10, 5, 2, 0] },
|
|
{ topic: "Optics", attempts: [60, 50, 45, 60, 55] },
|
|
{ topic: "Modern Physics", attempts: [10, 5, 2, 0, 0] }
|
|
];
|
|
|
|
const ZERO_HEATMAP_DATA = [
|
|
{ topic: "Physics (Core Mechanics)", attempts: [0, 0, 0, 0, 0] },
|
|
{ topic: "Chemistry (Physical & Organic)", attempts: [0, 0, 0, 0, 0] },
|
|
{ topic: "Mathematics / Biology", attempts: [0, 0, 0, 0, 0] },
|
|
];
|
|
|
|
export function MistakeHeatmap() {
|
|
const { userId } = useActiveUser();
|
|
const isDemo = isDemoUser(userId);
|
|
|
|
const getColor = (errorRate: number) => {
|
|
if (errorRate === 0) return 'bg-green-600/50 border-green-600/80 text-green-100';
|
|
if (errorRate >= 50) return 'bg-red-600/80 border-red-600 shadow-[0_0_10px_-2px_rgba(220,38,38,0.5)] text-red-100'; // Critical
|
|
if (errorRate >= 30) return 'bg-amber-500/60 border-amber-500'; // Warning
|
|
if (errorRate >= 10) return 'bg-yellow-500/30 border-yellow-500/50'; // Moderate
|
|
return 'bg-green-500/20 border-green-500/30'; // Good
|
|
};
|
|
|
|
const rows = isDemo ? HEATMAP_DATA : ZERO_HEATMAP_DATA;
|
|
|
|
return (
|
|
<Card className="border-border bg-background shadow-sm h-full min-h-[540px] flex flex-col justify-between">
|
|
<CardHeader className="pb-4">
|
|
<div className="flex items-center justify-between">
|
|
<CardTitle className="text-xl flex items-center gap-2">
|
|
<Flame className="text-orange-500" /> Mastery Map
|
|
<Badge variant="outline" className="border-[var(--cyan-ai-30)] text-[var(--cyan-ai)] bg-[var(--cyan-ai-10)] rounded-md text-[10px] uppercase font-bold ml-2 hidden sm:flex">AI Coach</Badge>
|
|
</CardTitle>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">
|
|
{isDemo ? "Topic-wise error rate across last 5 tests." : "Topic-wise error rate across your recent test sessions."}
|
|
</p>
|
|
</CardHeader>
|
|
<CardContent className="flex-1 p-4 md:p-6 flex flex-col justify-between">
|
|
<div className="w-full">
|
|
{/* Header row */}
|
|
<div className="flex items-center mb-3 text-xs sm:text-sm font-black text-muted-foreground uppercase tracking-wider">
|
|
<div className="w-32 sm:w-36 md:w-40 shrink-0 pr-2">Topic</div>
|
|
<div className="flex-1 grid grid-cols-5 gap-0.5 sm:gap-1 text-center text-[9px] sm:text-xs">
|
|
<span className="whitespace-nowrap">T-4</span>
|
|
<span className="whitespace-nowrap">T-3</span>
|
|
<span className="whitespace-nowrap">T-2</span>
|
|
<span className="whitespace-nowrap">T-1</span>
|
|
<span className="whitespace-nowrap truncate">Latest</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Data rows */}
|
|
<div className="space-y-3">
|
|
{rows.map((row, idx) => (
|
|
<div key={idx} className="flex items-center group">
|
|
<div className="w-32 sm:w-36 md:w-40 shrink-0 text-xs sm:text-sm md:text-base font-bold truncate pr-2 group-hover:text-primary transition-colors" title={row.topic}>
|
|
{row.topic}
|
|
</div>
|
|
<div className="flex-1 grid grid-cols-5 gap-0.5 sm:gap-1">
|
|
{row.attempts.map((errRate, i) => (
|
|
<div
|
|
key={i}
|
|
className={`h-8 sm:h-9 rounded-sm sm:rounded-md border transition-all duration-300 group-hover:scale-105 flex items-center justify-center cursor-help ${getColor(errRate)}`}
|
|
title={`Error Rate: ${errRate}%`}
|
|
>
|
|
<span className="text-[8px] sm:text-xs font-black text-foreground/90">{errRate}%</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{!isDemo && (
|
|
<div className="mt-6 p-4 rounded-xl bg-secondary/30 border border-border/50 text-center space-y-1">
|
|
<div className="flex items-center justify-center gap-1.5 text-xs font-bold text-green-400">
|
|
<CheckCircle2 size={14} /> Clean Slate · 0 Errors Recorded
|
|
</div>
|
|
<p className="text-[11px] text-muted-foreground">
|
|
As you attempt DPPs and CBT mock tests, questions you get wrong will automatically be tracked and heatmapped here.
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|