refactor(exam): flat sidebar list, weight badges, UI cleanup

Signed-off-by: Abhinav Sinha <[email protected]>
This commit is contained in:
Abhinav Sinha
2026-06-18 01:11:19 +05:30
parent 7e75c8ae02
commit 09bd1b20da
9 changed files with 273 additions and 72 deletions
+49 -5
View File
@@ -2,11 +2,18 @@ import { useState, useEffect, useRef } from 'react'
import styles from './ExamStartModal.module.css'
export default function ExamStartModal({ bundle, onStart, onCancel }) {
const totalScenarios = bundle?.scenario_ids?.length || 0
const [minutes, setMinutes] = useState(bundle?.exam_minutes || 120)
const [scenarioCount, setScenarioCount] = useState(totalScenarios)
const inputRef = useRef(null)
// Keep scenarioCount in sync if bundle changes
useEffect(() => {
// Focus input on open
setScenarioCount(bundle?.scenario_ids?.length || 0)
}, [bundle])
useEffect(() => {
// Focus duration input on open
const t = setTimeout(() => inputRef.current?.select(), 60)
return () => clearTimeout(t)
}, [])
@@ -14,11 +21,14 @@ export default function ExamStartModal({ bundle, onStart, onCancel }) {
if (!bundle) return null
const numMinutes = Number(minutes)
const isValid = numMinutes >= 5 && numMinutes <= 300
const numScenarios = Number(scenarioCount)
const isMinutesValid = numMinutes >= 5 && numMinutes <= 300
const isScenariosValid = numScenarios >= 1 && numScenarios <= totalScenarios
const isValid = isMinutesValid && isScenariosValid
const handleStart = () => {
if (!isValid) return
onStart(numMinutes)
onStart(numMinutes, numScenarios)
}
const presets = [
@@ -42,9 +52,10 @@ export default function ExamStartModal({ bundle, onStart, onCancel }) {
<div className={styles.body}>
<div className={styles.info}>
<span>📋</span>
<span>{bundle.scenario_ids?.length || '?'} scenarios · Recommended: <strong>{bundle.exam_minutes} min</strong></span>
<span>{totalScenarios} scenarios · Recommended: <strong>{bundle.exam_minutes} min</strong></span>
</div>
{/* Duration field */}
<div className={styles.field}>
<label className={styles.label}>Exam Duration</label>
<div className={styles.presets}>
@@ -73,13 +84,46 @@ export default function ExamStartModal({ bundle, onStart, onCancel }) {
<span className={styles.unit}>minutes</span>
</div>
<div className={styles.hint}>
{!isValid ? (
{!isMinutesValid ? (
<span style={{ color: 'var(--red)' }}> Duration must be between 5 and 300 minutes</span>
) : numMinutes < 60 ? '⚡ Speed run mode' :
numMinutes <= 120 ? '🎯 Realistic exam timing' :
'🧘 Relaxed practice pace'}
</div>
</div>
{/* Scenario count field */}
<div className={styles.field}>
<label className={styles.label}>Number of Scenarios</label>
<div className={styles.scenarioRow}>
<input
type="range"
className={styles.slider}
value={numScenarios}
min={1}
max={totalScenarios}
step={1}
onChange={e => setScenarioCount(Number(e.target.value))}
style={{ '--bcolor': bundle.color }}
/>
<input
type="number"
className={styles.input}
value={scenarioCount}
min={1}
max={totalScenarios}
onChange={e => setScenarioCount(e.target.value)}
onKeyDown={e => e.key === 'Enter' && handleStart()}
/>
</div>
<div className={styles.hint}>
{!isScenariosValid ? (
<span style={{ color: 'var(--red)' }}> Must be between 1 and {totalScenarios}</span>
) : numScenarios === totalScenarios
? `📚 Full exam — all ${totalScenarios} scenarios`
: `🎯 ${numScenarios} randomly selected scenario${numScenarios > 1 ? 's' : ''}`}
</div>
</div>
</div>
<div className={styles.actions}>