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

327 lines
13 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, TrendingUp } from "lucide-react";
import LinearTransformationWidget from "../../../components/lessons/LinearTransformationWidget";
import Quiz from "../../../components/lessons/Quiz";
import { LINEAR_TRANSFORMATIONS_QUIZ_DATA } from "../../../utils/constants";
interface LessonProps {
onFinish?: () => void;
}
const LinearTransformationsLesson: 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="Shift, Reflect & Scale"
icon={TrendingUp}
/>
<SectionMarker index={1} 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 */}
<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">
Function Transformations
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-8">
<p>
Given a base function f(x), transformations let you shift, flip,
and scale it predictably. These rules apply to <em>any</em>{" "}
function type linear, quadratic, absolute value, or otherwise.
The SAT tests these with graphs, tables, and algebraic forms, so
you must recognize them quickly.
</p>
</div>
{/* Transformation Table */}
<div className="bg-blue-50 border border-blue-200 rounded-2xl p-6 mb-8 space-y-5">
<h3 className="text-lg font-bold text-blue-900">
All Six Transformations
</h3>
<div className="overflow-x-auto rounded-xl border border-blue-200">
<table className="w-full text-sm border-collapse">
<thead>
<tr className="bg-blue-900 text-white">
<th className="p-3 text-left">Transformation</th>
<th className="p-3 text-left">Notation</th>
<th className="p-3 text-left">Effect on Graph</th>
<th className="p-3 text-left">Effect on Points</th>
</tr>
</thead>
<tbody className="divide-y divide-blue-100">
<tr className="bg-white">
<td className="p-3 font-bold">Shift Up k units</td>
<td className="p-3 font-mono text-blue-700">f(x) + k</td>
<td className="p-3 text-slate-600">
Entire graph moves up by k
</td>
<td className="p-3 text-slate-600">(x, y) (x, y + k)</td>
</tr>
<tr className="bg-slate-50">
<td className="p-3 font-bold">Shift Down k units</td>
<td className="p-3 font-mono text-blue-700">f(x) k</td>
<td className="p-3 text-slate-600">
Entire graph moves down by k
</td>
<td className="p-3 text-slate-600">(x, y) (x, y k)</td>
</tr>
<tr className="bg-red-50">
<td className="p-3 font-bold text-red-900">
Shift Right h units
</td>
<td className="p-3 font-mono text-red-700">f(x h)</td>
<td className="p-3 text-red-800">Graph moves RIGHT by h</td>
<td className="p-3 text-red-700">(x, y) (x + h, y)</td>
</tr>
<tr className="bg-red-50">
<td className="p-3 font-bold text-red-900">
Shift Left h units
</td>
<td className="p-3 font-mono text-red-700">f(x + h)</td>
<td className="p-3 text-red-800">Graph moves LEFT by h</td>
<td className="p-3 text-red-700">(x, y) (x h, y)</td>
</tr>
<tr className="bg-white">
<td className="p-3 font-bold">Reflect over x-axis</td>
<td className="p-3 font-mono text-blue-700">f(x)</td>
<td className="p-3 text-slate-600">
Graph flips vertically
</td>
<td className="p-3 text-slate-600">(x, y) (x, y)</td>
</tr>
<tr className="bg-slate-50">
<td className="p-3 font-bold">Reflect over y-axis</td>
<td className="p-3 font-mono text-blue-700">f(x)</td>
<td className="p-3 text-slate-600">
Graph flips horizontally
</td>
<td className="p-3 text-slate-600">(x, y) (x, y)</td>
</tr>
</tbody>
</table>
</div>
{/* The #1 Trap */}
<div className="bg-red-100 border border-red-300 rounded-xl p-5">
<p className="font-bold text-red-900 text-base mb-2">
The #1 Trap Horizontal Shifts Are BACKWARDS
</p>
<p className="text-slate-700 text-sm mb-2">
f(x 3) shifts the graph <strong>right 3</strong> (NOT left).
f(x + 2) shifts the graph <strong>left 2</strong> (NOT right).
</p>
<p className="text-slate-600 text-sm">
Why? Because to get the same y-value, x must be 3 larger. The
shift in the graph is always <em>opposite</em> to the sign
inside.
</p>
<div className="font-mono text-sm mt-2 bg-white rounded p-2 text-slate-700">
<p>f(x) = x² has vertex at (0, 0)</p>
<p>g(x) = (x 3)² vertex at (3, 0) shifted RIGHT 3 </p>
<p>h(x) = (x + 2)² vertex at (2, 0) shifted LEFT 2 </p>
</div>
</div>
{/* Combined Transformations */}
<div className="bg-white rounded-xl p-5 border border-blue-100">
<p className="font-bold text-blue-800 mb-3">
Combined Transformations Apply in Order
</p>
<p className="text-slate-600 text-sm mb-3">
When multiple transformations are applied, you can read them
directly from the equation. Apply in this order: horizontal
shift vertical stretch/compress reflection vertical shift.
</p>
<div className="space-y-3">
<div className="bg-blue-50 rounded-lg p-4 text-sm">
<p className="font-semibold text-blue-800 mb-2">
Example: g(x) = f(x 2) + 3
</p>
<div className="space-y-1 text-slate-700">
<div className="flex gap-2">
<span className="font-bold text-red-600">
shift right 2:
</span>
<span>f(x 2) moves graph right 2</span>
</div>
<div className="flex gap-2">
<span className="font-bold text-purple-600">
reflect over x-axis:
</span>
<span>f(...) flips graph vertically</span>
</div>
<div className="flex gap-2">
<span className="font-bold text-blue-600">
shift up 3:
</span>
<span>+ 3 moves graph up 3</span>
</div>
<p className="font-mono text-blue-700 font-bold mt-1">
If f has point (4, 1), then g has point (4 + 2, 1 + 3) =
(6, 2)
</p>
</div>
</div>
<div className="bg-blue-50 rounded-lg p-4 text-sm">
<p className="font-semibold text-blue-800 mb-2">
Example: h(x) = 2f(x + 1) 4
</p>
<div className="space-y-1 text-slate-700">
<div className="flex gap-2">
<span className="font-bold text-red-600">
shift left 1:
</span>
<span>f(x + 1) moves graph left 1</span>
</div>
<div className="flex gap-2">
<span className="font-bold text-green-600">
stretch vertically by 2:
</span>
<span>all y-values multiply by 2</span>
</div>
<div className="flex gap-2">
<span className="font-bold text-blue-600">
shift down 4:
</span>
<span> 4 moves graph down 4</span>
</div>
</div>
</div>
</div>
</div>
{/* SAT Question Type */}
<div className="bg-sky-50 border border-sky-200 rounded-xl p-4 text-sm">
<p className="font-bold text-sky-900 mb-1">
SAT Question Type: Table of Values
</p>
<p className="text-slate-700">
The SAT may give you a table of values for f(x) and ask for
values of g(x) = f(x 2) + 1. Strategy: for each value in the
g(x) table, work backwards. To find g(3), you need f(3 2) + 1
= f(1) + 1. Look up f(1) in the original table, then add 1.
</p>
</div>
</div>
<LinearTransformationWidget />
<button
onClick={() => scrollToSection(1)}
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 2: Quiz */}
<section
ref={(el) => {
sectionsRef.current[1] = 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_TRANSFORMATIONS_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 LinearTransformationsLesson;