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:
parent
0bc336a7d0
commit
b92d2b9232
2 changed files with 27 additions and 23 deletions
|
|
@ -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); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue