Quiz UX: remove popups, hide skipped questions warning

- safeNavigate: removed window.confirm() entirely — navigating between
  question numbers never loses answers (they're in React state), so the
  popup was wrong and annoying
- handleSubmit: replaced window.confirm() with a 3s toast pill at the
  bottom of the screen (dark, slides up, fades away automatically)
- ModeSelectScreen: removed skipped-questions warning block — internal
  extraction detail, not useful to show users
- Added fadeInUp CSS keyframe for the toast animation

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel 2026-04-01 02:44:00 +02:00
parent 0bc336a7d0
commit b92d2b9232
2 changed files with 27 additions and 23 deletions

View file

@ -265,6 +265,11 @@ body {
.spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid var(--border); border-top-color: var(--primary); border-radius: 50%; animation: spin 0.6s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes fadeInUp {
from { opacity: 0; transform: translateX(-50%) translateY(8px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
/* ── Section items ──────────────────────────────────────────── */
.section-item { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 8px; background: var(--card-bg); }

View file

@ -85,18 +85,6 @@ function ModeSelectScreen({ quiz, voices, onStart }) {
{quiz.time_limit_minutes ? ` · ${quiz.time_limit_minutes} min limit` : ''}
</p>
{skipped.length > 0 && (
<div style={{ background: '#fffbeb', border: '1px solid #fcd34d', borderRadius: 8, padding: 12, marginBottom: 20, textAlign: 'left' }}>
<p style={{ margin: '0 0 6px', fontWeight: 600, fontSize: '0.85rem', color: '#92400e' }}>
{skipped.length} question{skipped.length > 1 ? 's' : ''} skipped (no correct answer found):
</p>
<ul style={{ margin: 0, paddingLeft: 16 }}>
{skipped.map((q, i) => (
<li key={i} style={{ fontSize: '0.8rem', color: '#78350f', marginBottom: 2 }}>{q}</li>
))}
</ul>
</div>
)}
<p style={{ fontWeight: 600, marginBottom: 16, color: '#374151' }}>Choose how to take this quiz:</p>
<div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginBottom: 20 }}>
@ -147,9 +135,17 @@ export default function QuizPage() {
const [attemptId, setAttemptId] = useState(null)
const [timeLeft, setTimeLeft] = useState(null)
const [totalTime, setTotalTime] = useState(null)
const [toast, setToast] = useState('')
const timerRef = useRef(null)
const toastRef = useRef(null)
const hasStarted = useRef(false)
const showToast = (msg) => {
setToast(msg)
clearTimeout(toastRef.current)
toastRef.current = setTimeout(() => setToast(''), 3000)
}
useEffect(() => {
const load = async () => {
try {
@ -198,21 +194,13 @@ useEffect(() => {
const setAnswer = (questionId, value) => setAnswers(prev => ({ ...prev, [questionId]: value }))
// Warn before navigating away mid-quiz
const safeNavigate = (targetIdx) => {
if (Object.keys(answers).length > 0 && attemptId) {
const confirmed = window.confirm(
'You have answered some questions.\n\n• OK — go back (progress saved so far)\n• Cancel — stay on current question'
)
if (!confirmed) return
}
setCurrentIdx(targetIdx)
}
const safeNavigate = (targetIdx) => setCurrentIdx(targetIdx)
const handleSubmit = useCallback(async (autoSubmit = false) => {
if (!attemptId || submitting) return
if (!autoSubmit && Object.keys(answers).length === 0) {
if (!window.confirm('You haven\'t answered any questions. Submit anyway?')) return
showToast('No answers selected — submitting with 0 answered.')
await new Promise(r => setTimeout(r, 1200))
}
clearInterval(timerRef.current)
setSubmitting(true)
@ -251,6 +239,17 @@ useEffect(() => {
return (
<div className="quiz-bottom">
{toast && (
<div style={{
position: 'fixed', bottom: 24, left: '50%', transform: 'translateX(-50%)',
background: 'rgba(15,23,42,0.92)', color: '#f1f5f9',
padding: '10px 20px', borderRadius: 24, fontSize: '0.85rem',
zIndex: 500, whiteSpace: 'nowrap', pointerEvents: 'none',
animation: 'fadeInUp 0.2s ease',
}}>
{toast}
</div>
)}
<div className="card" style={{ marginBottom: 16 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
<div>