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,179 @@
import React, { useState, useRef } from 'react';
const TangentPropertiesWidget: React.FC = () => {
const [pointP, setPointP] = useState({ x: 350, y: 150 });
const isDragging = useRef(false);
const svgRef = useRef<SVGSVGElement>(null);
const center = { x: 150, y: 150 };
const radius = 60;
// Interaction
const handleMouseMove = (e: React.MouseEvent) => {
if (!isDragging.current || !svgRef.current) return;
const rect = svgRef.current.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Constrain P to be outside the circle (distance > radius)
const dx = x - center.x;
const dy = y - center.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// Min distance to keep things looking nice (radius + padding)
if (dist < radius + 20) {
const angle = Math.atan2(dy, dx);
setPointP({
x: center.x + (radius + 20) * Math.cos(angle),
y: center.y + (radius + 20) * Math.sin(angle)
});
} else {
setPointP({ x, y });
}
};
// Calculations
const dx = pointP.x - center.x;
const dy = pointP.y - center.y;
const distPO = Math.sqrt(dx * dx + dy * dy);
const anglePO = Math.atan2(dy, dx);
// Angle offset to tangent points
// cos(theta) = Adjacent / Hypotenuse = radius / distPO
const theta = Math.acos(radius / distPO);
const t1Angle = anglePO - theta;
const t2Angle = anglePO + theta;
const T1 = {
x: center.x + radius * Math.cos(t1Angle),
y: center.y + radius * Math.sin(t1Angle)
};
const T2 = {
x: center.x + radius * Math.cos(t2Angle),
y: center.y + radius * Math.sin(t2Angle)
};
const tangentLength = Math.sqrt(distPO * distPO - radius * radius);
// Right Angle Markers
const markerSize = 10;
const getRightAnglePath = (p: {x:number, y:number}, angle: number) => {
// angle is the angle of the radius. We need to go inwards and perpendicular
// Actually simpler: Vector from Center to T, and Vector T to P are perp.
// Let's just draw a small square aligned with radius
const rAngle = angle;
// Point on radius
const p1 = { x: p.x - markerSize * Math.cos(rAngle), y: p.y - markerSize * Math.sin(rAngle) };
// Point on tangent (towards P)
// Tangent is perpendicular to radius.
// We need to know if we go clockwise or counter clockwise.
// Vector T->P
const tpAngle = Math.atan2(pointP.y - p.y, pointP.x - p.x);
const p2 = { x: p.x + markerSize * Math.cos(tpAngle), y: p.y + markerSize * Math.sin(tpAngle) };
// Corner
const p3 = { x: p1.x + markerSize * Math.cos(tpAngle), y: p1.y + markerSize * Math.sin(tpAngle) };
return `M ${p1.x} ${p1.y} L ${p3.x} ${p3.y} L ${p2.x} ${p2.y}`;
};
return (
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200 flex flex-col md:flex-row gap-8 items-center">
<div className="relative">
<svg
ref={svgRef}
width="400" height="300"
className="select-none cursor-default bg-slate-50 rounded-lg border border-slate-100"
onMouseMove={handleMouseMove}
onMouseUp={() => isDragging.current = false}
onMouseLeave={() => isDragging.current = false}
>
{/* Circle */}
<circle cx={center.x} cy={center.y} r={radius} fill="white" stroke="#94a3b8" strokeWidth="2" />
<circle cx={center.x} cy={center.y} r="3" fill="#64748b" />
<text x={center.x - 15} y={center.y + 5} className="text-xs font-bold fill-slate-400">O</text>
{/* Radii */}
<line x1={center.x} y1={center.y} x2={T1.x} y2={T1.y} stroke="#cbd5e1" strokeWidth="2" strokeDasharray="4,4" />
<line x1={center.x} y1={center.y} x2={T2.x} y2={T2.y} stroke="#cbd5e1" strokeWidth="2" strokeDasharray="4,4" />
{/* Tangents */}
<line x1={pointP.x} y1={pointP.y} x2={T1.x} y2={T1.y} stroke="#7c3aed" strokeWidth="3" />
<line x1={pointP.x} y1={pointP.y} x2={T2.x} y2={T2.y} stroke="#7c3aed" strokeWidth="3" />
{/* Right Angle Markers */}
<path d={getRightAnglePath(T1, t1Angle)} stroke="#64748b" fill="transparent" strokeWidth="1" />
<path d={getRightAnglePath(T2, t2Angle)} stroke="#64748b" fill="transparent" strokeWidth="1" />
{/* Points */}
<circle cx={T1.x} cy={T1.y} r="5" fill="#7c3aed" />
<text x={T1.x + (T1.x - center.x)*0.2} y={T1.y + (T1.y - center.y)*0.2} className="text-xs font-bold fill-violet-700">A</text>
<circle cx={T2.x} cy={T2.y} r="5" fill="#7c3aed" />
<text x={T2.x + (T2.x - center.x)*0.2} y={T2.y + (T2.y - center.y)*0.2} className="text-xs font-bold fill-violet-700">B</text>
{/* External Point P */}
<g
onMouseDown={() => isDragging.current = true}
className="cursor-grab active:cursor-grabbing"
>
<circle cx={pointP.x} cy={pointP.y} r="15" fill="transparent" />
<circle cx={pointP.x} cy={pointP.y} r="6" fill="#f43f5e" stroke="white" strokeWidth="2" />
<text x={pointP.x + 10} y={pointP.y} className="text-sm font-bold fill-rose-600">P</text>
</g>
{/* Length Labels (Midpoints) */}
<rect
x={(pointP.x + T1.x)/2 - 15} y={(pointP.y + T1.y)/2 - 10}
width="30" height="20" rx="4" fill="white" stroke="#e2e8f0"
/>
<text x={(pointP.x + T1.x)/2} y={(pointP.y + T1.y)/2 + 4} textAnchor="middle" className="text-xs font-bold fill-violet-600">
{Math.round(tangentLength)}
</text>
<rect
x={(pointP.x + T2.x)/2 - 15} y={(pointP.y + T2.y)/2 - 10}
width="30" height="20" rx="4" fill="white" stroke="#e2e8f0"
/>
<text x={(pointP.x + T2.x)/2} y={(pointP.y + T2.y)/2 + 4} textAnchor="middle" className="text-xs font-bold fill-violet-600">
{Math.round(tangentLength)}
</text>
</svg>
</div>
<div className="flex-1 space-y-6">
<div className="bg-violet-50 p-4 rounded-xl border border-violet-100">
<h4 className="font-bold text-violet-900 mb-2 flex items-center gap-2">
<span className="bg-violet-200 text-xs px-2 py-0.5 rounded-full text-violet-800">Rule 1</span>
Equal Tangents
</h4>
<p className="text-sm text-violet-800 mb-2">
Tangents from the same external point are always congruent.
</p>
<p className="font-mono text-lg font-bold text-violet-600 bg-white p-2 rounded border border-violet-100 text-center">
PA = PB = {Math.round(tangentLength)}
</p>
</div>
<div className="bg-slate-50 p-4 rounded-xl border border-slate-200">
<h4 className="font-bold text-slate-700 mb-2 flex items-center gap-2">
<span className="bg-slate-200 text-xs px-2 py-0.5 rounded-full text-slate-600">Rule 2</span>
Perpendicular Radius
</h4>
<p className="text-sm text-slate-600">
The radius to the point of tangency is always perpendicular to the tangent line.
</p>
<div className="flex gap-4 mt-2 justify-center">
<span className="text-xs font-bold bg-white px-2 py-1 rounded border border-slate-200">OAP = 90°</span>
<span className="text-xs font-bold bg-white px-2 py-1 rounded border border-slate-200">OBP = 90°</span>
</div>
</div>
<p className="text-xs text-center text-slate-400">Drag point <strong>P</strong> to verify!</p>
</div>
</div>
);
};
export default TangentPropertiesWidget;