Files
edbridge-scholars/src/pages/student/lessons/LinearEquationsLesson.tsx
2026-03-01 20:24:14 +06:00

505 lines
20 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, { useRef, useState, useEffect } from "react";
import {
ArrowDown,
Check,
BookOpen,
Scale,
ArrowRight,
RotateCcw,
} from "lucide-react";
import LinearSolutionsWidget from "../../../components/lessons/LinearSolutionsWidget";
import Quiz from "../../../components/lessons/Quiz";
import { LINEAR_EQ_QUIZ_DATA } from "../../../utils/constants";
import { Frac } from "../../../components/Math";
interface LessonProps {
onFinish?: () => void;
}
const BalanceScaleWidget = () => {
const [left, setLeft] = useState(15);
const [right, setRight] = useState(15);
const [tilt, setTilt] = useState(0);
const [message, setMessage] = useState("Balanced");
const apply = (val: number, side: "both" | "left" | "right") => {
let newLeft = left;
let newRight = right;
if (side === "left" || side === "both") newLeft += val;
if (side === "right" || side === "both") newRight += val;
setLeft(newLeft);
setRight(newRight);
if (newLeft === newRight) {
setTilt(0);
setMessage("Perfectly Balanced! ✅");
} else if (newLeft > newRight) {
setTilt(-15);
setMessage("Unbalanced! Left side is heavier. ❌");
} else {
setTilt(15);
setMessage("Unbalanced! Right side is heavier. ❌");
}
};
const reset = () => {
setLeft(15);
setRight(15);
setTilt(0);
setMessage("Balanced");
};
return (
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
<div className="flex justify-between items-center mb-8">
<h3 className="font-bold text-slate-700">Algebra Balance Scale</h3>
<button onClick={reset} className="text-slate-400 hover:text-slate-600">
<RotateCcw className="w-4 h-4" />
</button>
</div>
<div className="relative h-40 w-full mb-8 flex justify-center items-end">
<div className="absolute bottom-0 w-0 h-0 border-l-[20px] border-l-transparent border-r-[20px] border-r-transparent border-b-[40px] border-b-slate-800"></div>
<div
className="w-64 h-2 bg-slate-600 absolute bottom-[40px] transition-transform duration-700"
style={{ transform: `rotate(${tilt}deg)` }}
>
<div
className="absolute left-0 top-0 flex flex-col items-center"
style={{ transform: `translateY(0px) rotate(${-tilt}deg)` }}
>
<div className="w-20 h-20 border-b-4 border-l-2 border-r-2 border-slate-400 rounded-b-xl bg-slate-50 flex items-center justify-center shadow-inner relative top-2">
<span className="font-bold text-blue-600 text-lg">{left} kg</span>
</div>
<div className="w-0.5 h-16 bg-slate-400 absolute -top-16"></div>
</div>
<div
className="absolute right-0 top-0 flex flex-col items-center"
style={{ transform: `translateY(0px) rotate(${-tilt}deg)` }}
>
<div className="w-20 h-20 border-b-4 border-l-2 border-r-2 border-slate-400 rounded-b-xl bg-slate-50 flex items-center justify-center shadow-inner relative top-2">
<span className="font-bold text-emerald-600 text-lg">
{right} kg
</span>
</div>
<div className="w-0.5 h-16 bg-slate-400 absolute -top-16"></div>
</div>
</div>
</div>
<div
className={`text-center font-bold mb-6 ${tilt === 0 ? "text-green-600" : "text-rose-600"}`}
>
{message}
</div>
<div className="grid grid-cols-2 gap-4">
<button
onClick={() => apply(-5, "left")}
className="p-3 rounded-lg border-2 border-slate-200 hover:border-rose-300 hover:bg-rose-50 text-slate-600 text-sm font-bold transition-all"
>
Subtract 5 from Left ONLY
</button>
<button
onClick={() => apply(-5, "both")}
className="p-3 rounded-lg border-2 border-blue-500 bg-blue-50 hover:bg-blue-100 text-blue-700 text-sm font-bold transition-all shadow-sm"
>
Subtract 5 from BOTH sides
</button>
</div>
<p className="text-xs text-center text-slate-400 mt-4">
Goal: Keep the scale balanced while isolating the variable.
</p>
</div>
);
};
const LinearEquationsLesson: React.FC<LessonProps> = ({ onFinish }) => {
const [activeSection, setActiveSection] = useState(0);
const sectionsRef = useRef<(HTMLElement | null)[]>([]);
const scrollToSection = (index: number) => {
setActiveSection(index);
sectionsRef.current[index]?.scrollIntoView({
behavior: "smooth",
block: "start",
});
};
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const index = sectionsRef.current.indexOf(
entry.target as HTMLElement,
);
if (index !== -1) setActiveSection(index);
}
});
},
{ rootMargin: "-20% 0px -60% 0px" },
);
sectionsRef.current.forEach((section) => {
if (section) observer.observe(section);
});
return () => observer.disconnect();
}, []);
const SectionMarker = ({
index,
title,
icon: Icon,
}: {
index: number;
title: string;
icon: any;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;
return (
<button
onClick={() => scrollToSection(index)}
className={`flex items-center gap-3 p-3 w-full rounded-lg transition-all ${isActive ? "bg-white shadow-md border border-blue-100" : "hover:bg-slate-100"}`}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${isActive ? "bg-blue-600 text-white" : isPast ? "bg-blue-400 text-white" : "bg-slate-200 text-slate-500"}`}
>
{isPast ? (
<Check className="w-4 h-4" />
) : (
<Icon className="w-4 h-4" />
)}
</div>
<div className="text-left">
<p
className={`text-sm font-bold ${isActive ? "text-blue-900" : "text-slate-600"}`}
>
{title}
</p>
</div>
</button>
);
};
return (
<div className="flex flex-col lg:flex-row min-h-screen">
<aside className="w-full lg:w-64 lg:fixed lg:top-20 lg:bottom-0 lg:overflow-y-auto p-4 border-r border-slate-200 bg-slate-50 z-0 hidden lg:block">
<nav className="space-y-2">
<SectionMarker index={0} title="The Balance Principle" icon={Scale} />
<SectionMarker
index={1}
title="Number of Solutions"
icon={ArrowRight}
/>
<SectionMarker index={2} title="Practice" icon={BookOpen} />
</nav>
</aside>
<div className="flex-1 lg:ml-64 p-6 md:p-12 max-w-4xl mx-auto">
{/* Section 1: Balance Principle */}
<section
ref={(el) => {
sectionsRef.current[0] = el;
}}
className="min-h-screen flex flex-col justify-center mb-24 pt-20 lg:pt-0"
>
<h2 className="text-4xl font-extrabold text-slate-900 mb-6">
The Balance Principle
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-6">
<p>
Linear equations are like a balance scale. Whatever you do to one
side, you <strong>must</strong> do to the other. The goal is
always to <strong>isolate the variable</strong> get x by itself
on one side with a number on the other.
</p>
</div>
<BalanceScaleWidget />
{/* 4-Step Process */}
<div className="mt-8 bg-blue-50 border border-blue-200 rounded-2xl p-6 space-y-4">
<h3 className="font-bold text-blue-900 text-lg">
The 4-Step Solve Process
</h3>
<div className="space-y-3">
{[
{
step: "1",
title: "Distribute",
desc: "Expand parentheses and clear fractions by multiplying through by the LCD (lowest common denominator).",
},
{
step: "2",
title: "Combine Like Terms",
desc: "Simplify each side of the equation separately — add/subtract terms with the same variable.",
},
{
step: "3",
title: "Move",
desc: "Get all variable terms on one side, all constant terms on the other, using addition or subtraction.",
},
{
step: "4",
title: "Divide",
desc: "Divide both sides by the coefficient of the variable to solve.",
},
].map((item) => (
<div
key={item.step}
className="flex gap-4 bg-white rounded-xl p-4 border border-blue-100"
>
<div className="w-8 h-8 bg-blue-600 text-white rounded-full flex items-center justify-center font-bold shrink-0">
{item.step}
</div>
<div>
<p className="font-bold text-slate-800">{item.title}</p>
<p className="text-slate-600 text-sm">{item.desc}</p>
</div>
</div>
))}
</div>
</div>
{/* Three Worked Examples */}
<div className="mt-8 space-y-4">
<div className="bg-emerald-50 border border-emerald-200 rounded-xl p-6">
<p className="font-bold text-emerald-800 mb-3">
Example 1: Basic (no fractions)
</p>
<div className="font-mono text-sm space-y-1 text-slate-700">
<p>Solve: 3(2x 4) = 2x + 8</p>
<p className="text-slate-500">
Step 1 (Distribute): 6x 12 = 2x + 8
</p>
<p className="text-slate-500">Step 3 (Move): 4x = 20</p>
<p className="text-slate-500">
Step 4 (Divide by 4):{" "}
<strong className="text-emerald-700">x = 5</strong>
</p>
</div>
</div>
<div className="bg-emerald-50 border border-emerald-200 rounded-xl p-6">
<p className="font-bold text-emerald-800 mb-3">
Example 2: With fractions clear them first!
</p>
<div className="font-mono text-sm space-y-1 text-slate-700">
<p>
Solve: <Frac n="x" d="3" /> + 2 = <Frac n="x" d="2" /> 1
</p>
<p className="text-slate-500">
Multiply every term by 6 (LCD of 3 and 2):
</p>
<p className="text-slate-500">2x + 12 = 3x 6</p>
<p className="text-slate-500">12 + 6 = 3x 2x</p>
<p className="text-slate-500">
<strong className="text-emerald-700">x = 18</strong>
</p>
</div>
</div>
<div className="bg-emerald-50 border border-emerald-200 rounded-xl p-6">
<p className="font-bold text-emerald-800 mb-3">
Example 3: Literal equations (isolating a variable)
</p>
<div className="font-mono text-sm space-y-1 text-slate-700">
<p>Solve for r: A = P(1 + rt)</p>
<p className="text-slate-500">
Divide both sides by P: <Frac n="A" d="P" /> = 1 + rt
</p>
<p className="text-slate-500">
Subtract 1: <Frac n="A" d="P" /> 1 = rt
</p>
<p className="text-slate-500">
Divide by t:{" "}
<strong className="text-emerald-700">
r ={" "}
<Frac
n={
<>
<Frac n="A" d="P" /> 1
</>
}
d="t"
/>
</strong>
</p>
</div>
</div>
</div>
<div className="mt-6 bg-red-50 border border-red-200 rounded-xl p-4 text-sm">
<p className="font-bold text-red-800 mb-1">
Common SAT Mistake: Distributing a Negative
</p>
<p className="text-slate-700">
When distributing a negative sign: 3(x 4) = 3x + 12, NOT 3x
12. The negative multiplies EVERY term inside the parentheses.
</p>
</div>
<button
onClick={() => scrollToSection(1)}
className="mt-8 group flex items-center text-blue-600 font-bold hover:text-blue-800 transition-colors"
>
Next: Number of Solutions{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 2: Number of Solutions */}
<section
ref={(el) => {
sectionsRef.current[1] = el;
}}
className="min-h-screen flex flex-col justify-center mb-24"
>
<h2 className="text-4xl font-extrabold text-slate-900 mb-6">
One, None, or Infinite Solutions?
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-8">
<p>
Not all equations have exactly one answer. After simplifying, look
at what's left. The SAT frequently asks for a value of <em>k</em>{" "}
that makes an equation have no solution or infinitely many
solutions this is a critical concept to master.
</p>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-2xl p-6 mb-8 space-y-4">
<h3 className="text-lg font-bold text-blue-900">
The Three Outcomes
</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-blue-100 rounded-xl p-4 text-center">
<div className="font-bold text-blue-900 text-base mb-2">
One Solution
</div>
<div className="font-mono text-sm text-blue-700 bg-white rounded p-2 mb-2">
2x + 1 = x + 5
</div>
<div className="text-xs text-blue-700">
Variable survives after simplification unique answer:{" "}
<strong>x = 4</strong>
</div>
</div>
<div className="bg-red-50 border border-red-200 rounded-xl p-4 text-center">
<div className="font-bold text-red-900 text-base mb-2">
No Solution
</div>
<div className="font-mono text-sm text-red-700 bg-white rounded p-2 mb-2">
2x + 3 = 2x + 5
</div>
<div className="text-xs text-red-700">
Variables cancel <strong>3 = 5</strong> (false statement
impossible)
</div>
</div>
<div className="bg-emerald-50 border border-emerald-200 rounded-xl p-4 text-center">
<div className="font-bold text-emerald-900 text-base mb-2">
Infinite Solutions
</div>
<div className="font-mono text-sm text-emerald-700 bg-white rounded p-2 mb-2">
2x + 3 = 2x + 3
</div>
<div className="text-xs text-emerald-700">
Variables cancel <strong>3 = 3</strong> (always true every
x works)
</div>
</div>
</div>
</div>
{/* Finding k */}
<div className="bg-blue-50 border border-blue-200 rounded-2xl p-6 mb-8 space-y-4">
<h3 className="text-lg font-bold text-blue-900">
SAT Focus: Finding the Value of k
</h3>
<div className="bg-white rounded-xl p-5 border border-blue-100">
<p className="font-bold text-blue-800 mb-3">
Example: For what value of k does 4x + k = 4x 2 have no
solution?
</p>
<div className="font-mono text-sm text-slate-700 space-y-1">
<p>After subtracting 4x from both sides: k = 2</p>
<p>
If k = 2: 2 = 2 (always true) infinite solutions, not no
solution!
</p>
<p className="text-blue-700 font-bold">
For no solution: k 2 (any other value gives a false
statement like k = 2 being false)
</p>
</div>
<p className="text-slate-500 text-xs mt-2">
The equation has no solution for any value of k EXCEPT 2. For
infinite solutions, set k = 2.
</p>
</div>
<div className="bg-white rounded-xl p-5 border border-blue-100">
<p className="font-bold text-blue-800 mb-3">
Example: For what value of k does 3(x + k) = 3x + 12 have
infinite solutions?
</p>
<div className="font-mono text-sm text-slate-700 space-y-1">
<p>Distribute: 3x + 3k = 3x + 12</p>
<p>Subtract 3x: 3k = 12</p>
<p className="text-blue-700 font-bold">k = 4</p>
</div>
<p className="text-slate-500 text-xs mt-2">
For k = 4: 12 = 12 (always true) infinite solutions.
</p>
</div>
<div className="bg-red-50 border border-red-200 rounded-xl p-4 text-sm">
<p className="font-bold text-red-800 mb-1">The Key Rule</p>
<p className="text-slate-700">
<strong>No solution</strong>: same variable coefficients,
different constants (parallel lines).
<br />
<strong>Infinite solutions</strong>: same variable coefficients
AND same constants (identical lines).
</p>
</div>
</div>
<LinearSolutionsWidget />
<button
onClick={() => scrollToSection(2)}
className="mt-12 group flex items-center text-blue-600 font-bold hover:text-blue-800 transition-colors"
>
Next: Practice Quiz{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 3: Quiz */}
<section
ref={(el) => {
sectionsRef.current[2] = el;
}}
className="min-h-screen flex flex-col justify-center"
>
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{LINEAR_EQ_QUIZ_DATA.map((quiz, idx) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>
))}
<div className="p-8 bg-blue-900 rounded-2xl text-white text-center mt-12">
<h3 className="text-2xl font-bold mb-4">Topic Mastered!</h3>
<button
onClick={onFinish}
className="px-6 py-3 bg-white text-blue-900 font-bold rounded-full hover:bg-blue-50 transition-colors"
>
Finish Lesson
</button>
</div>
</section>
</div>
</div>
);
};
export default LinearEquationsLesson;