From 43aa367f636205fe382584babe904ce7dfbd844d Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Thu, 20 Aug 2026 13:58:43 -0400 Subject: [PATCH] Add Graded sub-tab to student inbox New grades show in the New tab on first visit; on return they move to the Graded tab so the inbox stays clean without losing the history. Co-Authored-By: Claude Sonnet 4.6 --- public/app.js | 51 ++++++++++++++++++++++++++++++++--------------- public/index.html | 14 +++++++++++-- public/styles.css | 3 +++ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/public/app.js b/public/app.js index 369d3b6..a017844 100644 --- a/public/app.js +++ b/public/app.js @@ -332,6 +332,21 @@ function markAllSeen(studentId, ids) { localStorage.setItem(inboxSeenKey(studentId), JSON.stringify([...ids])); } +function renderGradeCard(r, isNew) { + const pct = r.points_possible ? Math.round((r.score / r.points_possible) * 100) : null; + const scoreClass = pct !== null ? (pct >= 80 ? 'score-good' : 'score-bad') : ''; + const date = new Date(r.graded_at + 'Z').toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); + return `
+
+ ${esc(r.itemTitle)} + ${isNew ? '' : ''} +
+
${esc(r.courseName)} ยท ${esc(r.unitName)} ยท ${date}
+ ${r.points_possible !== null ? `
๐ŸŒŸ ${r.score} / ${r.points_possible}${pct !== null ? ` (${pct}%)` : ''}
` : ''} + ${r.parent_comment ? `
๐Ÿ’ฌ ${esc(r.parent_comment)}
` : ''} +
`; +} + async function loadInbox() { const sid = currentStudent?.id; if (!sid) return; @@ -340,29 +355,33 @@ async function loadInbox() { api(`/api/students/${sid}/messages`), ]); const seen = getSeenIds(sid); + const newItems = items.filter((r) => !seen.has(r.submissionId)); + const gradedItems = items.filter((r) => seen.has(r.submissionId)); - $('#inbox-empty').hidden = items.length > 0; - $('#inbox-list').innerHTML = items.map((r) => { - const pct = r.points_possible ? Math.round((r.score / r.points_possible) * 100) : null; - const isNew = !seen.has(r.submissionId); - const scoreClass = pct !== null ? (pct >= 80 ? 'score-good' : 'score-bad') : ''; - const date = new Date(r.graded_at + 'Z').toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); - return `
-
- ${esc(r.itemTitle)} - ${isNew ? '' : ''} -
-
${esc(r.courseName)} ยท ${esc(r.unitName)} ยท ${date}
- ${r.points_possible !== null ? `
๐ŸŒŸ ${r.score} / ${r.points_possible}${pct !== null ? ` (${pct}%)` : ''}
` : ''} - ${r.parent_comment ? `
๐Ÿ’ฌ ${esc(r.parent_comment)}
` : ''} -
`; - }).join(''); + $('#inbox-list').innerHTML = newItems.map((r) => renderGradeCard(r, true)).join(''); + $('#inbox-empty').hidden = newItems.length > 0; + + $('#graded-list').innerHTML = gradedItems.map((r) => renderGradeCard(r, false)).join(''); + $('#graded-empty').hidden = gradedItems.length > 0; + + const newBadge = $('#inbox-new-badge'); + if (newBadge) { newBadge.hidden = newItems.length === 0; newBadge.textContent = newItems.length; } markAllSeen(sid, items.map((r) => r.submissionId)); updateInboxBadge(sid, 0); renderMsgThread(msgs); } +document.querySelectorAll('.inbox-subtab').forEach((btn) => { + btn.addEventListener('click', () => { + document.querySelectorAll('.inbox-subtab').forEach((b) => b.classList.remove('active')); + btn.classList.add('active'); + const sub = btn.dataset.sub; + $('#inbox-sub-new').hidden = sub !== 'new'; + $('#inbox-sub-graded').hidden = sub !== 'graded'; + }); +}); + function renderMsgThread(msgs) { const thread = $('#msg-thread'); if (!thread) return; diff --git a/public/index.html b/public/index.html index daa7354..e15b78c 100644 --- a/public/index.html +++ b/public/index.html @@ -102,8 +102,18 @@