/* Admin → Gradebook: teacher grade entry + behavior/mood feedback.
Feeds the parent app's Grades and Safety & Wellbeing screens. */
function GradebookScreen() {
const MOODS = {
happy: { label:'Happy', icon:'smile', bg:'var(--success-100)', fg:'var(--success-600)' },
calm: { label:'Calm', icon:'meh', bg:'var(--info-100)', fg:'var(--info-500)' },
tired: { label:'Tired', icon:'cloud', bg:'var(--warning-100)', fg:'var(--warning-600)' },
upset: { label:'Upset', icon:'frown', bg:'var(--danger-100)', fg:'var(--danger-600)' },
};
const [rows, setRows] = React.useState([
{ n:'EJ Hatten', id:'KMS-1042', c:'green', pct:91, mood:'happy', fb:null },
{ n:'James Walker', id:'KMS-0911', c:'navy', pct:84, mood:'calm', fb:null },
{ n:'Sophia Perez', id:'KMS-1188', c:'gold', pct:96, mood:'happy', fb:null },
{ n:'Liam Carter', id:'KMS-0733', c:'blue', pct:78, mood:'tired', fb:null },
{ n:'Ava Mitchell', id:'KMS-1276', c:'red', pct:88, mood:'calm', fb:null },
{ n:'Noah Bennett', id:'KMS-1305', c:'cyan', pct:72, mood:'upset', fb:null },
]);
const letter = (p) => p>=93?'A':p>=90?'A-':p>=87?'B+':p>=83?'B':p>=80?'B-':p>=77?'C+':p>=73?'C':p>=70?'C-':'D';
const gColor = (p) => p>=90?'var(--success-500)':p>=80?'var(--blue-500)':p>=70?'var(--warning-600)':'var(--danger-500)';
const setPct = (i,v) => setRows(rs => rs.map((r,j)=> j===i ? { ...r, pct: Math.max(0, Math.min(100, parseInt(v||0))) } : r));
const cycleMood = (i) => {
const order = ['happy','calm','tired','upset'];
setRows(rs => rs.map((r,j)=> j===i ? { ...r, mood: order[(order.indexOf(r.mood)+1)%order.length] } : r));
};
const feedback = (i, kind) => {
setRows(rs => rs.map((r,j)=> j===i ? { ...r, fb:kind } : r));
const r = rows[i];
jlToast((kind==='pos'?'Praise':'Note') + ' sent to ' + r.n + '\u2019s parent', { icon: kind==='pos'?'thumbs-up':'flag', color: kind==='pos'?'#1F9D57':'#F0B540' });
};
const classAvg = Math.round(rows.reduce((s,r)=>s+r.pct,0)/rows.length);
const honor = rows.filter(r=>r.pct>=90).length;
const atRisk = rows.filter(r=>r.pct<75).length;
const moodCounts = ['happy','calm','tired','upset'].map(m => ({ m, n: rows.filter(r=>r.mood===m).length }));
return (
{/* toolbar */}
{/* gradebook table */}
{rows.length} students} pad={false}>
StudentGrade %LetterMoodQuick Feedback
{rows.map((r,i) => {
const mood = MOODS[r.mood];
return (
w[0]).join('')} color={r.c} size={32} />
{r.n}
{r.id}
setPct(i, e.target.value)} type="number" style={{ width:54, border:'1px solid var(--gray-200)', borderRadius:8, padding:'7px 8px', fontSize:13.5, fontFamily:'var(--font-mono)', fontWeight:700, color:'var(--ink)', outline:'none', textAlign:'center' }} />
{letter(r.pct)}
);
})}
{/* right rail */}
{[
{ n:classAvg+'%', l:'Class avg', c:gColor(classAvg) },
{ n:honor, l:'Honor roll', c:'var(--success-500)' },
{ n:atRisk, l:'At risk', c:'var(--danger-500)' },
].map((s,i)=>(
))}
{moodCounts.map(({m,n}) => {
const mood = MOODS[m];
const pct = Math.round((n/rows.length)*100);
return (
);
})}
Noah Bennett has logged “upset” 3 days running — consider a counselor check-in.
);
}
const gbSel = { border:'1px solid var(--gray-200)', borderRadius:10, padding:'10px 12px', fontSize:13.5, fontFamily:'var(--font-sans)', color:'var(--ink)', background:'#fff', outline:'none', fontWeight:600 };
const gbBtnGhost = { display:'flex', alignItems:'center', gap:7, background:'#fff', color:'var(--gray-700)', border:'1px solid var(--gray-200)', borderRadius:10, padding:'10px 14px', fontWeight:700, fontSize:13.5, cursor:'pointer', fontFamily:'var(--font-sans)' };
const gbBtnGold = { display:'flex', alignItems:'center', gap:7, background:'var(--gold-400)', color:'var(--on-gold)', border:0, borderRadius:10, padding:'10px 16px', fontWeight:700, fontSize:13.5, cursor:'pointer', fontFamily:'var(--font-sans)', boxShadow:'var(--shadow-gold)' };
const gbIcon = { width:34, height:34, borderRadius:9, border:'1px solid var(--gray-200)', display:'flex', alignItems:'center', justifyContent:'center', cursor:'pointer', background:'#fff' };
Object.assign(window, { GradebookScreen });