Publication per student: checkbox in lab column, partial state in eye, cursor on Variant
This commit is contained in:
@@ -348,8 +348,10 @@ code.code { padding: 0.125rem 0.5rem; border-radius: 0.25rem; font-family: monos
|
||||
.variant-btn {
|
||||
display: inline-block; padding: 0.18rem 0.5rem; border-radius: 0.4rem;
|
||||
border: 1px solid var(--c-border); background: var(--c-bg);
|
||||
font-size: 0.8rem; white-space: nowrap;
|
||||
font-size: 0.8rem; white-space: nowrap; cursor: pointer; color: var(--c-text);
|
||||
}
|
||||
.variant-btn:hover { border-color: var(--c-accent); color: var(--c-accent); }
|
||||
.pub-toggle { width: 1.05rem; height: 1.05rem; margin: 0; cursor: pointer; accent-color: #16a34a; }
|
||||
.variant-btn.manual { border-color: var(--c-border-strong, var(--c-border)); font-weight: 600; }
|
||||
.cell-btns { display: flex; gap: 0.2rem; }
|
||||
.cell-btn {
|
||||
@@ -375,6 +377,7 @@ code.code { padding: 0.125rem 0.5rem; border-radius: 0.25rem; font-family: monos
|
||||
.mini-btn:hover { background: var(--c-bg); }
|
||||
.mini-btn.ok { color: #16a34a; }
|
||||
.mini-btn.warn { color: #d97706; }
|
||||
.mini-btn.partial { color: #2563eb; }
|
||||
|
||||
/* Увеличенный диалог (оценка + комментарии) */
|
||||
dialog.modal.modal-lg { width: min(72rem, calc(100vw - 2rem)); }
|
||||
|
||||
@@ -253,6 +253,47 @@ function removeCommentTemplate(text, done) {
|
||||
.catch(function () { showToast('Не удалось удалить комментарий.', 'error'); });
|
||||
}
|
||||
|
||||
/* ---------- Публикация оценки и комментариев отдельному студенту ---------- */
|
||||
|
||||
function attachPublishToggles() {
|
||||
var urlEl = $id('pub-url');
|
||||
if (!urlEl) return;
|
||||
var csrfEl = document.querySelector('input[name="_csrf"]');
|
||||
document.querySelectorAll('.pub-toggle').forEach(function (cb) {
|
||||
cb.addEventListener('change', function () {
|
||||
var desired = cb.checked;
|
||||
var body = new URLSearchParams();
|
||||
body.set('_csrf', csrfEl ? csrfEl.value : '');
|
||||
body.set('labId', cb.getAttribute('data-lab') || '');
|
||||
body.set('studentId', cb.getAttribute('data-student') || '');
|
||||
body.set('published', desired ? 'true' : 'false');
|
||||
cb.disabled = true;
|
||||
fetch(urlEl.value, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: body.toString()
|
||||
})
|
||||
.then(function (r) { return r.ok ? r.json() : { ok: false }; })
|
||||
.then(function (res) {
|
||||
cb.disabled = false;
|
||||
if (res && res.ok) {
|
||||
showToast(desired
|
||||
? 'Оценка и комментарии опубликованы студенту.'
|
||||
: 'Оценка и комментарии скрыты от студента.');
|
||||
} else {
|
||||
cb.checked = !desired;
|
||||
showToast((res && res.error) || 'Не удалось изменить публикацию.', 'error');
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
cb.disabled = false;
|
||||
cb.checked = !desired;
|
||||
showToast('Не удалось изменить публикацию.', 'error');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- Индивидуальное задание (защита) ---------- */
|
||||
function defenseStatusText(status, code, seconds) {
|
||||
if (status === 'waiting') {
|
||||
@@ -418,4 +459,5 @@ document.addEventListener('submit', function (ev) {
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
focusLab();
|
||||
tickDefenseCells();
|
||||
attachPublishToggles();
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
</div>
|
||||
|
||||
<div th:if="${#lists.isEmpty(labs)}" class="card card-body mb-6 text-muted">Опубликованных лабораторных пока нет.</div>
|
||||
<input type="hidden" id="pub-url" th:value="@{/admin/groups/{id}/publish-student(id=${group.id})}">
|
||||
<div class="card card-flush" th:if="${!#lists.isEmpty(labs)}">
|
||||
<div class="scroll-x">
|
||||
<table class="matrix tablekit" id="group-table">
|
||||
@@ -39,16 +40,18 @@
|
||||
<div class="lab-col-head">
|
||||
<a th:href="@{/admin/labs/{id}(id=${lab.id})}" th:text="|ЛР ${lab.number}|">ЛР</a>
|
||||
<div class="lab-head-actions">
|
||||
<form class="inline-form" th:action="@{/admin/groups/{id}/publish(id=${group.id})}" method="post">
|
||||
<form class="inline-form" th:with="pub=${labPublication[lab.id]}"
|
||||
th:action="@{/admin/groups/{id}/publish(id=${group.id})}" method="post">
|
||||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||||
<input type="hidden" name="labId" th:value="${lab.id}">
|
||||
<input type="hidden" name="published"
|
||||
th:value="${publishedLabIds.contains(lab.id) ? 'false' : 'true'}">
|
||||
th:value="${pub != null and pub.allPublished} ? 'false' : 'true'">
|
||||
<button class="mini-btn" type="submit"
|
||||
th:classappend="${publishedLabIds.contains(lab.id)} ? 'ok' : 'warn'"
|
||||
th:title="${publishedLabIds.contains(lab.id) ? 'Оценки опубликованы — нажмите, чтобы скрыть' : 'Показать оценки и комментарии студентам'}">
|
||||
<svg th:if="${publishedLabIds.contains(lab.id)}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
<svg th:unless="${publishedLabIds.contains(lab.id)}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg>
|
||||
th:classappend="${pub != null and pub.allPublished} ? 'ok' : (${pub != null and pub.publishedCount > 0} ? 'partial' : 'warn')"
|
||||
th:title="${pub == null} ? '' : (${pub.allPublished} ? 'Оценки опубликованы всей группе — нажмите, чтобы скрыть у всех' : (${pub.publishedCount > 0} ? 'Опубликовано ' + ${pub.publishedCount} + ' из ' + ${pub.total} + ' — нажмите, чтобы опубликовать всем' : 'Показать оценки и комментарии всей группе'))">
|
||||
<svg th:if="${pub != null and pub.publishedCount > 0}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
<svg th:unless="${pub != null and pub.publishedCount > 0}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/><line x1="3" y1="3" x2="21" y2="21"/></svg>
|
||||
<span class="mini-count" th:if="${pub != null and !pub.allPublished and pub.publishedCount > 0}" th:text="${pub.publishedCount}"></span>
|
||||
</button>
|
||||
</form>
|
||||
<a class="mini-btn"
|
||||
@@ -70,11 +73,17 @@
|
||||
<td th:each="cell, iterStat : ${row.cells}" class="text-center cell-lab"
|
||||
th:attr="data-lab=${cell.labId},data-sort=${cell.variantNumber},data-filter-label=${cell.variantNumber != null ? 'вариант ' + cell.variantNumber : 'без варианта'}">
|
||||
<div class="cell-stack">
|
||||
<button class="variant-btn" type="button"
|
||||
th:classappend="${cell.variantManual} ? 'manual' : ''"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-variant=${cell.variantId}"
|
||||
onclick="openVariant(this)"
|
||||
title="Назначить вариант вручную или выдать случайный">Вариант <b th:text="${cell.variantNumber} ?: '—'">—</b></button>
|
||||
<div class="cell-top">
|
||||
<button class="variant-btn" type="button"
|
||||
th:classappend="${cell.variantManual} ? 'manual' : ''"
|
||||
th:attr="data-student=${row.studentId},data-student-name=${row.fullName},data-lab=${cell.labId},data-lab-name='ЛР ' + ${cell.labNumber},data-variant=${cell.variantId}"
|
||||
onclick="openVariant(this)"
|
||||
title="Назначить вариант вручную или выдать случайный">Вариант <b th:text="${cell.variantNumber} ?: '—'">—</b></button>
|
||||
<input type="checkbox" class="pub-toggle"
|
||||
th:checked="${cell.published}"
|
||||
th:attr="data-lab=${cell.labId},data-student=${row.studentId},data-name=${row.fullName}"
|
||||
th:title="${cell.published} ? 'Оценка и комментарии видны студенту — снять, чтобы скрыть' : 'Оценка и комментарии скрыты — поставить, чтобы опубликовать'">
|
||||
</div>
|
||||
<div class="cell-btns">
|
||||
<a th:if="${cell.submissionId}" class="cell-btn" title="Скачать последнее решение"
|
||||
th:href="@{/admin/submissions/{id}/download(id=${cell.submissionId}, group=${group.name}, no=${row.studentNo}, name=${row.fullName}, attempt=${cell.submissionSeq})}">↓<span class="cell-num" th:text="${cell.submissionSeq}"></span></a>
|
||||
|
||||
Reference in New Issue
Block a user