Files
LMS/web/src/main/resources/templates/admin/lab.html

203 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru" xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/base :: layout(~{::title}, ~{::#content})}">
<head><title th:text="${lab.title} + ' — LMS'">Лабораторная</title></head>
<body>
<div id="content">
<a th:href="@{/admin/labs}" class="nav-link">← Все лабораторные</a>
<div class="page-head mb-2">
<div>
<h1 class="page-title" th:text="|ЛР ${lab.number}. ${lab.title}|">ЛР 1</h1>
<div class="meta mb-2">
<span class="badge"
th:classappend="${lab.status == 'published'} ? 'badge-ok' : 'badge-warn'"
th:text="${lab.status == 'published'} ? 'Опубликована' : (${lab.status == 'archived'} ? 'В архиве' : 'Черновик')">Черновик</span>
<form class="inline-form" th:action="@{/admin/labs/{id}/toggle-status(id=${lab.id})}"
method="post"
data-confirm="Сменить статус лабораторной? Опубликованную увидят студенты, черновик — только вы.">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="btn btn-sm" type="submit"
th:text="${lab.status == 'published'} ? 'Скрыть' : 'Опубликовать'">Опубликовать
</button>
</form>
</div>
<span th:if="${deadlineMsk != null}">Срок: <span th:text="${deadlineMsk} + ' МСК'"></span></span>
<span th:if="${defenseMsk != null}"> · Защита: <span th:text="${defenseMsk} + ' МСК'"></span></span>
· Максимум: <span th:text="${lab.pointsMax}"></span> б.
<span th:if="${totalVariants > 0}"> · Вариантов занято: <span th:text="${occupiedVariants}"></span> из <span
th:text="${totalVariants}"></span></span>
<span th:if="${submissionCount > 0}"> · Сдач: <span th:text="${submissionCount}"></span></span>
</div>
</div>
<div class="flex items-center gap-2 mb-2">
<h2 class="section-title m-0">Условие лабораторной</h2>
<button class="btn btn-secondary btn-sm p-2" type="button" data-action="lab-edit"
title="Редактировать параметры лабораторной" aria-label="Редактировать параметры лабораторной">
<i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i>
</button>
</div>
<div class="card card-body md-body mb-6" id="lab-description-view" th:utext="${descriptionHtml}"></div>
<form id="lab-edit-form" hidden class="card card-body form-stack mb-6" method="post"
th:action="@{/admin/labs/{id}/edit(id=${lab.id})}">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<div class="row g-3">
<div class="col-md-6"><label class="label" for="l-title">Название</label>
<input class="input" id="l-title" name="title" th:value="${lab.title}" required></div>
<div class="col-md-6"><label class="label" for="l-points">Максимум баллов</label>
<input class="input" id="l-points" name="pointsMax" type="number" step="0.1" min="0"
th:value="${lab.pointsMax}" required></div>
</div>
<div class="row g-3">
<div class="col-md-6"><label class="label" for="l-deadline">Срок сдачи (время — МСК)</label>
<input class="input" id="l-deadline" name="deadlineAt" type="datetime-local"
th:value="${deadlineLocal}"></div>
<div class="col-md-6"><label class="label" for="l-defense">Дата защиты (время — МСК)</label>
<input class="input" id="l-defense" name="defenseAt" type="datetime-local" th:value="${defenseLocal}">
</div>
</div>
<div><label class="label" for="l-status">Статус</label>
<select class="input" id="l-status" name="status">
<option value="draft" th:selected="${lab.status == 'draft'}">Черновик</option>
<option value="published" th:selected="${lab.status == 'published'}">Опубликована</option>
<option value="archived" th:selected="${lab.status == 'archived'}">В архиве</option>
</select></div>
<div><label class="label" for="l-desc">Условие (markdown)</label>
<textarea class="input" id="l-desc" name="descriptionMd" rows="14"
th:text="${lab.descriptionMd}"></textarea></div>
<div class="flex justify-end gap-2">
<button class="btn btn-secondary" type="button" data-action="lab-edit-cancel">Отмена</button>
<button class="btn btn-primary" type="submit">Сохранить</button>
</div>
</form>
<h2 class="section-title">Уровни и варианты</h2>
<div th:if="${#lists.isEmpty(lab.levels)}" class="card card-body text-muted">
Уровней пока нет — добавьте их при создании лабораторной или импорте.
</div>
<div th:each="level : ${lab.levels}" class="card mb-4">
<div class="card-body">
<div class="flex flex-wrap items-center justify-between gap-2 mb-4">
<h3 class="section-title m-0" th:text="${level.title}">Уровень</h3>
<div class="flex items-center gap-2">
<button class="btn btn-secondary btn-sm p-2" type="button" th:attr="data-modal=|modal-level-rename-${level.id}|"
title="Переименовать уровень" aria-label="Переименовать уровень">
<i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i>
</button>
<form class="inline-form"
th:action="@{/admin/labs/{labId}/levels/{levelId}/delete(labId=${lab.id}, levelId=${level.id})}"
method="post" data-confirm="Удалить уровень? Варианты на уровне должны быть пустыми.">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="btn btn-danger btn-sm p-2" type="submit" title="Удалить уровень"
aria-label="Удалить уровень">
<i class="cell-ico ico bi bi-trash" aria-hidden="true"></i>
</button>
</form>
<button class="btn btn-sm" type="button" th:attr="data-modal=|modal-variant-add-${level.id}|">+
Добавить
</button>
</div>
</div>
<div th:each="v : ${level.variants}" class="variant-block mb-4" th:id="'variant-' + ${v.id}">
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="variant-name">Вариант <b th:text="${v.number}">—</b><span th:if="${v.title}"
class="text-muted"
th:text="' · ' + ${v.title}"></span>
</div>
<div class="flex gap-2">
<button class="btn btn-secondary btn-sm p-2" type="button" th:attr="data-modal=|modal-variant-edit-${v.id}|"
title="Редактировать вариант" aria-label="Редактировать вариант">
<i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i>
</button>
<form class="inline-form"
th:action="@{/admin/labs/{labId}/variants/{variantId}/delete(labId=${lab.id}, variantId=${v.id})}"
method="post" data-confirm="Удалить вариант вместе с его условием?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="btn btn-danger btn-sm p-2" type="submit" title="Удалить вариант"
aria-label="Удалить вариант">
<i class="cell-ico ico bi bi-trash" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
<div class="md-body mt-2" th:utext="${variantHtml[v.id]}"></div>
</div>
<p th:if="${#lists.isEmpty(level.variants)}" class="text-sm text-muted m-0">Вариантов пока нет.</p>
</div>
</div>
<div th:each="level : ${lab.levels}" th:remove="tag">
<dialog th:id="|modal-level-rename-${level.id}|" class="dlg">
<div class="dlg-header">
<h2 class="dlg-title">Переименовать уровень</h2>
<button class="dlg-close" type="button" aria-label="Закрыть">✕</button>
</div>
<div class="dlg-body">
<form class="form-stack" th:action="@{/admin/labs/{labId}/levels/save(labId=${lab.id})}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="levelId" th:value="${level.id}">
<div><label class="label" for="|lvl-title-${level.id}|">Название</label>
<input class="input" th:id="|lvl-title-${level.id}|" name="title" th:value="${level.title}"
required></div>
<button class="btn btn-primary" type="submit">Сохранить</button>
</form>
</div>
</dialog>
<dialog th:id="|modal-variant-add-${level.id}|" class="dlg">
<div class="dlg-header">
<h2 class="dlg-title" th:text="|Новый вариант — ${level.title}|">Новый вариант</h2>
<button class="dlg-close" type="button" aria-label="Закрыть">✕</button>
</div>
<div class="dlg-body">
<form class="form-stack" th:action="@{/admin/labs/{labId}/variants/save(labId=${lab.id})}"
method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="labLevelId" th:value="${level.id}">
<div class="row g-3">
<div class="col-md-6"><label class="label" for="|va-number-${level.id}|">Номер</label>
<input class="input" th:id="|va-number-${level.id}|" name="number" required></div>
<div class="col-md-6"><label class="label" for="|va-title-${level.id}|">Название</label>
<input class="input" th:id="|va-title-${level.id}|" name="title"></div>
</div>
<div><label class="label" for="|va-statement-${level.id}|">Условие (markdown)</label>
<textarea class="input" th:id="|va-statement-${level.id}|" name="statementMd"
rows="8"></textarea></div>
<button class="btn btn-primary" type="submit">Создать</button>
</form>
</div>
</dialog>
<th:block th:each="v : ${level.variants}">
<dialog th:id="|modal-variant-edit-${v.id}|" class="dlg">
<div class="dlg-header">
<h2 class="dlg-title" th:text="|Вариант ${v.number}|">Вариант</h2>
<button class="dlg-close" type="button" aria-label="Закрыть">✕</button>
</div>
<div class="dlg-body">
<form class="form-stack" th:action="@{/admin/labs/{labId}/variants/save(labId=${lab.id})}"
method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="variantId" th:value="${v.id}">
<input type="hidden" name="labLevelId" th:value="${level.id}">
<div class="row g-3">
<div class="col-md-6"><label class="label" for="|ve-number-${v.id}|">Номер</label>
<input class="input" th:id="|ve-number-${v.id}|" name="number" th:value="${v.number}"
required></div>
<div class="col-md-6"><label class="label" for="|ve-title-${v.id}|">Название</label>
<input class="input" th:id="|ve-title-${v.id}|" name="title" th:value="${v.title}">
</div>
</div>
<div><label class="label" for="|ve-statement-${v.id}|">Условие (markdown)</label>
<textarea class="input" th:id="|ve-statement-${v.id}|" name="statementMd" rows="10"
th:text="${v.statementMd}"></textarea></div>
<button class="btn btn-primary" type="submit">Сохранить</button>
</form>
</div>
</dialog>
</th:block>
</div>
<script th:src="@{/js/admin-lab.js(v='28')}" defer></script>
</div>
</body>
</html>