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,871 @@
import React, { useState } from "react";
import {
Square,
Circle,
Box,
Layers,
Ruler,
BookOpen,
ChevronDown,
} from "lucide-react";
import LessonShell, {
ConceptCard,
FormulaBox,
ExampleCard,
TipCard,
PracticeFromDataset,
} from "../../../components/lessons/LessonShell";
import { Frac } from "../../../components/Math";
import CompositeAreaWidget from "../../../components/lessons/CompositeAreaWidget";
import InteractiveSectorWidget from "../../../components/lessons/InteractiveSectorWidget";
import CompositeSolidsWidget from "../../../components/lessons/CompositeSolidsWidget";
import ScaleFactorWidget from "../../../components/lessons/ScaleFactorWidget";
import { AREA_VOL_EASY, AREA_VOL_MEDIUM } from "../../../data/math/area-volume";
/* ─── Clickable formula card with shape diagram + example ─── */
function FormulaCard({
formula,
diagram,
example,
}: {
formula: React.ReactNode;
diagram: React.ReactNode;
example: React.ReactNode;
}) {
const [open, setOpen] = useState(false);
return (
<button
onClick={() => setOpen(!open)}
className={`w-full text-left glass-formula rounded-xl border transition-all duration-300 overflow-hidden ${open ? "border-emerald-300 shadow-md" : "border-emerald-100 hover:border-emerald-200"}`}
>
<div className="flex items-center justify-between py-3 px-5">
<span className="font-mono text-base font-bold text-slate-800">
{formula}
</span>
<ChevronDown
className={`w-4 h-4 text-emerald-400 shrink-0 transition-transform duration-300 ${open ? "rotate-180" : ""}`}
/>
</div>
<div
className={`transition-all duration-300 ease-in-out ${open ? "max-h-[400px] opacity-100" : "max-h-0 opacity-0"}`}
>
<div className="border-t border-emerald-100 px-5 py-4 flex flex-col sm:flex-row items-center gap-5 bg-gradient-to-br from-emerald-50/50 to-white/80">
<div className="shrink-0">{diagram}</div>
<div className="text-sm text-slate-600 space-y-1 font-mono">
{example}
</div>
</div>
</div>
</button>
);
}
/* ─── Shape SVG diagrams ─── */
const RectSvg = () => (
<svg width="140" height="95" viewBox="-5 -5 130 90" overflow="visible">
<rect
x="10"
y="10"
width="100"
height="60"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
rx="2"
/>
<text
x="60"
y="82"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
l = 8
</text>
<text
x="2"
y="44"
textAnchor="end"
className="text-[13px] font-bold fill-emerald-700"
transform="rotate(-90 2 44)"
>
w = 5
</text>
</svg>
);
const TriSvg = () => (
<svg width="140" height="105" viewBox="-5 -5 130 105" overflow="visible">
<polygon
points="60,10 10,80 110,80"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<line
x1="60"
y1="10"
x2="60"
y2="80"
stroke="#059669"
strokeWidth="1.5"
strokeDasharray="4"
/>
<text
x="60"
y="96"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
b = 10
</text>
<text x="68" y="50" className="text-[13px] font-bold fill-emerald-600">
h = 6
</text>
</svg>
);
const ParaSvg = () => (
<svg width="150" height="95" viewBox="-5 -5 140 90" overflow="visible">
<polygon
points="30,10 120,10 100,70 10,70"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<line
x1="30"
y1="10"
x2="30"
y2="70"
stroke="#059669"
strokeWidth="1.5"
strokeDasharray="4"
/>
<text
x="60"
y="84"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
b = 9
</text>
<text x="20" y="44" className="text-[13px] font-bold fill-emerald-600">
h = 4
</text>
</svg>
);
const TrapSvg = () => (
<svg width="150" height="105" viewBox="-5 -5 140 105" overflow="visible">
<polygon
points="35,18 95,18 115,75 15,75"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<line
x1="35"
y1="18"
x2="35"
y2="75"
stroke="#059669"
strokeWidth="1.5"
strokeDasharray="4"
/>
<text
x="65"
y="14"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
b = 6
</text>
<text
x="65"
y="92"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
b = 10
</text>
<text x="25" y="52" className="text-[13px] font-bold fill-emerald-600">
h = 4
</text>
</svg>
);
const CircAreaSvg = () => (
<svg width="120" height="110" viewBox="-5 -5 110 110" overflow="visible">
<circle
cx="50"
cy="50"
r="40"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<line x1="50" y1="50" x2="90" y2="50" stroke="#059669" strokeWidth="2" />
<circle cx="50" cy="50" r="2.5" fill="#059669" />
<text x="70" y="44" className="text-[13px] font-bold fill-emerald-700">
r = 5
</text>
</svg>
);
const CircCircSvg = () => (
<svg width="120" height="110" viewBox="-5 -5 110 110" overflow="visible">
<circle
cx="50"
cy="50"
r="40"
fill="none"
stroke="#059669"
strokeWidth="2"
/>
<line x1="10" y1="50" x2="90" y2="50" stroke="#059669" strokeWidth="2" />
<text
x="50"
y="44"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
d = 10
</text>
</svg>
);
const PrismSASvg = () => (
<svg width="140" height="115" viewBox="-5 -5 130 115" overflow="visible">
<polygon
points="20,40 70,40 70,90 20,90"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<polygon
points="20,40 45,20 95,20 70,40"
fill="#a7f3d0"
stroke="#059669"
strokeWidth="2"
/>
<polygon
points="70,40 95,20 95,70 70,90"
fill="#6ee7b7"
stroke="#059669"
strokeWidth="2"
/>
<text
x="45"
y="105"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
l = 4
</text>
<text x="10" y="68" className="text-[13px] font-bold fill-emerald-600">
h = 2
</text>
<text x="88" y="48" className="text-[13px] font-bold fill-emerald-700">
w = 3
</text>
</svg>
);
const CylSASvg = () => (
<svg width="120" height="120" viewBox="-5 -5 115 120" overflow="visible">
<ellipse
cx="50"
cy="25"
rx="35"
ry="12"
fill="#a7f3d0"
stroke="#059669"
strokeWidth="2"
/>
<rect x="15" y="25" width="70" height="60" fill="#d1fae5" />
<line x1="15" y1="25" x2="15" y2="85" stroke="#059669" strokeWidth="2" />
<line x1="85" y1="25" x2="85" y2="85" stroke="#059669" strokeWidth="2" />
<ellipse
cx="50"
cy="85"
rx="35"
ry="12"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<line
x1="50"
y1="25"
x2="85"
y2="25"
stroke="#059669"
strokeWidth="1.5"
strokeDasharray="3"
/>
<text x="67" y="19" className="text-[13px] font-bold fill-emerald-700">
r = 3
</text>
<text x="92" y="58" className="text-[13px] font-bold fill-emerald-600">
h = 5
</text>
</svg>
);
const SphereSvg = () => (
<svg width="120" height="110" viewBox="-5 -5 110 110" overflow="visible">
<circle
cx="50"
cy="50"
r="40"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<ellipse
cx="50"
cy="50"
rx="40"
ry="14"
fill="none"
stroke="#059669"
strokeWidth="1"
strokeDasharray="4"
/>
<line x1="50" y1="50" x2="90" y2="50" stroke="#059669" strokeWidth="2" />
<circle cx="50" cy="50" r="2.5" fill="#059669" />
<text x="70" y="44" className="text-[13px] font-bold fill-emerald-700">
r = 4
</text>
</svg>
);
const PrismVolSvg = () => (
<svg width="140" height="115" viewBox="-5 -5 130 115" overflow="visible">
<polygon
points="20,40 70,40 70,90 20,90"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<polygon
points="20,40 45,20 95,20 70,40"
fill="#a7f3d0"
stroke="#059669"
strokeWidth="2"
/>
<polygon
points="70,40 95,20 95,70 70,90"
fill="#6ee7b7"
stroke="#059669"
strokeWidth="2"
/>
<text
x="45"
y="105"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
l = 6
</text>
<text x="10" y="68" className="text-[13px] font-bold fill-emerald-600">
h = 4
</text>
<text x="88" y="48" className="text-[13px] font-bold fill-emerald-700">
w = 3
</text>
</svg>
);
const CylVolSvg = () => (
<svg width="120" height="120" viewBox="-5 -5 115 120" overflow="visible">
<ellipse
cx="50"
cy="25"
rx="35"
ry="12"
fill="#a7f3d0"
stroke="#059669"
strokeWidth="2"
/>
<rect x="15" y="25" width="70" height="60" fill="#d1fae5" />
<line x1="15" y1="25" x2="15" y2="85" stroke="#059669" strokeWidth="2" />
<line x1="85" y1="25" x2="85" y2="85" stroke="#059669" strokeWidth="2" />
<ellipse
cx="50"
cy="85"
rx="35"
ry="12"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<text x="67" y="19" className="text-[13px] font-bold fill-emerald-700">
r = 5
</text>
<text x="92" y="58" className="text-[13px] font-bold fill-emerald-600">
h = 8
</text>
</svg>
);
const ConeSvg = () => (
<svg width="120" height="120" viewBox="-5 -5 115 120" overflow="visible">
<polygon
points="50,10 15,90 85,90"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<ellipse
cx="50"
cy="90"
rx="35"
ry="12"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<line
x1="50"
y1="10"
x2="50"
y2="90"
stroke="#059669"
strokeWidth="1.5"
strokeDasharray="4"
/>
<text x="55" y="56" className="text-[13px] font-bold fill-emerald-600">
h = 9
</text>
<text x="67" y="96" className="text-[13px] font-bold fill-emerald-700">
r = 4
</text>
</svg>
);
const SphereVolSvg = () => (
<svg width="120" height="110" viewBox="-5 -5 110 110" overflow="visible">
<circle
cx="50"
cy="50"
r="40"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<ellipse
cx="50"
cy="50"
rx="40"
ry="14"
fill="none"
stroke="#059669"
strokeWidth="1"
strokeDasharray="4"
/>
<line x1="50" y1="50" x2="90" y2="50" stroke="#059669" strokeWidth="2" />
<circle cx="50" cy="50" r="2.5" fill="#059669" />
<text x="70" y="44" className="text-[13px] font-bold fill-emerald-700">
r = 6
</text>
</svg>
);
const PyramidSvg = () => (
<svg width="130" height="125" viewBox="-5 -5 120 125" overflow="visible">
<polygon
points="55,10 10,90 100,90"
fill="#d1fae5"
stroke="#059669"
strokeWidth="2"
/>
<polygon
points="55,10 100,90 80,100 35,100 10,90"
fill="#d1fae5"
stroke="#059669"
strokeWidth="1.5"
/>
<line
x1="55"
y1="10"
x2="55"
y2="90"
stroke="#059669"
strokeWidth="1.5"
strokeDasharray="4"
/>
<text x="62" y="56" className="text-[13px] font-bold fill-emerald-600">
h = 12
</text>
<text
x="55"
y="114"
textAnchor="middle"
className="text-[13px] font-bold fill-emerald-700"
>
B = 25
</text>
</svg>
);
interface LessonProps {
onFinish?: () => void;
}
const SECTIONS = [
{ title: "Area Formulas", icon: Square },
{ title: "Composite Shapes", icon: Layers },
{ title: "Arc Length & Sector Area", icon: Circle },
{ title: "Surface Area", icon: Box },
{ title: "Volume", icon: Ruler },
{ title: "Practice & Quiz", icon: BookOpen },
];
export default function AreaVolumeLesson({ onFinish }: LessonProps) {
return (
<LessonShell
title="Area & Volume"
sections={SECTIONS}
color="emerald"
onFinish={onFinish}
>
{/* Section 1: Area Formulas */}
<div>
<h2 className="text-3xl font-extrabold text-slate-900 mb-6">
Area Formulas
</h2>
<ConceptCard color="emerald">
<p className="text-slate-700 leading-relaxed">
All area formulas are on the SAT reference sheet, but knowing them
saves time. <strong>Tap any formula</strong> to see a diagram and
worked example.
</p>
<div className="grid md:grid-cols-2 gap-3 mt-4">
<FormulaCard
formula="Rectangle: A = l × w"
diagram={<RectSvg />}
example={
<>
<p>l = 8, w = 5</p>
<p>
A = 8 × 5 = <strong className="text-emerald-700">40</strong>
</p>
</>
}
/>
<FormulaCard
formula={
<>
Triangle: A = <Frac n="1" d="2" /> × b × h
</>
}
diagram={<TriSvg />}
example={
<>
<p>b = 10, h = 6</p>
<p>
A = ½ × 10 × 6 ={" "}
<strong className="text-emerald-700">30</strong>
</p>
</>
}
/>
<FormulaCard
formula="Parallelogram: A = b × h"
diagram={<ParaSvg />}
example={
<>
<p>b = 9, h = 4</p>
<p>
A = 9 × 4 = <strong className="text-emerald-700">36</strong>
</p>
</>
}
/>
<FormulaCard
formula={
<>
Trapezoid: A = <Frac n="1" d="2" />
(b + b) × h
</>
}
diagram={<TrapSvg />}
example={
<>
<p>b = 6, b = 10, h = 4</p>
<p>
A = ½(6+10) × 4 ={" "}
<strong className="text-emerald-700">32</strong>
</p>
</>
}
/>
<FormulaCard
formula="Circle: A = πr²"
diagram={<CircAreaSvg />}
example={
<>
<p>r = 5</p>
<p>
A = π(25) ={" "}
<strong className="text-emerald-700">25π 78.5</strong>
</p>
</>
}
/>
<FormulaCard
formula="Circumference: C = 2πr = πd"
diagram={<CircCircSvg />}
example={
<>
<p>d = 10 (r = 5)</p>
<p>
C = 2π(5) ={" "}
<strong className="text-emerald-700">10π 31.4</strong>
</p>
</>
}
/>
</div>
</ConceptCard>
<div className="mt-4">
<TipCard type="warning">
<p className="text-slate-700">
The height of a triangle is ALWAYS perpendicular to the base
it's NOT the side length (unless it's a right triangle).
</p>
</TipCard>
</div>
</div>
{/* Section 2: Composite Shapes */}
<div>
<h2 className="text-3xl font-extrabold text-slate-900 mb-6">
Composite Shapes
</h2>
<ConceptCard color="emerald">
<p className="text-slate-700 leading-relaxed">
Break complex shapes into simpler pieces. <strong>Add</strong> areas
for combined shapes. <strong>Subtract</strong> for cut-out regions.
</p>
</ConceptCard>
<ExampleCard title="Example: L-Shape" color="emerald">
<p>10 × 8 rectangle with a 4 × 3 piece cut out</p>
<p className="text-slate-500">
80 12 ={" "}
<strong className="text-emerald-700">68 square units</strong>
</p>
</ExampleCard>
<div className="mt-4">
<ExampleCard title="Example: Shaded Region" color="emerald">
<p>Square with side 10 and inscribed circle (r = 5)</p>
<p className="text-slate-500">
Shaded area = 100 25π {" "}
<strong className="text-emerald-700">21.5 square units</strong>
</p>
</ExampleCard>
</div>
<div className="mt-6">
<CompositeAreaWidget />
</div>
</div>
{/* Section 3: Arc Length & Sector Area */}
<div>
<h2 className="text-3xl font-extrabold text-slate-900 mb-6">
Arc Length & Sector Area
</h2>
<ConceptCard color="emerald">
<p className="text-slate-700 leading-relaxed">
A sector is a "pizza slice" of a circle. The fraction of the circle
used = central angle ÷ 360°.
</p>
<div className="space-y-3 mt-4">
<FormulaBox>Arc Length = (θ ÷ 360) × 2πr</FormulaBox>
<FormulaBox>Sector Area = (θ ÷ 360) × πr²</FormulaBox>
</div>
</ConceptCard>
<ExampleCard title="Example" color="emerald">
<p>Circle with r = 6, central angle = 60°</p>
<p className="text-slate-500">
Arc = (60 ÷ 360) × 2π(6) = <Frac n="1" d="6" /> × 12π = 2π
</p>
<p className="text-slate-500">
Sector = (60 ÷ 360) × π(36) ={" "}
<strong className="text-emerald-700">6π</strong>
</p>
</ExampleCard>
<div className="mt-6">
<InteractiveSectorWidget />
</div>
</div>
{/* Section 4: Surface Area */}
<div>
<h2 className="text-3xl font-extrabold text-slate-900 mb-6">
Surface Area
</h2>
<ConceptCard color="emerald">
<p className="text-slate-700 leading-relaxed">
Surface area is the total area of all faces or surfaces of a 3D
shape. <strong>Tap any formula</strong> to see the shape and a
worked example.
</p>
<div className="space-y-3 mt-4">
<FormulaCard
formula="Rectangular Prism: SA = 2(lw + lh + wh)"
diagram={<PrismSASvg />}
example={
<>
<p>l = 4, w = 3, h = 2</p>
<p>SA = 2(12 + 8 + 6)</p>
<p>
SA = 2(26) ={" "}
<strong className="text-emerald-700">52</strong>
</p>
</>
}
/>
<FormulaCard
formula="Cylinder: SA = 2πr² + 2πrh"
diagram={<CylSASvg />}
example={
<>
<p>r = 3, h = 5</p>
<p>SA = 2π(9) + 2π(15)</p>
<p>
SA = 18π + 30π ={" "}
<strong className="text-emerald-700">48π 150.8</strong>
</p>
</>
}
/>
<FormulaCard
formula="Sphere: SA = 4πr²"
diagram={<SphereSvg />}
example={
<>
<p>r = 4</p>
<p>
SA = 4π(16) ={" "}
<strong className="text-emerald-700">64π 201.1</strong>
</p>
</>
}
/>
</div>
</ConceptCard>
</div>
{/* Section 5: Volume */}
<div>
<h2 className="text-3xl font-extrabold text-slate-900 mb-6">Volume</h2>
<ConceptCard color="emerald">
<p className="text-slate-700 leading-relaxed">
Volume formulas are provided on the SAT reference sheet.{" "}
<strong>Tap any formula</strong> to explore.
</p>
<div className="space-y-3 mt-4">
<FormulaCard
formula="Rectangular Prism: V = lwh"
diagram={<PrismVolSvg />}
example={
<>
<p>l = 6, w = 3, h = 4</p>
<p>
V = 6 × 3 × 4 ={" "}
<strong className="text-emerald-700">72</strong>
</p>
</>
}
/>
<FormulaCard
formula="Cylinder: V = πr²h"
diagram={<CylVolSvg />}
example={
<>
<p>r = 5, h = 8</p>
<p>
V = π(25)(8) ={" "}
<strong className="text-emerald-700">200π 628.3</strong>
</p>
</>
}
/>
<FormulaCard
formula={
<>
Cone: V = <Frac n="1" d="3" />
πr²h
</>
}
diagram={<ConeSvg />}
example={
<>
<p>r = 4, h = 9</p>
<p>V = × π(16)(9)</p>
<p>
V ={" "}
<strong className="text-emerald-700">48π 150.8</strong>
</p>
</>
}
/>
<FormulaCard
formula={
<>
Sphere: V = <Frac n="4" d="3" />
πr³
</>
}
diagram={<SphereVolSvg />}
example={
<>
<p>r = 6</p>
<p>V = × π(216)</p>
<p>
V ={" "}
<strong className="text-emerald-700">288π 904.8</strong>
</p>
</>
}
/>
<FormulaCard
formula={
<>
Pyramid: V = <Frac n="1" d="3" />
Bh
</>
}
diagram={<PyramidSvg />}
example={
<>
<p>B = 25 (5×5 base), h = 12</p>
<p>
V = × 25 × 12 ={" "}
<strong className="text-emerald-700">100</strong>
</p>
</>
}
/>
</div>
</ConceptCard>
<div className="mt-6">
<CompositeSolidsWidget />
</div>
<div className="mt-6">
<ScaleFactorWidget />
</div>
<div className="mt-4">
<TipCard type="remember">
<p className="text-slate-700">
A cone is <Frac n="1" d="3" /> of a cylinder with the same base
and height. A pyramid is <Frac n="1" d="3" /> of a prism with the
same base and height.
</p>
</TipCard>
</div>
</div>
{/* Section 6: Practice */}
<div>
<h2 className="text-3xl font-extrabold text-slate-900 mb-6">
Practice
</h2>
{AREA_VOL_EASY.slice(0, 2).map((q) => (
<PracticeFromDataset key={q.id} question={q} color="emerald" />
))}
{AREA_VOL_MEDIUM.slice(0, 1).map((q) => (
<PracticeFromDataset key={q.id} question={q} color="emerald" />
))}
</div>
</LessonShell>
);
}