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

317 lines
14 KiB
TypeScript

import React, { useRef, useState, useEffect } from "react";
import { ArrowDown, Check, BookOpen, AlertTriangle, Zap } from "lucide-react";
import EvidenceHunterWidget, {
type EvidenceExercise,
} from "../../../components/lessons/EvidenceHunterWidget";
import { PracticeFromDataset } from "../../../components/lessons/LessonShell";
import {
RHETORICAL_EASY,
RHETORICAL_MEDIUM,
} from "../../../data/rw/rhetorical-synthesis";
interface LessonProps {
onFinish?: () => void;
}
const EVIDENCE_EXERCISES: EvidenceExercise[] = [
{
question:
"Goal: Support the claim that urban trees improve air quality. Which note most directly accomplishes this goal?",
passage: [
"Urban trees provide shade, reducing heat absorption by buildings and roads.",
"A single mature tree absorbs up to 48 pounds of CO\u2082 per year and filters particulate matter from the air.",
"Tree-lined streets have been linked to higher property values in multiple U.S. cities.",
"Urban tree canopies reduce storm water runoff by intercepting rainfall before it reaches drains.",
"Studies show that access to green spaces reduces stress and improves mental health outcomes.",
],
evidenceIndex: 1,
explanation:
"Note 2 is the only one that directly addresses AIR QUALITY — specifically CO\u2082 absorption and filtering of particulate matter. Notes 1, 3, 4, and 5 address heat, property values, water, and mental health respectively. The goal specifies air quality, so only note 2 directly accomplishes it. This mirrors how SAT synthesis questions work: identify the goal, then find the note that MATCHES it.",
},
{
question:
"Goal: Emphasize the economic cost of food waste. Which sentence best accomplishes this goal using the notes below?",
passage: [
"Approximately one-third of all food produced globally is wasted each year.",
"Food waste occurs at every stage: production, processing, retail, and consumption.",
"In the United States alone, food waste costs the economy an estimated $218 billion annually.",
"Landfill decomposition of food waste produces methane, a potent greenhouse gas.",
"Reducing household food waste by 50% could save the average American family approximately $1,500 per year.",
],
evidenceIndex: 2,
explanation:
"Sentence 3 directly addresses the economic cost of food waste with a specific figure ($218 billion). The goal specifies ECONOMIC COST, not scale (sentence 1), process (sentence 2), environmental impact (sentence 4), or household savings (sentence 5). Sentence 5 mentions savings but focuses on consumers — sentence 3 is the broadest economic cost figure and best accomplishes the goal.",
},
{
question:
"Goal: Contrast how A. cerana and A. mellifera differ in learning speed. Which sentence best accomplishes this goal?",
passage: [
"Biologist Keiko Tanaka studied two bee species in a controlled laboratory setting.",
"The study measured how quickly each species associated a scent with a sugar reward.",
"A. cerana reached the learning criterion after an average of 8 trials.",
"A. mellifera required an average of 12 trials to reach the same criterion.",
"Tanaka hypothesized that differences in foraging environments may explain the variation.",
],
evidenceIndex: 2,
explanation:
'To contrast the two species\' learning speeds, the answer must include BOTH data points and explicitly compare them. Sentence 3 alone only gives A. cerana\'s data. The ideal answer would use sentences 3 and 4 together: "8 trials vs. 12 trials." But if choosing a single sentence that begins the contrast, sentence 3 establishes A. cerana\'s faster rate (8 < 12). On the SAT, the correct synthesis choice typically combines both relevant notes into one sentence — watch for answer choices that include "whereas," "while," or "compared to."',
},
];
const EBRWExpressionIdeasLesson: React.FC<LessonProps> = ({ onFinish }) => {
const [activeSection, setActiveSection] = useState(0);
const sectionsRef = useRef<(HTMLElement | null)[]>([]);
useEffect(() => {
const observers: IntersectionObserver[] = [];
sectionsRef.current.forEach((el, idx) => {
if (!el) return;
const obs = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setActiveSection(idx);
},
{ threshold: 0.3 },
);
obs.observe(el);
observers.push(obs);
});
return () => observers.forEach((o) => o.disconnect());
}, []);
const scrollToSection = (i: number) => {
setActiveSection(i);
sectionsRef.current[i]?.scrollIntoView({ behavior: "smooth" });
};
const SectionMarker = ({
index,
title,
icon: Icon,
}: {
index: number;
title: string;
icon: React.ElementType;
}) => {
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 text-left transition-all ${isActive ? "bg-fuchsia-50" : "hover:bg-slate-50"}`}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${isActive ? "bg-fuchsia-600 text-white" : isPast ? "bg-fuchsia-400 text-white" : "bg-slate-200 text-slate-500"}`}
>
{isPast ? (
<Check className="w-4 h-4" />
) : (
<Icon className="w-4 h-4" />
)}
</div>
<p
className={`text-sm font-bold ${isActive ? "text-fuchsia-900" : "text-slate-600"}`}
>
{title}
</p>
</button>
);
};
return (
<div className="flex flex-col lg:flex-row min-h-screen">
<aside className="w-full lg:w-64 lg:fixed lg:top-14 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 pt-6">
<SectionMarker index={0} title="Synthesis & Goals" icon={BookOpen} />
<SectionMarker
index={1}
title="Evidence Hunter"
icon={AlertTriangle}
/>
<SectionMarker index={2} title="Practice Quiz" icon={Zap} />
</nav>
</aside>
<div className="flex-1 lg:ml-64 p-6 md:p-12 max-w-4xl mx-auto">
{/* Section 0: Synthesis & Goals */}
<section
ref={(el) => {
sectionsRef.current[0] = el;
}}
className="min-h-screen flex flex-col justify-center mb-24 pt-20 lg:pt-0"
>
<div className="inline-flex items-center gap-2 bg-fuchsia-100 text-fuchsia-700 px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-4">
Expression of Ideas
</div>
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
Synthesis &amp; Goals
</h2>
<p className="text-lg text-slate-500 mb-8">
You are given student notes and a stated goal. Choose the sentence
that uses the most relevant notes to accomplish exactly that goal
nothing more, nothing less.
</p>
{/* Rule Grid */}
<div className="rounded-2xl p-6 mb-8 bg-fuchsia-50 border border-fuchsia-200 space-y-5">
<h3 className="text-lg font-bold text-fuchsia-900">
Four Rules for Synthesis Questions
</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{[
{
title: "Read the Goal First",
body: "Always read the student's goal BEFORE the notes. The goal tells you what the correct sentence must accomplish.",
},
{
title: "Match All Note Bullets",
body: "The correct answer uses only information from the provided notes — no outside knowledge, no unsupported claims.",
},
{
title: "Goal-Relevant Details Only",
body: "If the goal is 'to emphasize the benefit,' an answer mentioning cost is wrong even if the cost data exists in the notes.",
},
{
title: "Avoid Unsupported Additions",
body: "If an answer adds a causal claim ('because of X') not supported by the notes, it's wrong.",
},
].map((rule, i) => (
<div
key={i}
className="bg-white rounded-xl p-4 border border-fuchsia-100"
>
<p className="text-sm font-bold text-fuchsia-800 mb-1">
{rule.title}
</p>
<p className="text-xs text-slate-600 leading-relaxed">
{rule.body}
</p>
</div>
))}
</div>
</div>
{/* Static annotation visual */}
<div className="rounded-2xl p-6 mb-8 bg-fuchsia-50 border border-fuchsia-200 space-y-3">
<h3 className="text-lg font-bold text-fuchsia-900 mb-1">
Goal vs. Data: Worked Example
</h3>
<div className="bg-fuchsia-100 rounded-xl p-4 border border-fuchsia-200">
<p className="text-xs font-bold text-fuchsia-800 mb-1">
Notes bullet:
</p>
<p className="text-sm text-fuchsia-900">
"Average temperature drop: 1.5°C in areas with green roofs"
</p>
</div>
<div className="bg-green-100 rounded-xl p-4 border border-green-200">
<p className="text-xs font-bold text-green-800 mb-1">
Goal: Emphasize the cooling benefit
</p>
<p className="text-sm text-green-900">
Use: "Neighborhoods with cool roofs are up to 1.5°C cooler."
</p>
</div>
<div className="bg-orange-100 rounded-xl p-4 border border-orange-200">
<p className="text-xs font-bold text-orange-800 mb-1">
Wrong answer:
</p>
<p className="text-sm text-orange-900">
"Cool roofs are cheaper than conventional roofs" not supported
by the temperature note, and mentions cost (irrelevant to goal).
</p>
</div>
</div>
{/* Trap callout */}
<div className="bg-red-50 border border-red-200 rounded-xl p-5 mb-8">
<p className="text-sm font-bold text-red-800 mb-1">
Synthesis Trap
</p>
<p className="text-sm text-slate-700">
An answer that uses data from the notes but addresses the WRONG
goal. Always check that your answer accomplishes what the student
asked not just that it mentions the right topic.
</p>
</div>
{/* Golden Rule */}
<div className="bg-fuchsia-900 rounded-2xl p-6 mb-8">
<p className="text-xs font-bold text-fuchsia-300 uppercase tracking-wider mb-2">
Golden Rule
</p>
<p className="text-base font-bold text-white">
Goal Data match. The correct sentence must (1) accomplish the
stated goal, (2) use only note data, and (3) not add unsupported
claims.
</p>
</div>
<button
onClick={() => scrollToSection(1)}
className="mt-4 group flex items-center text-fuchsia-600 font-bold hover:text-fuchsia-800 transition-colors"
>
Next: Evidence Hunter{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* Section 1: Evidence Hunter */}
<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-2">
Evidence Hunter
</h2>
<p className="text-lg text-slate-500 mb-8">
Each passage is a set of student notes. Read the stated goal, then
select the sentence that best accomplishes it. Reveal the
explanation to check your reasoning.
</p>
<EvidenceHunterWidget
exercises={EVIDENCE_EXERCISES}
accentColor="fuchsia"
/>
<button
onClick={() => scrollToSection(2)}
className="mt-12 group flex items-center text-fuchsia-600 font-bold hover:text-fuchsia-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: Practice Quiz */}
<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">
Practice Quiz
</h2>
{RHETORICAL_EASY.slice(0, 2).map((q) => (
<PracticeFromDataset key={q.id} question={q} color="rose" />
))}
{RHETORICAL_MEDIUM.slice(0, 1).map((q) => (
<PracticeFromDataset key={q.id} question={q} color="rose" />
))}
<div className="mt-8 text-center">
<button
onClick={onFinish}
className="px-6 py-3 bg-fuchsia-900 text-white font-bold rounded-full hover:bg-fuchsia-700 transition-colors"
>
Finish Lesson
</button>
</div>
</section>
</div>
</div>
);
};
export default EBRWExpressionIdeasLesson;