Update college predictor graph to look like a control chart

This commit is contained in:
2026-09-25 16:27:24 +05:30
parent fee6e5ebda
commit 09ba8cc992

View File

@@ -3,54 +3,54 @@
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { GraduationCap, BrainCircuit, LineChart as LineChartIcon } from "lucide-react" import { GraduationCap, BrainCircuit, LineChart as LineChartIcon } from "lucide-react"
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine, ReferenceArea, LabelList } from 'recharts'
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser" import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const DEMO_TREND_DATA = [ const DEMO_TREND_DATA = [
{ name: 'Week 1', rank: 4500, label: 'NIT Trichy CSE' }, { name: '1', value: 0.120, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: 'Week 2', rank: 4100, label: 'NIT Surathkal CSE' }, { name: '2', value: 0.100, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: 'Week 3', rank: 3800, label: 'IIT Mandi EEE' }, { name: '3', value: 0.075, ucl: 0.195, lcl: 0.005, cl: 0.10, target: 0.16 },
{ name: 'Week 4', rank: 3300, label: 'IIT Ropar CSE' }, { name: '4', value: 0.090, ucl: 0.195, lcl: 0.005, cl: 0.10, target: 0.16 },
{ name: 'Week 5', rank: 2900, label: 'IIT Guwahati ECE' }, { name: '5', value: 0.119, ucl: 0.180, lcl: 0.020, cl: 0.10, target: 0.16 },
{ name: 'Week 6', rank: 2750, label: 'IIT Roorkee' }, { name: '6', value: 0.110, ucl: 0.180, lcl: 0.020, cl: 0.10, target: 0.16 },
] { name: '7', value: 0.110, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: '8', value: 0.160, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: '9', value: 0.111, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
{ name: '10', value: 0.067, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
{ name: '11', value: 0.180, ucl: 0.178, lcl: 0.022, cl: 0.10, target: 0.16 },
{ name: '12', value: 0.125, ucl: 0.178, lcl: 0.022, cl: 0.10, target: 0.16 },
{ name: '13', value: 0.075, ucl: 0.178, lcl: 0.022, cl: 0.10, target: 0.16 },
{ name: '14', value: 0.066, ucl: 0.180, lcl: 0.020, cl: 0.10, target: 0.16 },
{ name: '15', value: 0.055, ucl: 0.180, lcl: 0.020, cl: 0.10, target: 0.16 },
{ name: '16', value: 0.100, ucl: 0.195, lcl: 0.005, cl: 0.10, target: 0.16 },
{ name: '17', value: 0.125, ucl: 0.195, lcl: 0.005, cl: 0.10, target: 0.16 },
{ name: '18', value: 0.088, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
{ name: '19', value: 0.056, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
{ name: '20', value: 0.080, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: '21', value: 0.050, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: '22', value: 0.080, ucl: 0.185, lcl: 0.015, cl: 0.10, target: 0.16 },
{ name: '23', value: 0.100, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
{ name: '24', value: 0.067, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
{ name: '25', value: 0.100, ucl: 0.190, lcl: 0.010, cl: 0.10, target: 0.16 },
];
const ZERO_TREND_DATA = [ const ZERO_TREND_DATA = DEMO_TREND_DATA.map(d => ({ ...d, value: 0 }));
{ name: 'Week 1', rank: 0 },
{ name: 'Week 2', rank: 0 },
{ name: 'Week 3', rank: 0 },
{ name: 'Week 4', rank: 0 },
{ name: 'Week 5', rank: 0 },
{ name: 'Week 6', rank: 0 },
]
// Custom Label to permanently show text on the graph (not on hover) // Custom dot to match the requested look
const CustomLabel = (props: any) => { const CustomDot = (props: any) => {
const { x, y, index, data } = props; const { cx, cy, payload } = props;
const point = data[index];
if (!point || !point.label) return null;
let anchor = "middle"; // Find if this is the outlier (highest point, around 0.180) to color it red
let xPos = x; const isOutlier = payload.value >= 0.180;
let yPos = y - 20;
if (index === 0) {
anchor = "start";
xPos = x + 5;
} else if (index === data.length - 1) {
anchor = "end";
xPos = x - 5;
}
return ( return (
<g> <circle
<text x={xPos} y={yPos} fill="#fff" fontSize={11} fontWeight="bold" textAnchor={anchor as any}> cx={cx}
{point.label} cy={cy}
</text> r={4}
<text x={xPos} y={yPos + 14} fill="var(--cyan-ai)" fontSize={11} fontWeight="bold" textAnchor={anchor as any}> fill={isOutlier ? "#ef4444" : "#3b82f6"}
Rank: {point.rank} stroke="none"
</text> />
</g>
); );
}; };
@@ -92,19 +92,46 @@ export default function CollegePredictorPage() {
</CardHeader> </CardHeader>
<CardContent className="h-[350px] mt-4"> <CardContent className="h-[350px] mt-4">
<ResponsiveContainer width="100%" height="100%"> <ResponsiveContainer width="100%" height="100%">
<LineChart data={trendData} margin={{ top: 35, right: 30, bottom: 5, left: 10 }}> <LineChart data={trendData} margin={{ top: 20, right: 30, bottom: 20, left: 10 }}>
{/* User said static line, not dotted. So removed strokeDasharray from grid? Let's make it solid if they mean grid, or just the line. Line is monotone. */} <CartesianGrid vertical={false} stroke="var(--border)" opacity={0.5} />
<CartesianGrid stroke="#333" vertical={false} /> <XAxis dataKey="name" stroke="var(--muted-foreground)" tick={{ fill: 'var(--muted-foreground)', fontSize: 12 }} axisLine={false} tickLine={false} />
<XAxis dataKey="name" stroke="#888" tick={{ fill: '#888' }} /> <YAxis
<YAxis stroke="#888" tick={{ fill: '#888' }} domain={[0, 5000]} reversed={true} /> stroke="var(--muted-foreground)"
tick={{ fill: 'var(--muted-foreground)', fontSize: 12 }}
domain={[0.02, 0.22]}
ticks={[0.02, 0.07, 0.12, 0.17, 0.22]}
tickFormatter={(val) => val.toFixed(3)}
axisLine={false}
tickLine={false}
label={{ value: 'Prediction Volatility', angle: -90, position: 'insideLeft', fill: 'var(--muted-foreground)', fontSize: 12, fontWeight: 'bold' }}
/>
<Tooltip
contentStyle={{ backgroundColor: 'var(--background)', borderColor: 'var(--border)', color: 'var(--foreground)' }}
itemStyle={{ color: 'var(--foreground)' }}
/>
{/* Target Spec Line Band */}
<ReferenceArea y1={0.15} y2={0.17} fill="rgba(250, 204, 21, 0.2)" />
{/* Target Line */}
<ReferenceLine y={0.16} stroke="#3b82f6" strokeWidth={2} label={{ position: 'insideTopLeft', value: 'Target 0.16', fill: 'var(--foreground)', fontSize: 11, fontWeight: 'bold' }} />
{/* Center Line (CL) */}
<ReferenceLine y={0.10} stroke="#2dd4bf" strokeWidth={1} label={{ position: 'insideTopLeft', value: 'CL 0.10', fill: 'var(--foreground)', fontSize: 11, fontWeight: 'bold' }} />
{/* Control Limits (stepped) */}
<Line type="stepAfter" dataKey="ucl" stroke="#ef4444" strokeWidth={2} strokeDasharray="5 5" dot={false} isAnimationActive={false} />
<Line type="stepAfter" dataKey="lcl" stroke="#ef4444" strokeWidth={2} strokeDasharray="5 5" dot={false} isAnimationActive={false} />
{/* Main Data Line */}
<Line <Line
type="linear" type="linear"
dataKey="rank" dataKey="value"
stroke="var(--cyan-ai)" stroke="#3b82f6"
strokeWidth={3} strokeWidth={1.5}
dot={{ r: 4, fill: 'var(--cyan-ai)' }} dot={<CustomDot />}
activeDot={{ r: 6 }} activeDot={{ r: 6, fill: 'var(--cyan-ai)' }}
label={<CustomLabel data={trendData} />} isAnimationActive={true}
/> />
</LineChart> </LineChart>
</ResponsiveContainer> </ResponsiveContainer>