feat: exam9 — назначение варианта как ДЗ + импорт нечётных в банк
Импорт 40 нечётных вариантов (v01, v03, ..., v79) в банк вопросов: - 400 questions с allow_html=1, source_type='экзамен 9', year=2025 - 540 options (single-choice) + correct_text (short_answer) - 40 tests (по 1 на вариант), title="Экзамен 9 — Вариант N" - exam9_variant_tests маппинг для назначения Назначение варианта как ДЗ на /exam9 (для учителей/админов): - Кнопка «Назначить как ДЗ» под заголовком варианта (только если test_id есть) - Модалка выбора классов + опциональный deadline - POST /api/assignments/bulk с test_id из exam9_variant_tests Поддержка HTML/SVG в вопросах банка через флаг questions.allow_html: - Миграция 003: ALTER TABLE questions ADD COLUMN allow_html - sessionController: SELECT возвращают allow_html и image - test-run.html: рендер q.text и opt.text как HTML при allow_html=1 - test-result.html: то же для explanation и opt.text - KaTeX: добавлены $...$ и $$...$$ delimiters в обеих страницах Бонус-фикс: bulkSchema требовал class_id (single), контроллер ждёт class_ids (array). Существующий вызов из classes.html был сломан; исправлено вместе. Команда: node backend/scripts/import-exam9.js (--all для всех 80)
This commit is contained in:
@@ -181,6 +181,76 @@
|
||||
}
|
||||
.ex-empty svg { width:48px; height:48px; opacity:.5; margin-bottom:14px; stroke:var(--text-3); }
|
||||
|
||||
/* ── Assign button + modal ── */
|
||||
.ex-assign-row {
|
||||
display:flex; align-items:center; gap:12px; margin-bottom:22px; flex-wrap:wrap;
|
||||
}
|
||||
.ex-assign-btn {
|
||||
display:inline-flex; align-items:center; gap:7px;
|
||||
padding:8px 16px; border:1.5px solid var(--violet); border-radius:10px;
|
||||
background:rgba(155,93,229,.08); color:var(--violet);
|
||||
font-family:'Manrope',sans-serif; font-size:.85rem; font-weight:700;
|
||||
cursor:pointer; transition:all .15s;
|
||||
}
|
||||
.ex-assign-btn:hover { background:var(--violet); color:#fff; }
|
||||
.ex-assign-btn:disabled {
|
||||
opacity:.5; cursor:not-allowed; background:transparent; color:var(--text-3); border-color:var(--border);
|
||||
}
|
||||
.ex-assign-btn svg { width:14px; height:14px; }
|
||||
.ex-assign-note {
|
||||
font-size:.78rem; color:var(--text-3);
|
||||
}
|
||||
|
||||
.ax-form { display:flex; flex-direction:column; gap:14px; }
|
||||
.ax-field label {
|
||||
display:block; font-size:.78rem; font-weight:700; color:var(--text-2);
|
||||
text-transform:uppercase; letter-spacing:.05em; margin-bottom:6px;
|
||||
}
|
||||
.ax-classes {
|
||||
display:flex; flex-direction:column; gap:6px; max-height:240px; overflow-y:auto;
|
||||
border:1.5px solid var(--border); border-radius:10px; padding:8px;
|
||||
}
|
||||
.ax-class {
|
||||
display:flex; align-items:center; gap:10px; padding:8px 10px;
|
||||
border-radius:8px; cursor:pointer; transition:background .12s;
|
||||
font-size:.9rem;
|
||||
}
|
||||
.ax-class:hover { background:var(--border); }
|
||||
.ax-class input { accent-color:var(--violet); flex-shrink:0; }
|
||||
.ax-class .ax-cname { font-weight:600; }
|
||||
.ax-class .ax-cmeta { font-size:.78rem; color:var(--text-3); margin-left:auto; }
|
||||
.ax-input {
|
||||
width:100%; padding:9px 12px; border:1.5px solid var(--border-h);
|
||||
border-radius:9px; background:var(--surface); color:var(--text);
|
||||
font-family:'Manrope',sans-serif; font-size:.9rem;
|
||||
}
|
||||
.ax-input:focus { outline:none; border-color:var(--violet); }
|
||||
.ax-actions {
|
||||
display:flex; gap:10px; justify-content:flex-end; margin-top:6px;
|
||||
}
|
||||
.ax-btn {
|
||||
padding:9px 18px; border-radius:10px; border:1.5px solid var(--border-h);
|
||||
background:transparent; color:var(--text);
|
||||
font-family:'Manrope',sans-serif; font-size:.88rem; font-weight:700;
|
||||
cursor:pointer; transition:all .15s;
|
||||
}
|
||||
.ax-btn:hover { border-color:var(--text-2); }
|
||||
.ax-btn-primary { background:var(--violet); border-color:var(--violet); color:#fff; }
|
||||
.ax-btn-primary:hover { background:#7e3eca; border-color:#7e3eca; }
|
||||
.ax-btn-primary:disabled { opacity:.5; cursor:not-allowed; }
|
||||
.ax-error {
|
||||
padding:9px 12px; border-radius:8px; background:rgba(241,91,68,.1);
|
||||
border:1px solid rgba(241,91,68,.3); color:#F94144;
|
||||
font-size:.84rem; display:none;
|
||||
}
|
||||
.ax-error.visible { display:block; }
|
||||
.ax-success {
|
||||
padding:9px 12px; border-radius:8px; background:rgba(6,214,160,.1);
|
||||
border:1px solid rgba(6,214,160,.3); color:#06D6A0;
|
||||
font-size:.84rem; display:none;
|
||||
}
|
||||
.ax-success.visible { display:block; }
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.ex-wrap { padding:20px 16px 60px; }
|
||||
.ex-title { font-size:1.15rem; }
|
||||
@@ -241,6 +311,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ex-overlay" id="assign-overlay" onclick="onAssignOverlayClick(event)">
|
||||
<div class="ex-panel" onclick="event.stopPropagation()" style="width:min(520px,94vw)">
|
||||
<div class="ex-panel-head">
|
||||
<h2 id="assign-title">Назначить вариант</h2>
|
||||
<button class="ex-panel-close" onclick="closeAssignModal()" title="Закрыть">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<form class="ax-form" id="assign-form" onsubmit="event.preventDefault(); submitAssign()">
|
||||
<div class="ax-field">
|
||||
<label>Классы</label>
|
||||
<div class="ax-classes" id="ax-classes-list">Загрузка…</div>
|
||||
</div>
|
||||
<div class="ax-field">
|
||||
<label>Срок сдачи (опционально)</label>
|
||||
<input type="datetime-local" class="ax-input" id="ax-deadline" />
|
||||
</div>
|
||||
<div class="ax-error" id="ax-error"></div>
|
||||
<div class="ax-success" id="ax-success"></div>
|
||||
<div class="ax-actions">
|
||||
<button type="button" class="ax-btn" onclick="closeAssignModal()">Отмена</button>
|
||||
<button type="submit" class="ax-btn ax-btn-primary" id="ax-submit">Назначить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/lucide@0.469.0/dist/umd/lucide.min.js"></script>
|
||||
<script src="/js/api.js"></script>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user