feat(web): ведомость преподавателя, материалы курса и аудит

This commit is contained in:
2026-10-27 20:39:28 +03:00
parent 4d97feaf9e
commit 97e14f040c
20 changed files with 1542 additions and 124 deletions

View File

@@ -15,7 +15,8 @@
<span th:text="|Групп: ${#lists.size(groups)}|">Групп: 0</span>
· <span th:text="|Лабораторных: ${#lists.size(labs)}|">Лабораторных: 0</span>
</div>
<div class="mt-2">
<div class="mt-2 flex flex-wrap gap-2">
<a class="btn btn-sm btn-primary" th:href="@{/teacher/courses/{id}/content(id=${course.id})}">Материалы курса</a>
<a class="btn btn-sm btn-secondary" th:href="@{/learn/course/{no}(no=${course.courseNo})}">Учебные материалы курса</a>
</div>
</div>
@@ -39,16 +40,26 @@
<a th:if="${currentUser.admin}"
th:href="@{/admin/groups/{id}(id=${group.groupId})}"
class="font-mono"
th:text="${group.groupId}">—</a>
th:text="${groupNames[group.groupId] ?: group.groupId}">—</a>
<a th:unless="${currentUser.admin}"
th:href="@{/teacher/courses/{courseId}/groups/{groupId}(courseId=${course.id}, groupId=${group.groupId})}"
class="font-mono"
th:text="${group.groupId}">—</a>
th:text="${groupNames[group.groupId] ?: group.groupId}">—</a>
</td>
<td class="text-muted" th:text="${group.addedAt}">—</td>
<td th:unless="${currentUser.admin}" class="text-center">
<a class="btn btn-sm btn-secondary"
th:href="@{/teacher/courses/{courseId}/groups/{groupId}(courseId=${course.id}, groupId=${group.groupId})}">Студенты</a>
<div class="flex flex-wrap items-center justify-center gap-2">
<a class="btn btn-sm btn-secondary"
th:href="@{/admin/groups/{groupId}(groupId=${group.groupId}, courseId=${course.id})}">Ведомость</a>
<a class="btn btn-sm btn-secondary"
th:href="@{/teacher/courses/{courseId}/groups/{groupId}(courseId=${course.id}, groupId=${group.groupId})}">Студенты</a>
<form class="inline-form"
th:action="@{/teacher/courses/{courseId}/groups/detach(courseId=${course.id})}"
method="post" data-confirm="Отвязать группу от курса? Ведомость и данные группы останутся в системе.">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="groupId" th:value="${group.groupId}">
<button class="btn btn-sm btn-danger" type="submit">Отвязать</button>
</form>
</div>
</td>
</tr>
<tr th:if="${#lists.isEmpty(groups)}">
@@ -57,7 +68,23 @@
</tbody>
</table>
</div>
<p class="hint mt-2">Состав групп и привязку к курсу изменяет администратор.</p>
<form class="card card-body form-stack mt-4" th:unless="${currentUser.admin}"
th:action="@{/teacher/courses/{id}/groups/attach(id=${course.id})}" method="post">
<div><label class="label" for="courseGroup">Привязать группу</label>
<select class="input" id="courseGroup" name="groupId" required
th:disabled="${#lists.isEmpty(availableGroups)}">
<option value="">— выберите группу —</option>
<option th:each="group : ${availableGroups}" th:value="${group.id}"
th:text="${group.name} + (${group.status == 'ACTIVE'} ? '' : ' (архив)')">—</option>
</select>
<p class="hint" th:if="${#lists.isEmpty(availableGroups)}">Все доступные группы уже привязаны.</p>
</div>
<button class="btn btn-primary" type="submit"
th:disabled="${#lists.isEmpty(availableGroups)}">Привязать</button>
<input type="hidden" name="_csrf" th:value="${csrfToken}">
</form>
<p class="hint mt-2" th:if="${currentUser.admin}">Состав групп и привязку к курсу изменяет администратор.</p>
</div>
<div class="col-lg-6">