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,86 @@
import React, { useState } from 'react';
const HistogramBuilderWidget: React.FC = () => {
const [mode, setMode] = useState<'count' | 'percent'>('count');
// Data: [60, 70), [70, 80), [80, 90), [90, 100)
const data = [
{ bin: '60-70', count: 4, label: '60s' },
{ bin: '70-80', count: 9, label: '70s' },
{ bin: '80-90', count: 6, label: '80s' },
{ bin: '90-100', count: 1, label: '90s' },
];
const total = data.reduce((acc, curr) => acc + curr.count, 0); // 20
const maxCount = Math.max(...data.map(d => d.count));
const maxPercent = maxCount / total; // 0.45
return (
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
<div className="flex justify-between items-center mb-6">
<h3 className="font-bold text-slate-700">Test Scores Distribution</h3>
<div className="flex bg-slate-100 p-1 rounded-lg">
<button
onClick={() => setMode('count')}
className={`px-4 py-1.5 text-sm font-bold rounded-md transition-all ${mode === 'count' ? 'bg-white shadow-sm text-indigo-600' : 'text-slate-500 hover:text-slate-700'}`}
>
Frequency (Count)
</button>
<button
onClick={() => setMode('percent')}
className={`px-4 py-1.5 text-sm font-bold rounded-md transition-all ${mode === 'percent' ? 'bg-white shadow-sm text-rose-600' : 'text-slate-500 hover:text-slate-700'}`}
>
Relative Freq (%)
</button>
</div>
</div>
<div className="relative h-64 border-b-2 border-slate-200 flex items-end px-8 gap-1">
{/* Y Axis Labels */}
<div className="absolute left-0 top-0 bottom-0 flex flex-col justify-between text-xs font-mono text-slate-400 py-2">
<span>{mode === 'count' ? maxCount + 1 : ((maxPercent + 0.05)*100).toFixed(0) + '%'}</span>
<span>{mode === 'count' ? Math.round((maxCount+1)/2) : (((maxPercent + 0.05)/2)*100).toFixed(0) + '%'}</span>
<span>0</span>
</div>
{data.map((d, i) => {
const heightRatio = d.count / maxCount; // Normalize to max height of graph area roughly
// Actually map 0 to maxScale
const maxScale = mode === 'count' ? maxCount + 1 : (maxPercent + 0.05);
const val = mode === 'count' ? d.count : d.count / total;
const hPercent = (val / maxScale) * 100;
return (
<div key={i} className="flex-1 flex flex-col justify-end group relative h-full">
{/* Tooltip */}
<div className="opacity-0 group-hover:opacity-100 absolute -top-10 left-1/2 -translate-x-1/2 bg-slate-800 text-white text-xs py-1 px-2 rounded pointer-events-none transition-opacity z-10 whitespace-nowrap">
{d.bin}: {mode === 'count' ? d.count : `${(d.count/total*100).toFixed(0)}%`}
</div>
{/* Bar */}
<div
className={`w-full transition-all duration-500 rounded-t ${mode === 'count' ? 'bg-indigo-500 group-hover:bg-indigo-600' : 'bg-rose-500 group-hover:bg-rose-600'}`}
style={{ height: `${hPercent}%` }}
></div>
{/* Bin Label */}
<div className="absolute -bottom-6 w-full text-center text-xs font-bold text-slate-500">
{d.label}
</div>
</div>
);
})}
</div>
<div className="mt-8 p-4 bg-slate-50 rounded-xl border border-slate-200">
<p className="text-sm text-slate-600">
<strong>Key Takeaway:</strong> Notice that the <span className="font-bold text-slate-800">shape</span> of the distribution stays exactly the same.
Only the <span className="font-bold text-slate-800">Y-axis scale</span> changes.
</p>
</div>
</div>
);
};
export default HistogramBuilderWidget;