Skip to content

Commit ccabf61

Browse files
web-flowEdwin Chan
authored andcommitted
Add test mode
- /test/ page reads config from URL hash (#subject=...&topic=...&n=10&...) - TestSetup modal on subject + topic pages: pick size + course/level/paper, hit Start - TestRunner picks N random pestle questions matching filters, fetches full HTML, renders with collapsible markschemes - Mulberry32 PRNG so reloading without changing seed is reproducible (we always pass a fresh seed for now; can be exposed later for sharing tests) - Christos rows are filtered out of the pool — they're PDF links, not testable inline
1 parent 6e44bc4 commit ccabf61

6 files changed

Lines changed: 585 additions & 6 deletions

File tree

site/app/globals.css

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,110 @@ footer {
193193
font-size: 13px;
194194
}
195195

196-
.page-title { margin: 24px 0 8px; }
197-
.page-lede { color: var(--muted); margin: 0 0 12px; max-width: 720px; }
196+
.page-header {
197+
display: flex;
198+
align-items: center;
199+
justify-content: space-between;
200+
gap: 12px;
201+
flex-wrap: wrap;
202+
margin: 24px 0 8px;
203+
}
204+
.page-header h1 { margin: 0; }
205+
206+
.btn {
207+
display: inline-flex;
208+
align-items: center;
209+
justify-content: center;
210+
gap: 6px;
211+
padding: 8px 14px;
212+
border: 1px solid var(--border);
213+
border-radius: 6px;
214+
background: var(--card);
215+
color: var(--fg);
216+
font: inherit;
217+
font-size: 14px;
218+
cursor: pointer;
219+
transition: border-color 0.15s, background 0.15s;
220+
}
221+
.btn:hover { border-color: var(--accent); }
222+
.btn-primary { background: var(--accent); color: white; border-color: var(--accent); }
223+
.btn-primary:hover { filter: brightness(1.1); }
224+
.btn-block { width: 100%; padding: 12px; font-size: 15px; }
225+
226+
.test-setup-body {
227+
padding: 16px;
228+
display: flex;
229+
flex-direction: column;
230+
gap: 16px;
231+
}
232+
.test-setup-row { display: flex; flex-direction: column; gap: 6px; }
233+
.test-setup-row label {
234+
font-size: 13px;
235+
font-weight: 600;
236+
color: var(--fg);
237+
}
238+
.test-setup-row select {
239+
padding: 8px 10px;
240+
border: 1px solid var(--border);
241+
border-radius: 6px;
242+
background: var(--bg);
243+
color: var(--fg);
244+
font: inherit;
245+
}
246+
247+
.size-buttons { display: flex; gap: 8px; }
248+
.size-btn {
249+
flex: 1;
250+
padding: 8px;
251+
border: 1px solid var(--border);
252+
border-radius: 6px;
253+
background: var(--card);
254+
color: var(--fg);
255+
font: inherit;
256+
cursor: pointer;
257+
}
258+
.size-btn:hover { border-color: var(--accent); }
259+
.size-btn.active {
260+
background: var(--accent);
261+
color: white;
262+
border-color: var(--accent);
263+
}
264+
265+
.test-header {
266+
display: flex;
267+
align-items: flex-start;
268+
justify-content: space-between;
269+
gap: 12px;
270+
flex-wrap: wrap;
271+
margin: 24px 0 16px;
272+
}
273+
.test-actions { display: flex; gap: 8px; flex-wrap: wrap; }
274+
275+
.test-list {
276+
list-style: none;
277+
padding: 0;
278+
margin: 0;
279+
display: flex;
280+
flex-direction: column;
281+
gap: 24px;
282+
counter-reset: test-q;
283+
}
284+
.test-item {
285+
border: 1px solid var(--border);
286+
border-radius: 10px;
287+
padding: 20px;
288+
background: var(--card);
289+
}
290+
.test-item-header {
291+
display: flex;
292+
justify-content: space-between;
293+
align-items: baseline;
294+
gap: 12px;
295+
flex-wrap: wrap;
296+
margin-bottom: 12px;
297+
}
298+
.test-item-header h2 { margin: 0; font-size: 18px; }
299+
.test-item-header .meta { color: var(--muted); font-size: 13px; }
198300

199301
.topic-card {
200302
display: block;

site/app/subject/[key]/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Link from "next/link";
22
import { notFound } from "next/navigation";
33
import { listSubjectKeys, loadSubject } from "@/lib/data";
4+
import { TestSetup } from "@/components/TestSetup";
45

56
export async function generateStaticParams() {
67
const keys = await listSubjectKeys();
@@ -29,7 +30,10 @@ export default async function SubjectPage({
2930
<p style={{ margin: 0 }}>
3031
<Link href="/">← all subjects</Link>
3132
</p>
32-
<h1>{subj.label}</h1>
33+
<div className="page-header">
34+
<h1>{subj.label}</h1>
35+
<TestSetup subject={key} hasMaths={key === "maths"} />
36+
</div>
3337
<div className="summary-bar">
3438
<span>{subj.total.toLocaleString()} questions</span>
3539
{sourceLine && <span>{sourceLine}</span>}

site/app/subject/[key]/topic/[tid]/page.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Link from "next/link";
22
import { notFound } from "next/navigation";
33
import { listSubjectKeys, loadSubject, loadSubjectTopic } from "@/lib/data";
44
import { TopicBrowser } from "@/components/TopicBrowser";
5+
import { TestSetup } from "@/components/TestSetup";
56

67
export async function generateStaticParams() {
78
const keys = await listSubjectKeys();
@@ -37,9 +38,12 @@ export default async function TopicPage({
3738
<p style={{ margin: 0 }}>
3839
<Link href={`/subject/${key}/`}>{subj.label}</Link>
3940
</p>
40-
<h1>
41-
{topic.id}. {topic.name}
42-
</h1>
41+
<div className="page-header">
42+
<h1>
43+
{topic.id}. {topic.name}
44+
</h1>
45+
<TestSetup subject={key} topic={tid} hasMaths={key === "maths"} />
46+
</div>
4347
<p className="summary-bar">
4448
<span>{questions.length.toLocaleString()} questions</span>
4549
</p>

site/app/test/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { TestRunner } from "@/components/TestRunner";
2+
3+
export default function TestPage() {
4+
return <TestRunner />;
5+
}

0 commit comments

Comments
 (0)