// Student · Quizzes & Assignments section (window.AssessmentsPanel) // Renders nothing unless published assessments exist. Expects React globals + Supabase `client` + `session`. (function(){ const { useState, useEffect } = React; const cap = s => s? s.charAt(0).toUpperCase()+s.slice(1) : s; function QuizModal({a, attempt, session, client, onClose, onGraded}){ const qs = Array.isArray(a.questions)? a.questions : []; const [ans,setAns]=useState(()=> qs.map(()=>-1)); const [busy,setBusy]=useState(false); const [err,setErr]=useState(''); const [result,setResult]=useState(attempt&&attempt.score!=null? {score:attempt.score, passed:attempt.passed, pass_mark:a.pass_mark} : null); async function submit(){ if(ans.some(x=>x<0)){ setErr('Please answer every question.'); return; } setErr(''); setBusy(true); try{ const { data, error }=await client.rpc('grade_quiz',{ a_id:a.id, ans }); if(error) throw error; setResult(data); onGraded(a.id, data); if(window.cgLog) window.cgLog(client, data.passed?'quiz_passed':'quiz_attempt', a.title, {score:data.score}); } catch(ex){ setErr(ex.message||'Could not submit'); } finally{ setBusy(false); } } return
e.stopPropagation()} style={{maxWidth:640}}>
Quiz

{a.title}

{a.description &&

{a.description}

} {result ?
{result.score}%
{result.passed?'✓ Passed':'Not passed yet'}
Pass mark {result.pass_mark||a.pass_mark}%
:
{qs.map((q,i)=>(
{i+1}.{q.text}
{(q.options||[]).map((o,j)=>())}
))} {err &&
{err}
}
}
; } function AssessmentsPanel({client, session}){ const [items,setItems]=useState([]); const [attempts,setAttempts]=useState({}); const [openQuiz,setOpenQuiz]=useState(null); async function load(){ const { data } = await client.from('assessment_public').select('*').order('sort_order'); setItems(data||[]); const { data:at } = await client.from('assessment_attempts').select('*').eq('student_id',session.user.id); const m={}; (at||[]).forEach(r=> m[r.assessment_id]=r); setAttempts(m); } useEffect(()=>{ load(); },[]); function onGraded(id, res){ setAttempts(m=>({...m,[id]:{...(m[id]||{}),assessment_id:id,student_id:session.user.id,score:res.score,passed:res.passed,completed:res.passed}})); } async function markDone(a){ const cur=attempts[a.id]; const next=!(cur&&cur.completed); setAttempts(m=>({...m,[a.id]:{...(m[a.id]||{}),assessment_id:a.id,student_id:session.user.id,completed:next}})); await client.from('assessment_attempts').upsert({assessment_id:a.id, student_id:session.user.id, completed:next, updated_at:new Date().toISOString()}, {onConflict:'assessment_id,student_id'}); } if(!items.length) return null; return

Quizzes & Assignments

Test what you've learned. Quizzes are graded instantly.

{items.map(a=>{ const at=attempts[a.id]; const quiz=a.kind==='quiz'; const done = at && at.completed; const score = at && at.score!=null ? at.score : null; return
{a.kind}
{a.title}
{a.description &&
{a.description}
}
{quiz ? (score!=null ? {at.passed?'✓ Passed':'Scored'} {score}% · pass {a.pass_mark}% : (a.questions||[]).length+' question(s) · pass '+a.pass_mark+'%') : (done ? ✓ Marked done : 'Assignment')}
{quiz ? : {a.file_url && ↓ {a.file_name||'File'}} {a.link_url && Open link ↗} }
; })}
{openQuiz && setOpenQuiz(null)} onGraded={onGraded} />}
; } window.AssessmentsPanel = AssessmentsPanel; })();