114 lines
7.7 KiB
HTML
114 lines
7.7 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru" xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/base :: layout(~{::title}, ~{::#content})}">
|
||
<head><title th:text="|ЛР ${lab.number} — LMS|">Лабораторная</title></head>
|
||
<body>
|
||
<div id="content">
|
||
<a th:href="@{/student/labs}" class="nav-link">← Лабораторные</a>
|
||
<div class="flex flex-wrap items-center justify-between gap-4 mt-4 mb-2">
|
||
<h1 class="page-title mb-1" th:text="|Лабораторная работа ${lab.number}. ${lab.title}|">ЛР</h1>
|
||
</div>
|
||
<p class="text-muted mb-6">
|
||
<span th:if="${deadlineMsk}"><b>Срок сдачи кода:</b> <span th:text="${deadlineMsk}"></span> МСК</span>
|
||
<span th:if="${deadlineMsk != null and defenseMsk != null}" class="nav-sep">·</span>
|
||
<span th:if="${defenseMsk}"><b>Защита:</b> <span th:text="${deadlineMsk}"></span> МСК</span>
|
||
</p>
|
||
|
||
<h2 class="section-title">Условие лабораторной</h2>
|
||
<div class="card card-body md-body mb-6" th:utext="${descriptionHtml}"></div>
|
||
|
||
<th:block th:if="${assignment != null}">
|
||
<h2 class="section-title">Ваш вариант <span class="font-mono" th:text="${assignment.variantNumber}"></span>
|
||
<span class="text-muted text-sm" th:if="${assignment.levelTitle}" th:text="|(${assignment.levelTitle})|"></span></h2>
|
||
<div class="card card-body md-body mb-6" th:utext="${statementHtml}"></div>
|
||
</th:block>
|
||
<div th:if="${assignment == null}" class="card card-body mb-6">
|
||
<p class="text-muted">Ваш вариант ещё не распределён. Дождитесь, пока преподаватель распределит варианты.</p>
|
||
</div>
|
||
|
||
<h2 class="section-title">Сдача работы</h2>
|
||
<div class="card form-card form-stack mb-4">
|
||
<p class="text-sm text-muted" th:if="${!canSubmit}">Приём решений закрыт. Вы по-прежнему можете скачать отправленные работы ниже.</p>
|
||
<form th:if="${canSubmit}" th:action="@{/student/labs/{id}/submit(id=${lab.id})}" method="post" enctype="multipart/form-data" class="form-stack">
|
||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||
<p class="text-sm text-muted">zip‑архив или исходники (.c/.h). Можно пересдавать сколько угодно раз до истечения срока — засчитывается последняя попытка.</p>
|
||
<div><input class="input" name="files" type="file" multiple required></div>
|
||
<button class="btn btn-primary" type="submit">Отправить решение</button>
|
||
</form>
|
||
<div th:if="${canSubmit and #lists.isEmpty(submissions)}" class="text-muted text-sm">Вы ещё ничего не сдавали.</div>
|
||
</div>
|
||
|
||
<h3 class="group-title">История сдач</h3>
|
||
<div class="card card-flush mb-6">
|
||
<table>
|
||
<thead><tr><th>Попытка</th><th>Время (МСК)</th><th>Файлы</th><th></th></tr></thead>
|
||
<tbody>
|
||
<tr th:each="s : ${submissions}">
|
||
<td th:text="${s.seq}">1</td>
|
||
<td class="text-muted" th:text="${s.createdAt}"></td>
|
||
<td class="font-mono text-sm" th:text="${#strings.listJoin(s.files, ', ')}">main.c</td>
|
||
<td><a class="btn btn-secondary" th:href="'/student/submissions/' + ${s.id} + '/download'">Скачать</a></td>
|
||
</tr>
|
||
<tr th:if="${#lists.isEmpty(submissions)}"><td colspan="4" class="empty">Нет сдач.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<th:block th:if="${canSeeGrade}">
|
||
<h2 class="section-title">Оценка и комментарии</h2>
|
||
<div class="card card-body mb-6">
|
||
<p class="mb-4 mt-1">Ваша оценка за лабораторную: <span class="score-big" th:text="${grade}"></span></p>
|
||
<div class="review-cols">
|
||
<div class="review-col" th:if="${!#lists.isEmpty(commentsPositive)}">
|
||
<h3 class="group-title tone-positive-text">Позитивный</h3>
|
||
<ul class="review-list"><li th:each="c : ${commentsPositive}" th:text="${c.text}">—</li></ul>
|
||
</div>
|
||
<div class="review-col" th:if="${!#lists.isEmpty(commentsNegative)}">
|
||
<h3 class="group-title tone-negative-text">Негативный</h3>
|
||
<ul class="review-list"><li th:each="c : ${commentsNegative}" th:text="${c.text}">—</li></ul>
|
||
</div>
|
||
<div class="review-col" th:if="${!#lists.isEmpty(commentsNeutral)}">
|
||
<h3 class="group-title">Нейтральный</h3>
|
||
<ul class="review-list"><li th:each="c : ${commentsNeutral}" th:text="${c.text}">—</li></ul>
|
||
</div>
|
||
</div>
|
||
<p class="text-sm text-muted"
|
||
th:if="${#lists.isEmpty(commentsPositive) and #lists.isEmpty(commentsNegative) and #lists.isEmpty(commentsNeutral)}">
|
||
Комментариев к оценке нет.
|
||
</p>
|
||
</div>
|
||
</th:block>
|
||
|
||
<h2 class="section-title">Защита лабораторной</h2>
|
||
<div class="card card-body form-stack">
|
||
<div th:if="${defense == null}" class="text-muted">
|
||
Индивидуальное задание для защиты ещё не назначено. Преподаватель сообщит, когда можно приходить.
|
||
</div>
|
||
<th:block th:if="${defense != null and defense.status == 'waiting'}">
|
||
<p class="text-sm">Преподаватель уже выдал вам одноразовый код. Подойдите на защиту и введите его:</p>
|
||
<form class="inline-form" th:action="@{/student/defenses/start}" method="post">
|
||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||
<input type="hidden" name="labId" th:value="${lab.id}">
|
||
<input class="input w-260" name="code" placeholder="6-значный код" maxlength="6" pattern="[0-9]{6}"
|
||
inputmode="numeric" autocomplete="one-time-code" required>
|
||
<button class="btn btn-primary" type="submit">Приступить</button>
|
||
</form>
|
||
</th:block>
|
||
<div th:if="${defense != null and defense.status == 'started'}">
|
||
<p class="text-sm defense-timer" th:attr="data-seconds=${defense.secondsLeft},data-lab=${lab.id}"></p>
|
||
<h3 class="group-title">Ваше индивидуальное задание</h3>
|
||
<div class="md-body" th:utext="${defense.assignmentHtml}"></div>
|
||
<form class="mt-4" th:action="'/student/defenses/' + ${defense.id} + '/finish?labId=' + ${lab.id}" method="post" data-confirm="Завершить защиту? Продолжить её будет нельзя — дождитесь преподавателя.">
|
||
<input type="hidden" name="_csrf" th:value="${csrfToken}">
|
||
<button class="btn btn-primary" type="submit">Готов к сдаче</button>
|
||
</form>
|
||
</div>
|
||
<div th:if="${defense != null and defense.status == 'ready'}">
|
||
<p><span class="badge" th:classappend="${defense.readyReason == 'timeout'} ? 'badge-warn' : 'badge-ok'" th:text="${defense.readyReason == 'timeout' ? 'Вышло время' : 'Готов к сдаче'}"></span></p>
|
||
<p class="text-sm text-muted">Ожидайте преподавателя с проверкой.</p>
|
||
</div>
|
||
</div>
|
||
<script th:src="@{/js/defense-timer.js(v='25')}" defer></script>
|
||
</div>
|
||
</body>
|
||
</html>
|