feat(lessons): add lessons from client db
This commit is contained in:
140
src/components/lessons/LinearSolutionsWidget.tsx
Normal file
140
src/components/lessons/LinearSolutionsWidget.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const LinearSolutionsWidget: React.FC = () => {
|
||||
// Model: ax + b = cx + d
|
||||
const [a, setA] = useState(2);
|
||||
const [b, setB] = useState(4);
|
||||
const [c, setC] = useState(2);
|
||||
const [d, setD] = useState(8);
|
||||
|
||||
const isParallel = a === c;
|
||||
const isCoincident = isParallel && b === d;
|
||||
|
||||
// Calculate solution if not parallel
|
||||
// ax + b = cx + d => (a-c)x = d-b => x = (d-b)/(a-c)
|
||||
const intersectionX = isParallel ? 0 : (d - b) / (a - c);
|
||||
const intersectionY = a * intersectionX + b;
|
||||
|
||||
// Visualization range
|
||||
const range = 10;
|
||||
const scale = 20; // 1 unit = 20px
|
||||
const center = 150; // px
|
||||
|
||||
const toPx = (val: number, isY = false) => {
|
||||
if (isY) return center - val * scale;
|
||||
return center + val * scale;
|
||||
};
|
||||
|
||||
const getLinePath = (slope: number, intercept: number) => {
|
||||
const x1 = -range;
|
||||
const y1 = slope * x1 + intercept;
|
||||
const x2 = range;
|
||||
const y2 = slope * x2 + intercept;
|
||||
return `M ${toPx(x1)} ${toPx(y1, true)} L ${toPx(x2)} ${toPx(y2, true)}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex justify-between items-center bg-slate-50 p-4 rounded-lg border border-slate-200">
|
||||
<div className="font-mono text-xl text-blue-700 font-bold">
|
||||
<span className="text-indigo-600">{a}x + {b}</span> = <span className="text-emerald-600">{c}x + {d}</span>
|
||||
</div>
|
||||
<div className={`px-3 py-1 rounded text-sm font-bold uppercase ${
|
||||
isCoincident ? 'bg-green-100 text-green-800' :
|
||||
isParallel ? 'bg-rose-100 text-rose-800' :
|
||||
'bg-blue-100 text-blue-800'
|
||||
}`}>
|
||||
{isCoincident ? "Infinite Solutions" : isParallel ? "No Solution" : "One Solution"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-8">
|
||||
{/* Controls */}
|
||||
<div className="w-full md:w-1/3 space-y-4">
|
||||
<div className="space-y-2 p-3 bg-indigo-50 rounded-lg border border-indigo-100">
|
||||
<p className="text-xs font-bold text-indigo-800 uppercase">Left Side (Line 1)</p>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500">Slope (a): {a}</label>
|
||||
<input type="range" min="-5" max="5" step="1" value={a} onChange={e => setA(Number(e.target.value))} className="w-full h-1 bg-indigo-200 rounded accent-indigo-600" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500">Intercept (b): {b}</label>
|
||||
<input type="range" min="-10" max="10" step="1" value={b} onChange={e => setB(Number(e.target.value))} className="w-full h-1 bg-indigo-200 rounded accent-indigo-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 p-3 bg-emerald-50 rounded-lg border border-emerald-100">
|
||||
<p className="text-xs font-bold text-emerald-800 uppercase">Right Side (Line 2)</p>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500">Slope (c): {c}</label>
|
||||
<input type="range" min="-5" max="5" step="1" value={c} onChange={e => setC(Number(e.target.value))} className="w-full h-1 bg-emerald-200 rounded accent-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500">Intercept (d): {d}</label>
|
||||
<input type="range" min="-10" max="10" step="1" value={d} onChange={e => setD(Number(e.target.value))} className="w-full h-1 bg-emerald-200 rounded accent-emerald-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Graph */}
|
||||
<div className="w-full md:flex-1 border border-slate-200 rounded-lg overflow-hidden relative h-[300px] bg-white">
|
||||
<svg width="100%" height="100%" viewBox="0 0 300 300" className="absolute top-0 left-0">
|
||||
<defs>
|
||||
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="#f1f5f9" strokeWidth="1"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||
|
||||
{/* Axes */}
|
||||
<line x1="0" y1="150" x2="300" y2="150" stroke="#cbd5e1" strokeWidth="2" />
|
||||
<line x1="150" y1="0" x2="150" y2="300" stroke="#cbd5e1" strokeWidth="2" />
|
||||
|
||||
{/* Lines */}
|
||||
<path d={getLinePath(a, b)} stroke="#4f46e5" strokeWidth="3" fill="none" />
|
||||
<path d={getLinePath(c, d)} stroke={isCoincident ? "#4f46e5" : "#10b981"} strokeWidth="3" strokeDasharray={isCoincident ? "5,5" : ""} fill="none" />
|
||||
|
||||
{/* Intersection Point */}
|
||||
{!isParallel && (
|
||||
<circle cx={toPx(intersectionX)} cy={toPx(intersectionY, true)} r="5" fill="#f43f5e" stroke="white" strokeWidth="2" />
|
||||
)}
|
||||
</svg>
|
||||
|
||||
{/* Labels */}
|
||||
{!isParallel && (
|
||||
<div className="absolute bottom-2 right-2 bg-white/90 p-2 rounded text-xs border border-slate-200 shadow-sm">
|
||||
Intersection: ({intersectionX.toFixed(2)}, {intersectionY.toFixed(2)})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Logic Explanation */}
|
||||
<div className="bg-slate-50 p-4 rounded-lg text-sm text-slate-700">
|
||||
<p className="font-bold mb-1">Algebraic Check:</p>
|
||||
<ul className="list-disc pl-5 space-y-1">
|
||||
<li>Subtract {c}x from both sides: <span className="font-mono font-bold">{(a-c).toFixed(0)}x + {b} = {d}</span></li>
|
||||
{a === c ? (
|
||||
<>
|
||||
<li><span className="text-rose-600 font-bold">0x</span> (Variables cancel!)</li>
|
||||
<li>Remaining statement: <span className="font-mono font-bold">{b} = {d}</span></li>
|
||||
<li className={`font-bold ${b === d ? 'text-green-600' : 'text-rose-600'}`}>
|
||||
{b === d ? "TRUE (Identity) → Infinite Solutions" : "FALSE (Contradiction) → No Solution"}
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<li>Variables do NOT cancel.</li>
|
||||
<li><span className="font-mono">{(a-c).toFixed(0)}x = {d - b}</span></li>
|
||||
<li>One unique solution exists.</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinearSolutionsWidget;
|
||||
Reference in New Issue
Block a user