317 lines
14 KiB
TypeScript
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 & 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;
|