Add instructions field to quiz and matching item types

Instructions are optional free text shown to the student above the questions/word bank.
Admin enters them in the item editor; stored in items.body for quiz and matching types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Nate EmmertandClaude Sonnet 4.6 committed 2026-08-01 16:57:52 -04:00
1 parent f9cc515cec
commit 60bfe3ca4a
5 files changed
+35 -4

No files matched your search

+8 -2
View File
@@ -226,7 +226,10 @@
</div>
<div id="ie-field-quiz" class="ie-field">
<div class="inline-form" style="margin-bottom:.5rem">
<label>Instructions <small style="color:#888">(optional — shown to student before questions)</small>
<textarea id="ie-quiz-instructions" rows="2" placeholder="e.g. Choose the best answer for each question." style="width:100%;margin-top:.25rem;resize:vertical"></textarea>
</label>
<div class="inline-form" style="margin:.5rem 0">
<select id="ie-template-select"><option value="">— load from template —</option></select>
<button type="button" id="ie-load-template" class="secondary">Load</button>
<button type="button" id="ie-save-template" class="secondary">Save as template</button>
@@ -236,7 +239,10 @@
</div>
<div id="ie-field-matching" class="ie-field">
<p class="hint" style="margin-bottom:.5rem">Enter word-clue pairs. Words are shown as A, B, C… (alphabetical order) in the word bank.</p>
<label>Instructions <small style="color:#888">(optional — shown to student before the word bank)</small>
<textarea id="ie-matching-instructions" rows="2" placeholder="e.g. Match each definition to the correct term." style="width:100%;margin-top:.25rem;resize:vertical"></textarea>
</label>
<p class="hint" style="margin:.5rem 0">Enter word-clue pairs. Words are shown as A, B, C… (alphabetical order) in the word bank.</p>
<div id="ie-pairs"></div>
<button type="button" id="ie-add-pair" class="secondary">+ Add pair</button>
</div>
+12 -2
View File
@@ -406,8 +406,12 @@ async function openItemEditor(unitId, itemId) {
$('#ie-retake-policy').value = item.retake_policy || 'latest';
if (item.type === 'spelling_practice' || item.type === 'spelling_test') $('#ie-list').value = item.ref_id;
if (item.type === 'flashcards') $('#ie-deck').value = item.ref_id;
if (item.type === 'quiz') (item.questions || []).forEach(addQuestionRow);
if (item.type === 'quiz') {
$('#ie-quiz-instructions').value = item.body || '';
(item.questions || []).forEach(addQuestionRow);
}
if (item.type === 'matching') {
$('#ie-matching-instructions').value = item.body || '';
$('#ie-pairs').innerHTML = '';
pairCount = 0;
(item.questions || []).forEach(addMatchingRow);
@@ -425,6 +429,8 @@ async function openItemEditor(unitId, itemId) {
$('#ie-title').value = '';
$('#ie-body-lesson').innerHTML = '';
$('#ie-body-assignment').innerHTML = '';
$('#ie-quiz-instructions').value = '';
$('#ie-matching-instructions').value = '';
$('#ie-points').value = 10;
$('#ie-due-date').value = '';
$('#ie-allow-retakes').checked = false;
@@ -713,7 +719,11 @@ $('#item-form').addEventListener('submit', async (e) => {
unitId: Number($('#ie-unit-id').value),
type,
title,
body: type === 'lesson' ? getLessonBody() : type === 'assignment' ? getAssignmentBody() : '',
body: type === 'lesson' ? getLessonBody()
: type === 'assignment' ? getAssignmentBody()
: type === 'quiz' ? $('#ie-quiz-instructions').value.trim()
: type === 'matching' ? $('#ie-matching-instructions').value.trim()
: '',
points: type === 'assignment' ? Number($('#ie-points').value) || 0 : 0,
refId: type === 'spelling_practice' || type === 'spelling_test' ? Number($('#ie-list').value)
: type === 'flashcards' ? Number($('#ie-deck').value) : null,
+6
View File
@@ -771,6 +771,9 @@ async function openQuiz(itemId, forceRetake = false) {
const item = await api(`/api/items/${itemId}?studentId=${currentStudent.id}`);
$('#quiz-kicker').textContent = `${item.course_name} · ${item.unit_name}`;
$('#quiz-title').textContent = item.title;
const instrEl = $('#quiz-instructions');
if (item.body) { instrEl.textContent = item.body; instrEl.hidden = false; }
else instrEl.hidden = true;
const graded = !forceRetake && item.submission && item.submission.status === 'graded';
const result = $('#quiz-result');
@@ -865,6 +868,9 @@ async function openMatching(itemId, forceRetake = false) {
const item = await api(`/api/items/${itemId}?studentId=${currentStudent.id}`);
$('#quiz-kicker').textContent = `${item.course_name} · ${item.unit_name}`;
$('#quiz-title').textContent = item.title;
const instrEl = $('#quiz-instructions');
if (item.body) { instrEl.textContent = item.body; instrEl.hidden = false; }
else instrEl.hidden = true;
const graded = !forceRetake && item.submission && item.submission.status === 'graded';
const result = $('#quiz-result');
+1
View File
@@ -135,6 +135,7 @@
<div class="item-card">
<p class="item-kicker" id="quiz-kicker"></p>
<h1 id="quiz-title"></h1>
<p id="quiz-instructions" class="quiz-instructions" hidden></p>
<form id="quiz-form"></form>
<div id="quiz-result" class="status-banner" hidden></div>
</div>
+8
View File
@@ -830,6 +830,14 @@ body.theme-indigo { --accent: #5b6abf; --accent-dark: #4a57a0; --bg: #f4f5ff; }
font-size: 1.05rem;
}
/* ---------- quiz / matching instructions ---------- */
.quiz-instructions {
background: var(--bg); border-left: 3px solid var(--accent);
border-radius: 6px; padding: 0.55rem 0.9rem;
font-size: 0.95rem; color: var(--ink);
margin-bottom: 1rem; text-align: left;
}
/* ---------- matching quiz ---------- */
.match-word-bank {
background: var(--bg); border: 2px solid var(--accent);