feat(lessons): add lessons from client db

This commit is contained in:
shafin-r
2026-03-01 20:24:14 +06:00
parent 2eaf77e13c
commit 2a00c44157
152 changed files with 74587 additions and 222 deletions

View File

@ -0,0 +1,125 @@
import React, { useState } from 'react';
interface DataPoint {
x: number;
y: number;
isOutlier?: boolean;
}
const ScatterplotInteractiveWidget: React.FC = () => {
const [showLine, setShowLine] = useState(false);
const [showResiduals, setShowResiduals] = useState(false);
const [hasOutlier, setHasOutlier] = useState(false);
// Base Data (approx linear y = 1.5x + 10)
const basePoints: DataPoint[] = [
{x: 1, y: 12}, {x: 2, y: 14}, {x: 3, y: 13}, {x: 4, y: 17},
{x: 5, y: 18}, {x: 6, y: 19}, {x: 7, y: 22}, {x: 8, y: 21}
];
const points: DataPoint[] = hasOutlier
? [...basePoints, {x: 7, y: 5, isOutlier: true}]
: basePoints;
// Simple Linear Regression Calculation
const n = points.length;
const sumX = points.reduce((a, p) => a + p.x, 0);
const sumY = points.reduce((a, p) => a + p.y, 0);
const sumXY = points.reduce((a, p) => a + p.x * p.y, 0);
const sumXX = points.reduce((a, p) => a + p.x * p.x, 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
const predict = (x: number) => slope * x + intercept;
// Scales
const width = 400;
const height = 250;
const maxX = 9;
const maxY = 25;
const toX = (val: number) => (val / maxX) * width;
const toY = (val: number) => height - (val / maxY) * height;
return (
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
<div className="flex flex-wrap gap-4 mb-6 justify-center">
<button
onClick={() => setShowLine(!showLine)}
className={`px-4 py-2 rounded-full font-bold text-sm transition-all ${showLine ? 'bg-indigo-600 text-white' : 'bg-slate-100 text-slate-600'}`}
>
{showLine ? 'Hide Line' : 'Show Line of Best Fit'}
</button>
<button
onClick={() => setShowResiduals(!showResiduals)}
className={`px-4 py-2 rounded-full font-bold text-sm transition-all ${showResiduals ? 'bg-indigo-600 text-white' : 'bg-slate-100 text-slate-600'}`}
>
{showResiduals ? 'Hide Residuals' : 'Show Residuals'}
</button>
<button
onClick={() => setHasOutlier(!hasOutlier)}
className={`px-4 py-2 rounded-full font-bold text-sm transition-all border ${hasOutlier ? 'bg-rose-100 text-rose-700 border-rose-300' : 'bg-white text-slate-600 border-slate-300'}`}
>
{hasOutlier ? 'Remove Outlier' : 'Add Outlier'}
</button>
</div>
<div className="relative border-b border-l border-slate-300 bg-slate-50 rounded-tr-lg mb-4 h-[250px]">
<svg width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} className="overflow-visible">
{/* Line of Best Fit */}
{showLine && (
<line
x1={toX(0)} y1={toY(predict(0))}
x2={toX(maxX)} y2={toY(predict(maxX))}
stroke="#4f46e5" strokeWidth="2" strokeDasharray={hasOutlier ? "5,5" : ""}
/>
)}
{/* Residuals */}
{showLine && showResiduals && points.map((p, i) => (
<line
key={`res-${i}`}
x1={toX(p.x)} y1={toY(p.y)}
x2={toX(p.x)} y2={toY(predict(p.x))}
stroke={p.y > predict(p.x) ? "#10b981" : "#f43f5e"} strokeWidth="1.5" opacity="0.6"
/>
))}
{/* Points */}
{points.map((p, i) => (
<g key={i}>
<circle
cx={toX(p.x)} cy={toY(p.y)}
r={p.isOutlier ? 6 : 4}
fill={p.isOutlier ? "#f43f5e" : "#475569"}
stroke="white" strokeWidth="2"
className="transition-all duration-300"
/>
{p.isOutlier && (
<text x={toX(p.x)+10} y={toY(p.y)} className="text-xs font-bold fill-rose-600">Outlier</text>
)}
</g>
))}
</svg>
{/* Axes Labels */}
<div className="absolute -bottom-6 w-full text-center text-xs font-bold text-slate-400">Variable X</div>
<div className="absolute -left-8 top-1/2 -rotate-90 text-xs font-bold text-slate-400">Variable Y</div>
</div>
<div className="bg-slate-50 p-4 rounded-lg border border-slate-200 flex justify-between items-center text-sm">
<div>
<span className="font-bold text-slate-500 block text-xs uppercase">Slope (m)</span>
<span className="font-mono font-bold text-indigo-700 text-lg">{slope.toFixed(2)}</span>
</div>
{hasOutlier && (
<div className="text-rose-600 font-bold bg-rose-50 px-3 py-1 rounded border border-rose-200">
Outlier pulls the line down!
</div>
)}
</div>
</div>
);
};
export default ScatterplotInteractiveWidget;