// 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}}>
{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)=>(
))}
{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;
})();