feat(lessons): add lessons from client db
This commit is contained in:
114
src/components/lessons/FrequencyMeanWidget.tsx
Normal file
114
src/components/lessons/FrequencyMeanWidget.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const FrequencyMeanWidget: React.FC = () => {
|
||||
// Data: Value -> Frequency
|
||||
const [counts, setCounts] = useState({ 0: 3, 1: 5, 2: 6, 3: 4, 4: 2 });
|
||||
|
||||
const handleChange = (val: number, delta: number) => {
|
||||
setCounts(prev => ({
|
||||
...prev,
|
||||
[val]: Math.max(0, (prev[val as keyof typeof prev] || 0) + delta)
|
||||
}));
|
||||
};
|
||||
|
||||
const values = [0, 1, 2, 3, 4];
|
||||
const totalStudents = values.reduce((sum, v) => sum + counts[v as keyof typeof counts], 0);
|
||||
const totalBooks = values.reduce((sum, v) => sum + v * counts[v as keyof typeof counts], 0);
|
||||
const mean = totalStudents > 0 ? (totalBooks / totalStudents).toFixed(2) : '0';
|
||||
|
||||
// Calculate Median position
|
||||
let cumulative = 0;
|
||||
let medianVal = 0;
|
||||
const mid = (totalStudents + 1) / 2;
|
||||
|
||||
if (totalStudents > 0) {
|
||||
for (const v of values) {
|
||||
cumulative += counts[v as keyof typeof counts];
|
||||
if (cumulative >= mid) {
|
||||
medianVal = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
|
||||
{/* Table Control */}
|
||||
<div>
|
||||
<h4 className="font-bold text-slate-700 mb-4 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-amber-500"></span>
|
||||
Edit Frequencies
|
||||
</h4>
|
||||
<div className="overflow-hidden rounded-lg border border-slate-200">
|
||||
<table className="w-full text-sm text-center">
|
||||
<thead className="bg-slate-50 text-slate-500 font-bold uppercase">
|
||||
<tr>
|
||||
<th className="p-3 border-b border-r border-slate-200">Books Read</th>
|
||||
<th className="p-3 border-b border-slate-200">Students (Freq)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{values.map(v => (
|
||||
<tr key={v} className="group hover:bg-amber-50/50 transition-colors">
|
||||
<td className="p-3 border-r border-slate-100 font-mono font-bold text-slate-700">{v}</td>
|
||||
<td className="p-2 flex justify-center items-center gap-3">
|
||||
<button
|
||||
onClick={() => handleChange(v, -1)}
|
||||
className="w-6 h-6 rounded bg-slate-100 hover:bg-slate-200 text-slate-600 font-bold flex items-center justify-center transition-colors"
|
||||
>-</button>
|
||||
<span className="w-4 font-mono font-bold text-slate-800">{counts[v as keyof typeof counts]}</span>
|
||||
<button
|
||||
onClick={() => handleChange(v, 1)}
|
||||
className="w-6 h-6 rounded bg-amber-100 hover:bg-amber-200 text-amber-700 font-bold flex items-center justify-center transition-colors"
|
||||
>+</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr className="bg-slate-50 font-bold text-slate-800">
|
||||
<td className="p-3 border-r border-slate-200">TOTAL</td>
|
||||
<td className="p-3">{totalStudents}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Visualization & Stats */}
|
||||
<div className="flex flex-col justify-between">
|
||||
<div className="bg-slate-50 p-4 rounded-xl border border-slate-200 mb-6">
|
||||
<h4 className="text-xs font-bold text-slate-400 uppercase mb-3">Dot Plot Preview</h4>
|
||||
<div className="flex justify-between items-end h-32 px-2 pb-2 border-b border-slate-300">
|
||||
{values.map(v => (
|
||||
<div key={v} className="flex flex-col-reverse items-center gap-1 w-8">
|
||||
{Array.from({length: counts[v as keyof typeof counts]}).map((_, i) => (
|
||||
<div key={i} className="w-3 h-3 rounded-full bg-amber-500 shadow-sm"></div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-between px-2 pt-2 text-xs font-mono font-bold text-slate-500">
|
||||
{values.map(v => <span key={v} className="w-8 text-center">{v}</span>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="p-4 bg-indigo-50 border border-indigo-100 rounded-lg">
|
||||
<p className="text-xs font-bold text-indigo-400 uppercase">Weighted Mean</p>
|
||||
<p className="text-2xl font-bold text-indigo-700">{mean}</p>
|
||||
<p className="text-[10px] text-indigo-400 mt-1">Total Books ({totalBooks}) / Students ({totalStudents})</p>
|
||||
</div>
|
||||
<div className="p-4 bg-emerald-50 border border-emerald-100 rounded-lg">
|
||||
<p className="text-xs font-bold text-emerald-400 uppercase">Median</p>
|
||||
<p className="text-2xl font-bold text-emerald-700">{medianVal}</p>
|
||||
<p className="text-[10px] text-emerald-400 mt-1">Middle Position (~{mid.toFixed(1)})</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FrequencyMeanWidget;
|
||||
Reference in New Issue
Block a user