feat(lessons): add lessons from client db

This commit is contained in:
shafin-r
2026-03-01 20:24:14 +06:00
parent 2eaf77e13c
commit 2a00c44157
152 changed files with 74587 additions and 222 deletions

View File

@ -0,0 +1,516 @@
import React, { useRef, useState, useEffect } from "react";
import { ArrowDown, Check, BookOpen, AlertTriangle, Zap } from "lucide-react";
import DataClaimWidget, {
type DataExercise,
} from "../../../components/lessons/DataClaimWidget";
import { PracticeFromDataset } from "../../../components/lessons/LessonShell";
import {
COMMAND_EVIDENCE_EASY,
COMMAND_EVIDENCE_MEDIUM,
} from "../../../data/rw/command-of-evidence";
interface LessonProps {
onFinish?: () => void;
}
const DATA_EXERCISES: DataExercise[] = [
{
title: "Bar — School Activities",
chart: {
type: "bar",
title:
"Student Participation in Extracurricular Activities (% of students)",
yLabel: "% of students",
xLabel: "Grade Level",
unit: "%",
source: "School District Survey, 2023",
series: [
{
name: "Sports",
data: [
{ label: "Gr 9", value: 45 },
{ label: "Gr 10", value: 42 },
{ label: "Gr 11", value: 38 },
{ label: "Gr 12", value: 30 },
],
},
{
name: "Arts & Music",
data: [
{ label: "Gr 9", value: 28 },
{ label: "Gr 10", value: 30 },
{ label: "Gr 11", value: 32 },
{ label: "Gr 12", value: 35 },
],
},
{
name: "Academic Clubs",
data: [
{ label: "Gr 9", value: 18 },
{ label: "Gr 10", value: 22 },
{ label: "Gr 11", value: 26 },
{ label: "Gr 12", value: 31 },
],
},
],
},
claims: [
{
text: "Sports participation declines as students advance from Grade 9 to Grade 12.",
verdict: "supported",
explanation:
"The chart shows sports falling from 45% (Grade 9) to 30% (Grade 12) — a consistent decrease at every grade level. This is directly supported.",
},
{
text: "Students drop sports because they find academic clubs more interesting.",
verdict: "neither",
explanation:
"The chart shows participation trends but gives no information about WHY students make these choices. Reasons (interest, time, pressure) cannot be inferred from percentages alone.",
},
{
text: "Sports is the most popular activity among students in every grade level shown.",
verdict: "contradicted",
explanation:
"Sports leads in Grades 911, but in Grade 12 Arts & Music (35%) exceeds Sports (30%). Since Sports is NOT the top activity in Grade 12, this claim is directly contradicted by the data.",
},
],
},
{
title: "Line — Temperature",
chart: {
type: "line",
title: "Average Global Temperature Anomaly (°C above 19511980 baseline)",
yLabel: "Anomaly (°C)",
xLabel: "Year",
unit: "°C",
source: "NASA GISS Surface Temperature Analysis",
series: [
{
name: "Temperature Anomaly",
data: [
{ label: "1980", value: 0.26 },
{ label: "1990", value: 0.44 },
{ label: "2000", value: 0.42 },
{ label: "2005", value: 0.67 },
{ label: "2010", value: 0.72 },
{ label: "2015", value: 0.87 },
{ label: "2020", value: 1.02 },
],
},
],
},
claims: [
{
text: "The temperature anomaly in 2020 was higher than in any previous year shown in the graph.",
verdict: "supported",
explanation:
"+1.02°C in 2020 is the highest data point — every prior year is lower. This is directly supported by the graph.",
},
{
text: "The temperature anomaly decreased between 1990 and 2000.",
verdict: "supported",
explanation:
"The graph shows +0.44°C in 1990 and +0.42°C in 2000 — a slight downward dip. This is directly supported, though the change is small.",
},
{
text: "Human industrial activity is the primary cause of the temperature increases shown.",
verdict: "neither",
explanation:
"The graph records temperature trends but provides no data on causes. Attributing increases to human activity requires additional scientific evidence not present in this graph.",
},
],
},
{
title: "Bar — Media Use",
chart: {
type: "bar",
title: "Average Daily Media Consumption by Age Group (hours per day)",
yLabel: "Hours per day",
xLabel: "Media Type",
unit: " hr",
source: "National Media Survey, 2024",
series: [
{
name: "Ages 1317",
data: [
{ label: "Social Media", value: 4.8 },
{ label: "Streaming", value: 3.2 },
{ label: "Video Games", value: 2.5 },
{ label: "Reading", value: 0.7 },
],
},
{
name: "Ages 1824",
data: [
{ label: "Social Media", value: 3.6 },
{ label: "Streaming", value: 3.9 },
{ label: "Video Games", value: 1.8 },
{ label: "Reading", value: 1.1 },
],
},
],
},
claims: [
{
text: "Teenagers (ages 1317) spend more time on social media than on any other media type shown.",
verdict: "supported",
explanation:
"Social Media (4.8 hr) is the highest value for ages 1317, exceeding Streaming (3.2 hr), Video Games (2.5 hr), and Reading (0.7 hr). Directly supported.",
},
{
text: "Adults ages 1824 spend more time on streaming video than teenagers do.",
verdict: "supported",
explanation:
"Ages 1824: Streaming = 3.9 hr. Ages 1317: Streaming = 3.2 hr. 3.9 > 3.2, so this claim is directly supported.",
},
{
text: "Teenagers read less because social media is more entertaining to them.",
verdict: "neither",
explanation:
"The chart shows that teens spend little time reading (0.7 hr), but provides no data about why. Entertainment preferences require separate survey data not shown here.",
},
],
},
{
title: "Line — Renewable Energy",
chart: {
type: "line",
title: "Renewable Energy Share of U.S. Electricity Generation (%)",
yLabel: "% of generation",
xLabel: "Year",
unit: "%",
source: "U.S. Energy Information Administration",
series: [
{
name: "Solar",
data: [
{ label: "2010", value: 0.1 },
{ label: "2013", value: 0.4 },
{ label: "2016", value: 1.3 },
{ label: "2019", value: 2.7 },
{ label: "2022", value: 5.5 },
],
},
{
name: "Wind",
data: [
{ label: "2010", value: 2.3 },
{ label: "2013", value: 4.1 },
{ label: "2016", value: 5.6 },
{ label: "2019", value: 7.3 },
{ label: "2022", value: 10.2 },
],
},
],
},
claims: [
{
text: "Wind energy generated a larger share of electricity than solar energy in every year shown.",
verdict: "supported",
explanation:
"In all five years (20102022), Wind exceeds Solar: e.g., 2010 (2.3% vs. 0.1%) and 2022 (10.2% vs. 5.5%). This trend is directly visible throughout the graph.",
},
{
text: "Solar energy's share grew by more percentage points than wind energy's share between 2010 and 2022.",
verdict: "contradicted",
explanation:
"Solar: 0.1% → 5.5% = +5.4 points. Wind: 2.3% → 10.2% = +7.9 points. Wind grew by more absolute percentage points, so the claim that Solar grew more is directly contradicted.",
},
{
text: "Renewable energy will replace all fossil fuels within the next 20 years.",
verdict: "neither",
explanation:
"The graph shows growth trends for two renewables through 2022 but provides no data about total energy mix, future rates, or fossil fuel usage. Future predictions require additional data not shown here.",
},
],
},
];
const EBRWGraphicDisplaysLesson: 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-amber-50" : "hover:bg-slate-50"}`}
>
<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>
<p
className={`text-sm font-bold ${isActive ? "text-amber-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="Reading Graphics" icon={BookOpen} />
<SectionMarker
index={1}
title="Data Claim Lab"
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: Reading Graphics */}
<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-amber-100 text-amber-700 px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-4">
Graphic Displays
</div>
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
Reading Graphics
</h2>
<p className="text-lg text-slate-500 mb-8">
Every SAT graphic question tests the same skill: does the data
directly prove the claim? Master these four rules before anything
else.
</p>
{/* Rule Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
<div className="rounded-2xl p-6 bg-amber-50 border border-amber-200 space-y-2">
<div className="flex items-center gap-2 mb-1">
<span className="w-7 h-7 rounded-full bg-amber-600 text-white flex items-center justify-center text-xs font-bold shrink-0">
1
</span>
<p className="text-sm font-bold text-amber-900">
Read the Title First
</p>
</div>
<p className="text-sm text-slate-700 leading-relaxed">
The title tells you what variable is being measured, the time
frame, and the units. Read it before looking at data points.
</p>
</div>
<div className="rounded-2xl p-6 bg-amber-50 border border-amber-200 space-y-2">
<div className="flex items-center gap-2 mb-1">
<span className="w-7 h-7 rounded-full bg-amber-600 text-white flex items-center justify-center text-xs font-bold shrink-0">
2
</span>
<p className="text-sm font-bold text-amber-900">
Identify Axes and Units
</p>
</div>
<p className="text-sm text-slate-700 leading-relaxed">
X-axis (horizontal) = independent variable (often time or
category). Y-axis (vertical) = measured value. Always check
units (%, count, dollars, etc.).
</p>
</div>
<div className="rounded-2xl p-6 bg-amber-50 border border-amber-200 space-y-2">
<div className="flex items-center gap-2 mb-1">
<span className="w-7 h-7 rounded-full bg-amber-600 text-white flex items-center justify-center text-xs font-bold shrink-0">
3
</span>
<p className="text-sm font-bold text-amber-900">
Look for Trends, Not Just Values
</p>
</div>
<p className="text-sm text-slate-700 leading-relaxed">
SAT questions often ask about overall trends
(increasing/decreasing) or comparisons between categories, not
single data points.
</p>
</div>
<div className="rounded-2xl p-6 bg-amber-50 border border-amber-200 space-y-2">
<div className="flex items-center gap-2 mb-1">
<span className="w-7 h-7 rounded-full bg-amber-600 text-white flex items-center justify-center text-xs font-bold shrink-0">
4
</span>
<p className="text-sm font-bold text-amber-900">
The Claim Must Match the Data Exactly
</p>
</div>
<p className="text-sm text-slate-700 leading-relaxed">
A claim is 'supported' only if the data directly proves it.
'Contradicted' if the data proves the opposite. 'Neither' if the
data is irrelevant or insufficient.
</p>
</div>
</div>
{/* Static Annotation Visual */}
<div className="rounded-2xl p-6 mb-6 bg-amber-50 border border-amber-200 space-y-4">
<h3 className="text-base font-bold text-amber-900 mb-2">
How to Evaluate a Claim Against a Graph
</h3>
<div className="rounded-xl p-4 bg-amber-100 border border-amber-200">
<p className="text-xs font-bold text-amber-800 uppercase tracking-wider mb-1">
Graph Context
</p>
<p className="text-sm text-slate-800">
Graph title: "Annual Carbon Emissions by Sector (20002020)"
</p>
</div>
<div className="rounded-xl p-4 bg-green-100 border border-green-200">
<p className="text-xs font-bold text-green-800 uppercase tracking-wider mb-1">
Supported Claim
</p>
<p className="text-sm text-slate-800">
"Transportation emissions increased between 2000 and 2020" if
the line goes up, this is directly proven.
</p>
</div>
<div className="rounded-xl p-4 bg-orange-100 border border-orange-200">
<p className="text-xs font-bold text-orange-800 uppercase tracking-wider mb-1">
Neither-Proven Claim
</p>
<p className="text-sm text-slate-800">
"Electric vehicles caused the transportation increase" the
graph shows the trend, not the cause. Causation requires
additional data.
</p>
</div>
</div>
{/* Trap Callout */}
<div className="rounded-2xl p-5 mb-6 bg-red-50 border border-red-200">
<p className="text-xs font-bold text-red-700 uppercase tracking-wider mb-2">
Common Trap
</p>
<p className="text-sm text-slate-700 leading-relaxed">
The biggest graphic display trap: a claim that is{" "}
<span className="font-bold text-red-800">PLAUSIBLE</span> and
consistent with real-world knowledge but is{" "}
<span className="font-bold text-red-800">
NOT proven by this specific graph
</span>
. The graph must be sufficient to prove or disprove the claim on
its own.
</p>
</div>
{/* Golden Rule */}
<div className="rounded-2xl p-5 mb-8 bg-amber-900 text-white">
<p className="text-xs font-bold text-amber-300 uppercase tracking-wider mb-2">
Golden Rule
</p>
<p className="text-sm leading-relaxed">
Only use what's on the graphic. Outside knowledge, reasonable
assumptions, and probable causes are all off-limits. The data
either proves it, disproves it, or doesn't address it.
</p>
</div>
<button
onClick={() => scrollToSection(1)}
className="mt-4 group flex items-center text-amber-600 font-bold hover:text-amber-800 transition-colors"
>
Next: Data Claim Lab{" "}
<ArrowDown className="ml-2 w-5 h-5 group-hover:translate-y-1 transition-transform" />
</button>
</section>
{/* SECTION 1: Data Claim Lab */}
<section
ref={(el) => {
sectionsRef.current[1] = el;
}}
className="min-h-screen flex flex-col justify-center mb-24"
>
<div className="inline-flex items-center gap-2 bg-amber-100 text-amber-700 px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-4">
Interactive Practice
</div>
<h2 className="text-4xl font-extrabold text-slate-900 mb-2">
Data Claim Lab
</h2>
<p className="text-lg text-slate-500 mb-8">
Read each dataset, then judge whether each claim is supported,
contradicted, or neither proven by the data.
</p>
<DataClaimWidget exercises={DATA_EXERCISES} accentColor="amber" />
<button
onClick={() => scrollToSection(2)}
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 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>
{COMMAND_EVIDENCE_EASY.slice(2, 4).map((q) => (
<PracticeFromDataset key={q.id} question={q} color="teal" />
))}
{COMMAND_EVIDENCE_MEDIUM.slice(1, 2).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-amber-900 text-white font-bold rounded-full hover:bg-amber-700 transition-colors"
>
Finish Lesson
</button>
</div>
</section>
</div>
</div>
);
};
export default EBRWGraphicDisplaysLesson;