349 lines
15 KiB
TypeScript
349 lines
15 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 {
|
||
CENTRAL_IDEAS_EASY,
|
||
CENTRAL_IDEAS_MEDIUM,
|
||
} from "../../../data/rw/central-ideas-details";
|
||
|
||
interface LessonProps {
|
||
onFinish?: () => void;
|
||
}
|
||
|
||
const EVIDENCE_EXERCISES: EvidenceExercise[] = [
|
||
{
|
||
question: "Which sentence states the central claim of this passage?",
|
||
passage: [
|
||
"Urban heat islands are a well-documented phenomenon in modern cities.",
|
||
"Researchers have found that cities are, on average, 1–3°C warmer than surrounding rural areas.",
|
||
"The primary driver of this warming is the replacement of natural surfaces with concrete, asphalt, and buildings, which absorb and retain heat.",
|
||
"However, urban tree canopies and green roofs have been shown to significantly reduce surface temperatures.",
|
||
"With targeted investment in urban greening programs, cities can meaningfully counteract the heat island effect and improve quality of life for residents.",
|
||
],
|
||
evidenceIndex: 4,
|
||
explanation:
|
||
"Sentence 5 is the central claim — it states the author's full position: that urban greening can counteract the heat island effect. Sentences 1–3 are context and causes; sentence 4 is a supporting detail. The main idea must capture the author's complete argument, not just the topic or a single detail.",
|
||
},
|
||
{
|
||
question: "What is the main idea of this passage about memory research?",
|
||
passage: [
|
||
"For decades, scientists believed that long-term memories were fixed once formed.",
|
||
"New research challenges this view, showing that memories are reconstructed each time they are recalled.",
|
||
"During recall, memories become temporarily unstable — a process called reconsolidation.",
|
||
"One study found that introducing false information during reconsolidation could alter participants' memories.",
|
||
"These findings suggest that human memory is not a static record but a dynamic, reconstructable process — with significant implications for eyewitness testimony in courts.",
|
||
],
|
||
evidenceIndex: 1,
|
||
explanation:
|
||
"Sentence 2 states the main idea: memory research has overturned the old view, showing memories are reconstructed rather than fixed. Sentence 5 expands the implication, but sentence 2 states the core claim. On the SAT, the main idea often appears early in the passage as the thesis — watch for sentences that challenge conventional wisdom.",
|
||
},
|
||
{
|
||
question: "Which sentence best captures what the entire passage argues?",
|
||
passage: [
|
||
"Microplastics have been detected in virtually every environment on Earth, from mountain peaks to deep ocean trenches.",
|
||
"Scientists discovered microplastics in Antarctic ice as early as 2014.",
|
||
"Recent studies have found microplastic particles in human blood, lung tissue, and breast milk.",
|
||
"While the health effects remain under investigation, preliminary evidence links microplastic exposure to inflammation and hormonal disruption.",
|
||
"Given their pervasive presence and potential health consequences, microplastics demand urgent regulatory attention and research investment.",
|
||
],
|
||
evidenceIndex: 4,
|
||
explanation:
|
||
"Sentence 5 is the main idea — it synthesizes the evidence (pervasive presence + health concerns) into a policy argument (urgent action needed). Sentences 1–4 all provide supporting evidence for this central claim. The main idea in argumentative passages typically appears as the author's call to action or judgment.",
|
||
},
|
||
];
|
||
|
||
const EBRWMainIdeaLesson: 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-teal-50" : "hover:bg-slate-50"}`}
|
||
>
|
||
<div
|
||
className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${isActive ? "bg-teal-600 text-white" : isPast ? "bg-teal-400 text-white" : "bg-slate-200 text-slate-500"}`}
|
||
>
|
||
{isPast ? (
|
||
<Check className="w-4 h-4" />
|
||
) : (
|
||
// @ts-ignore
|
||
<Icon className="w-4 h-4" />
|
||
)}
|
||
</div>
|
||
<p
|
||
className={`text-sm font-bold ${isActive ? "text-teal-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 z-0 hidden lg:block">
|
||
<nav className="space-y-2 pt-6">
|
||
<SectionMarker
|
||
index={0}
|
||
title="Concept & Annotation"
|
||
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: Concept & Annotation ── */}
|
||
<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-teal-100 text-teal-700 px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-4 self-start">
|
||
Information & Ideas
|
||
</div>
|
||
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
|
||
Main Idea
|
||
</h2>
|
||
<p className="text-lg text-slate-500 mb-10">
|
||
The main idea is what the author is arguing about the topic — not
|
||
just the topic itself. One sentence that covers everything.
|
||
</p>
|
||
|
||
{/* Rule Grid */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-10">
|
||
{/* Rule 1 */}
|
||
<div className="bg-teal-50 border border-teal-200 rounded-2xl p-5">
|
||
<p className="text-xs font-bold text-teal-600 uppercase tracking-wider mb-2">
|
||
Rule 1 — Topic vs. Main Idea
|
||
</p>
|
||
<p className="text-sm text-slate-700">
|
||
<span className="font-bold">Topic</span> = what the passage is
|
||
about (1–2 words). <span className="font-bold">Main Idea</span>{" "}
|
||
= what the author <span className="italic">says</span> about the
|
||
topic (full claim).
|
||
</p>
|
||
</div>
|
||
{/* Rule 2 */}
|
||
<div className="bg-teal-50 border border-teal-200 rounded-2xl p-5">
|
||
<p className="text-xs font-bold text-teal-600 uppercase tracking-wider mb-2">
|
||
Rule 2 — One-Sentence Test
|
||
</p>
|
||
<p className="text-sm text-slate-700">
|
||
The main idea fits in one sentence that covers the{" "}
|
||
<span className="font-bold">WHOLE</span> passage — not just one
|
||
paragraph.
|
||
</p>
|
||
</div>
|
||
{/* Rule 3 */}
|
||
<div className="bg-teal-50 border border-teal-200 rounded-2xl p-5">
|
||
<p className="text-xs font-bold text-teal-600 uppercase tracking-wider mb-2">
|
||
Rule 3 — Scope Check
|
||
</p>
|
||
<p className="text-sm text-slate-700">
|
||
<span className="font-bold">Too narrow</span> = describes only
|
||
one detail. <span className="font-bold">Too broad</span> =
|
||
overgeneralizes beyond what the text says.
|
||
</p>
|
||
</div>
|
||
{/* Rule 4 */}
|
||
<div className="bg-teal-50 border border-teal-200 rounded-2xl p-5">
|
||
<p className="text-xs font-bold text-teal-600 uppercase tracking-wider mb-2">
|
||
Rule 4 — Author's Stance
|
||
</p>
|
||
<p className="text-sm text-slate-700">
|
||
For persuasive texts, the main idea includes the author's{" "}
|
||
<span className="font-bold">position</span>, not just the topic.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Annotated Passage Visual */}
|
||
<div className="rounded-2xl border border-teal-200 overflow-hidden mb-8 shadow-sm">
|
||
<div className="bg-teal-600 px-5 py-3">
|
||
<p className="text-xs font-bold text-white uppercase tracking-wider">
|
||
Identifying Parts of a Passage
|
||
</p>
|
||
</div>
|
||
<div className="bg-white p-6 space-y-4">
|
||
{/* Box 1: Topic */}
|
||
<div className="bg-teal-100 border border-teal-200 rounded-xl p-4">
|
||
<p className="text-xs font-bold text-teal-700 uppercase tracking-wider mb-1">
|
||
Topic
|
||
</p>
|
||
<p className="text-sm text-slate-800 font-medium">
|
||
Climate change and coral reefs
|
||
</p>
|
||
<p className="text-xs text-teal-600 mt-1">
|
||
1–2 words — the subject, not the argument.
|
||
</p>
|
||
</div>
|
||
{/* Box 2: Main Idea */}
|
||
<div className="bg-green-100 border border-green-200 rounded-xl p-4">
|
||
<p className="text-xs font-bold text-green-700 uppercase tracking-wider mb-1">
|
||
Main Idea
|
||
</p>
|
||
<p className="text-sm text-slate-800 font-medium">
|
||
Coral reefs face existential threats from climate change, but
|
||
targeted conservation efforts can slow their decline.
|
||
</p>
|
||
<p className="text-xs text-green-700 mt-1">
|
||
The author's full claim — covers the entire passage.
|
||
</p>
|
||
</div>
|
||
{/* Box 3: Detail */}
|
||
<div className="bg-orange-100 border border-orange-200 rounded-xl p-4">
|
||
<p className="text-xs font-bold text-orange-700 uppercase tracking-wider mb-1">
|
||
Supporting Detail
|
||
</p>
|
||
<p className="text-sm text-slate-800">
|
||
"In the Great Barrier Reef, coral cover has declined by 50%
|
||
since 1985."
|
||
</p>
|
||
<p className="text-xs text-orange-700 mt-1">
|
||
← supporting evidence, NOT the main idea
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* SAT Trap Callout */}
|
||
<div className="bg-red-50 border border-red-200 rounded-2xl p-5 mb-6">
|
||
<p className="text-xs font-bold text-red-700 uppercase tracking-wider mb-2">
|
||
SAT Trap to Watch For
|
||
</p>
|
||
<p className="text-sm text-slate-700">
|
||
The SAT will offer a choice that is{" "}
|
||
<span className="font-bold">TRUE but too narrow</span> — it
|
||
describes only one paragraph's detail, not the whole passage's
|
||
claim. Always ask: does this cover the{" "}
|
||
<span className="font-bold">ENTIRE passage</span>?
|
||
</p>
|
||
</div>
|
||
|
||
{/* Golden Rule */}
|
||
<div className="bg-teal-900 rounded-2xl p-5 mb-10">
|
||
<p className="text-xs font-bold text-teal-300 uppercase tracking-wider mb-2">
|
||
Golden Rule
|
||
</p>
|
||
<p className="text-sm text-white leading-relaxed">
|
||
The main idea is the one sentence the author would give if you
|
||
asked:{" "}
|
||
<span className="italic">
|
||
"What's the point of this entire piece?"
|
||
</span>
|
||
</p>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => scrollToSection(1)}
|
||
className="group flex items-center text-teal-600 font-bold hover:text-teal-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">
|
||
Read each passage and tap the sentence that best states the central
|
||
claim. Think before you click.
|
||
</p>
|
||
|
||
<EvidenceHunterWidget
|
||
exercises={EVIDENCE_EXERCISES}
|
||
accentColor="teal"
|
||
/>
|
||
|
||
<button
|
||
onClick={() => scrollToSection(2)}
|
||
className="mt-12 group flex items-center text-teal-600 font-bold hover:text-teal-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>
|
||
{CENTRAL_IDEAS_EASY.slice(0, 2).map((q) => (
|
||
<PracticeFromDataset key={q.id} question={q} color="teal" />
|
||
))}
|
||
{CENTRAL_IDEAS_MEDIUM.slice(0, 1).map((q) => (
|
||
<PracticeFromDataset key={q.id} question={q} color="teal" />
|
||
))}
|
||
<div className="mt-8 text-center">
|
||
<button
|
||
onClick={onFinish}
|
||
className="px-6 py-3 bg-teal-900 text-white font-bold rounded-full hover:bg-teal-700 transition-colors"
|
||
>
|
||
Finish Lesson
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default EBRWMainIdeaLesson;
|