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,232 @@
import React, { useState, useRef } from "react";
const InteractiveSectorWidget: React.FC = () => {
const [angle, setAngle] = useState(60); // degrees
const [radius, setRadius] = useState(120); // pixels
const isDragging = useRef<"angle" | "radius" | null>(null);
const svgRef = useRef<SVGSVGElement>(null);
const cx = 200;
const cy = 200;
const maxRadius = 160;
// Calculate Handle Position
const rad = (angle * Math.PI) / 180;
const hx = cx + radius * Math.cos(-rad); // SVG Y is down, so -rad for standard math "up" rotation behavior if we want counter-clockwise from East
const hy = cy + radius * Math.sin(-rad);
// For the arc path
// Start point is (cx + r, cy) [0 degrees]
// End point is (hx, hy)
const largeArc = angle > 180 ? 1 : 0;
// Sweep flag 0 because we are using -rad (counter clockwise visual in SVG)
const pathData = `
M ${cx} ${cy}
L ${cx + radius} ${cy}
A ${radius} ${radius} 0 ${largeArc} 0 ${hx} ${hy}
Z
`;
// Interaction
const handleInteraction = (e: React.MouseEvent) => {
if (!svgRef.current || !isDragging.current) return;
const rect = svgRef.current.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
const dx = mx - cx;
const dy = my - cy;
if (isDragging.current === "angle") {
let deg = Math.atan2(-dy, dx) * (180 / Math.PI); // -dy to correct for SVG coords
if (deg < 0) deg += 360;
setAngle(Math.round(deg));
} else if (isDragging.current === "radius") {
const dist = Math.sqrt(dx * dx + dy * dy);
setRadius(Math.max(50, Math.min(maxRadius, dist)));
}
};
return (
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200 flex flex-col md:flex-row items-center gap-8">
<div className="relative select-none">
<svg
ref={svgRef}
width="400"
height="400"
onMouseMove={handleInteraction}
onMouseUp={() => (isDragging.current = null)}
onMouseLeave={() => (isDragging.current = null)}
className="cursor-crosshair"
>
{/* Full Circle Ghost */}
<circle
cx={cx}
cy={cy}
r={radius}
stroke="#e2e8f0"
strokeWidth="1"
fill="none"
strokeDasharray="4,4"
/>
{/* Sector */}
<path
d={pathData}
fill="rgba(249, 115, 22, 0.2)"
stroke="#f97316"
strokeWidth="2"
/>
{/* Radius Handle Line (Baseline) */}
<line
x1={cx}
y1={cy}
x2={cx + radius}
y2={cy}
stroke="#cbd5e1"
strokeWidth="2"
/>
{/* Radius Drag Handle (on baseline) */}
<circle
cx={cx + radius}
cy={cy}
r={8}
fill="#94a3b8"
stroke="white"
strokeWidth="2"
className="cursor-ew-resize hover:fill-slate-600 shadow-sm"
onMouseDown={() => (isDragging.current = "radius")}
/>
{/* Angle Drag Handle (on arc) */}
<circle
cx={hx}
cy={hy}
r={10}
fill="#f97316"
stroke="white"
strokeWidth="2"
className="cursor-pointer hover:scale-110 transition-transform shadow-md"
onMouseDown={() => (isDragging.current = "angle")}
/>
{/* Angle Text */}
<text
x={cx + 20}
y={cy - 10}
className="text-xs font-bold fill-orange-600"
>
{angle}°
</text>
{/* Radius Text */}
<text
x={cx + radius / 2}
y={cy + 15}
textAnchor="middle"
className="text-xs font-bold fill-slate-400"
>
r = {Math.round(radius / 10)}
</text>
{/* Center */}
<circle cx={cx} cy={cy} r={4} fill="#64748b" />
</svg>
</div>
<div className="flex-1 w-full space-y-6">
<div className="bg-orange-50 border border-orange-100 p-4 rounded-xl">
<h3 className="text-orange-900 font-bold mb-2 flex items-center gap-2">
<span className="p-1 bg-orange-200 rounded text-xs">INPUT</span>
Parameters
</h3>
<div className="space-y-4">
<div>
<div className="flex justify-between text-xs text-orange-700 uppercase font-bold mb-1">
Angle (θ): {angle}°
</div>
<input
type="range"
min="1"
max="360"
value={angle}
onChange={(e) => setAngle(parseInt(e.target.value))}
className="w-full h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer accent-orange-600"
/>
</div>
<div>
<div className="flex justify-between text-xs text-orange-700 uppercase font-bold mb-1">
Radius (r): {Math.round(radius / 10)}
</div>
<input
type="range"
min="50"
max={maxRadius}
value={radius}
onChange={(e) => setRadius(parseInt(e.target.value))}
className="w-full h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer accent-orange-600"
/>
</div>
</div>
</div>
<div className="space-y-4">
<div className="p-4 bg-white border border-slate-200 rounded-xl shadow-sm">
<div className="flex justify-between items-center mb-1">
<span className="text-sm font-bold text-slate-600">
Fraction of Circle
</span>
<span className="font-mono text-orange-600 font-bold">
{angle}/360 {(angle / 360).toFixed(2)}
</span>
</div>
<div className="w-full bg-slate-100 rounded-full h-2">
<div
className="bg-orange-500 h-2 rounded-full transition-all"
style={{ width: `${(angle / 360) * 100}%` }}
></div>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="p-4 bg-white border border-slate-200 rounded-xl shadow-sm">
<span className="text-xs font-bold text-slate-400 uppercase">
Arc Length
</span>
<div className="font-mono text-lg text-slate-800 mt-1">
2π({Math.round(radius / 10)}) ×{" "}
<span className="text-orange-600">
{(angle / 360).toFixed(2)}
</span>
</div>
<div className="font-bold text-xl text-slate-900 mt-1">
= {(2 * Math.PI * (radius / 10) * (angle / 360)).toFixed(1)}
</div>
</div>
<div className="p-4 bg-white border border-slate-200 rounded-xl shadow-sm">
<span className="text-xs font-bold text-slate-400 uppercase">
Sector Area
</span>
<div className="font-mono text-lg text-slate-800 mt-1">
π({Math.round(radius / 10)})² ×{" "}
<span className="text-orange-600">
{(angle / 360).toFixed(2)}
</span>
</div>
<div className="font-bold text-xl text-slate-900 mt-1">
={" "}
{(Math.PI * Math.pow(radius / 10, 2) * (angle / 360)).toFixed(
1,
)}
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default InteractiveSectorWidget;