Files
edbridge-scholars/src/pages/student/lessons/AreaVolumeLesson.tsx
2026-03-12 02:39:34 +06:00

872 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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-100 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-linear-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>
);
}