Files
edbridge-scholars/src/pages/student/lessons/DataAnalysisLesson.tsx

290 lines
9.4 KiB
TypeScript

import React, { useRef, useState, useEffect } from "react";
import {
ArrowDown,
Check,
BookOpen,
Target,
BarChart3,
Calculator,
} from "lucide-react";
import DataModifierWidget from "../../../components/lessons/DataModifierWidget";
import BoxPlotComparisonWidget from "../../../components/lessons/BoxPlotComparisonWidget";
import ConfidenceIntervalWidget from "../../../components/lessons/ConfidenceIntervalWidget";
import StudyDesignWidget from "../../../components/lessons/StudyDesignWidget";
import Quiz from "../../../components/lessons/Quiz";
import {
CENTER_SPREAD_QUIZ_DATA,
DISTRIBUTIONS_QUIZ_DATA,
INFERENCES_QUIZ_DATA,
} from "../../../utils/constants";
interface LessonProps {
onFinish?: () => void;
}
const DataAnalysisLesson: 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-amber-100"
: "hover:bg-slate-100"
}`}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${
isActive
? "bg-amber-600 text-white"
: isPast
? "bg-amber-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-amber-900" : "text-slate-600"}`}
>
{title}
</p>
</div>
</button>
);
};
const DATA_ANALYSIS_QUIZ_DATA = [
...CENTER_SPREAD_QUIZ_DATA,
...DISTRIBUTIONS_QUIZ_DATA,
...INFERENCES_QUIZ_DATA,
];
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 lg:bg-transparent z-0 hidden lg:block">
<nav className="space-y-2">
<SectionMarker index={0} title="Data Changes" icon={Calculator} />
<SectionMarker index={1} title="Distributions" icon={BarChart3} />
<SectionMarker index={2} title="Inferences" icon={Target} />
<SectionMarker index={3} title="Study Design" icon={BookOpen} />
<SectionMarker index={4} 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: Data Changes */}
<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">
Effects of Data Changes
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-8">
<p>
The SAT often asks how the Mean, Median, and Standard Deviation
change without doing the full math.
</p>
<ul className="list-disc pl-5 space-y-2">
<li>
<strong>Add Constant:</strong> Center shifts, Spread stays same.
</li>
<li>
<strong>Multiply Constant:</strong> Center and Spread both
scale.
</li>
<li>
<strong>Outliers:</strong> Pull the Mean strongly, but the
Median is resistant.
</li>
</ul>
</div>
<DataModifierWidget />
<button
onClick={() => scrollToSection(1)}
className="mt-12 group flex items-center text-amber-600 font-bold hover:text-amber-800 transition-colors"
>
Next: Comparing Distributions{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 2: Distributions */}
<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">
Comparing Distributions
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-8">
<p>
<strong>Box Plots</strong> are the fastest way to compare Median
and Spread (IQR & Range). Remember: The box contains the middle
50% of the data.
</p>
</div>
<BoxPlotComparisonWidget />
<button
onClick={() => scrollToSection(2)}
className="mt-12 group flex items-center text-amber-600 font-bold hover:text-amber-800 transition-colors"
>
Next: Data Inferences{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 3: Inferences */}
<section
ref={(el) => {
sectionsRef.current[2] = el;
}}
className="min-h-screen flex flex-col justify-center mb-24"
>
<h2 className="text-4xl font-extrabold text-slate-900 mb-6">
Data Inferences & Margin of Error
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-8">
<p>
A <strong>Confidence Interval</strong> (Estimate ± Margin of
Error) gives a range where the true population value likely lies.
<br />
To compare two groups, check for <strong>overlap</strong>.
</p>
</div>
<ConfidenceIntervalWidget />
<button
onClick={() => scrollToSection(3)}
className="mt-12 group flex items-center text-amber-600 font-bold hover:text-amber-800 transition-colors"
>
Next: Study Design{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 4: Study Design */}
<section
ref={(el) => {
sectionsRef.current[3] = el;
}}
className="min-h-screen flex flex-col justify-center mb-24"
>
<h2 className="text-4xl font-extrabold text-slate-900 mb-6">
Study Design: Conclusion Logic
</h2>
<div className="prose prose-slate text-lg text-slate-600 mb-8">
<p>
Two magic phrases determine what you can conclude:
<br />
1. <strong>Random Sampling</strong> allows Generalization.
<br />
2. <strong>Random Assignment</strong> allows Causation.
</p>
</div>
<StudyDesignWidget />
<button
onClick={() => scrollToSection(4)}
className="mt-12 group flex items-center text-amber-600 font-bold hover:text-amber-800 transition-colors"
>
Next: Practice Quiz{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 5: Quiz */}
<section
ref={(el) => {
sectionsRef.current[4] = el;
}}
className="min-h-screen flex flex-col justify-center"
>
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{DATA_ANALYSIS_QUIZ_DATA.map((quiz, idx) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>
))}
<div className="p-8 bg-amber-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-amber-900 font-bold rounded-full hover:bg-amber-50 transition-colors"
>
Finish Lesson
</button>
</div>
</section>
</div>
</div>
);
};
export default DataAnalysisLesson;