Rework on Bootstrap 5: standard navbar+offcanvas (fixed empty menu), Bootstrap Icons, complete B3 theme via BS vars, row/col grids

This commit is contained in:
2026-10-03 00:05:52 +03:00
parent 03b64586b2
commit f65a60462e
39 changed files with 229 additions and 311 deletions

View File

@@ -5,18 +5,18 @@
<div id="content">
<h1 class="page-title">Панель преподавателя</h1>
<div class="grid grid-stats">
<div class="card card-body"><p class="stat-title">Группы</p><p class="stat-value" th:text="${groupCount}">0</p></div>
<div class="card card-body"><p class="stat-title">Студенты</p><p class="stat-value" th:text="${studentCount}">0</p></div>
<div class="card card-body"><p class="stat-title">Лабораторные</p><p class="stat-value" th:text="${labCount}">0</p>
<p class="hint" th:if="${labCount > 0}">опубликовано: <span th:text="${publishedLabs}">0</span></p></div>
<div class="card card-body"><p class="stat-title">Средний балл</p>
<div class="row row-cols-1 row-cols-sm-2 row-cols-xl-4 g-3">
<div class="col"><div class="card card-body h-100"><p class="stat-title">Группы</p><p class="stat-value" th:text="${groupCount}">0</p></div></div>
<div class="col"><div class="card card-body h-100"><p class="stat-title">Студенты</p><p class="stat-value" th:text="${studentCount}">0</p></div></div>
<div class="col"><div class="card card-body h-100"><p class="stat-title">Лабораторные</p><p class="stat-value" th:text="${labCount}">0</p>
<p class="hint" th:if="${labCount > 0}">опубликовано: <span th:text="${publishedLabs}">0</span></p></div></div>
<div class="col"><div class="card card-body h-100"><p class="stat-title">Средний балл</p>
<p class="stat-value" th:if="${avgGrade != null}" th:text="${#numbers.formatDecimal(avgGrade, 1, 2)}">—</p>
<p class="stat-value text-muted" th:if="${avgGrade == null}">—</p></div>
<p class="stat-value text-muted" th:if="${avgGrade == null}">—</p></div></div>
</div>
<div class="grid grid-2 mt-8">
<div>
<div class="row g-4 mt-4">
<div class="col-lg-6">
<div class="flex items-center justify-between mb-4">
<h2 class="section-title m-0">Лабораторные</h2>
<a th:href="@{/admin/labs}" class="text-sm">Все лабораторные →</a>
@@ -33,7 +33,7 @@
</table>
</div>
</div>
<div>
<div class="col-lg-6">
<div class="flex items-center justify-between mb-4">
<h2 class="section-title m-0">Группы</h2>
<a th:href="@{/admin/groups}" class="text-sm">Все группы →</a>

View File

@@ -49,7 +49,7 @@
<button class="mini-btn" type="button"
th:attr="data-url=@{/admin/groups/{id}/assign-modal(id=${group.id}, labId=${lab.id})},data-lab-name=|ЛР ${lab.number}|" data-action="assign"
title="Выдача вариантов: уровни, варианты, автокраспределение по группе">
<span class="cell-ico ico ico-menu" aria-hidden="true"></span>
<i class="cell-ico ico bi bi-list" aria-hidden="true"></i>
</button>
<form class="inline-form" th:with="pub=${labPublication[lab.id]}, pc=${pub != null ? pub.publishedCount : 0}, all=${pub != null and pub.allPublished}"
th:action="@{/admin/groups/{id}/publish(id=${group.id})}" method="post">
@@ -61,8 +61,8 @@
th:attr="data-lab=${lab.id},data-count=${pc},data-total=${pub != null ? pub.total : 0},data-all=${all}"
th:classappend="${all} ? 'is-all' : (${pc > 0} ? 'is-partial' : 'is-empty')"
th:title="${pub == null} ? '' : (${all} ? 'Оценки опубликованы всей группе — нажмите, чтобы скрыть у всех' : (${pc > 0} ? 'Опубликовано ' + ${pc} + ' из ' + ${pub.total} + ' — нажмите, чтобы опубликовать всем' : 'Показать оценки и комментарии всей группе'))">
<span class="eye-open ico ico-eye-open" aria-hidden="true"></span>
<span class="eye-closed ico ico-eye-closed" aria-hidden="true"></span>
<i class="eye-open ico bi bi-eye" aria-hidden="true"></i>
<i class="eye-closed ico bi bi-eye-slash" aria-hidden="true"></i>
<span class="mini-count js-pub-count" th:classappend="(${all} or ${pc == 0}) ? 'is-hidden' : ''" th:text="${pc}"></span>
</button>
</form>
@@ -278,20 +278,20 @@
</form>
<form class="form-stack mt-4" th:action="@{/admin/groups/{gid}/students/create(gid=${group.id})}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<div class="grid grid-2">
<div><label class="label" for="stNo">Студенческий номер (логин)</label>
<div class="row g-3">
<div class="col-md-6"><label class="label" for="stNo">Студенческий номер (логин)</label>
<input class="input" id="stNo" name="studentNo" required></div>
<div><label class="label" for="stBasis">Основа обучения</label>
<div class="col-md-6"><label class="label" for="stBasis">Основа обучения</label>
<input class="input" id="stBasis" name="basis" placeholder="бюджет"></div>
</div>
</div>
<div><label class="label" for="stLast">Фамилия</label>
<input class="input" id="stLast" name="lastName" required></div>
<div class="grid grid-2">
<div><label class="label" for="stFirst">Имя</label>
<div class="row g-3">
<div class="col-md-6"><label class="label" for="stFirst">Имя</label>
<input class="input" id="stFirst" name="firstName"></div>
<div><label class="label" for="stMiddle">Отчество</label>
<div class="col-md-6"><label class="label" for="stMiddle">Отчество</label>
<input class="input" id="stMiddle" name="middleName"></div>
</div>
</div>
<p class="hint">Новому студенту пароль будет сгенерирован автоматически.</p>
<button class="btn btn-primary" type="submit">Создать нового</button>
</form>

View File

@@ -33,25 +33,25 @@
<h2 class="section-title m-0">Условие лабораторной</h2>
<button class="mini-btn" type="button" data-action="lab-edit"
title="Редактировать параметры лабораторной" aria-label="Редактировать параметры лабораторной">
<span class="cell-ico ico ico-pencil" aria-hidden="true"></span>
<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="grid grid-2">
<div><label class="label" for="l-title">Название</label>
<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><label class="label" for="l-points">Максимум баллов</label>
<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="grid grid-2">
<div><label class="label" for="l-deadline">Срок сдачи (время — МСК)</label>
<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><label class="label" for="l-defense">Дата защиты (время — МСК)</label>
<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>
@@ -82,7 +82,7 @@
<div class="flex items-center gap-2">
<button class="mini-btn" type="button" th:attr="data-modal=|modal-level-rename-${level.id}|"
title="Переименовать уровень" aria-label="Переименовать уровень">
<span class="cell-ico ico ico-pencil" aria-hidden="true"></span>
<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})}"
@@ -90,7 +90,7 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить уровень"
aria-label="Удалить уровень">
<span class="cell-ico ico ico-trash" aria-hidden="true"></span>
<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}|">+
@@ -108,7 +108,7 @@
<div class="flex gap-2">
<button class="mini-btn" type="button" th:attr="data-modal=|modal-variant-edit-${v.id}|"
title="Редактировать вариант" aria-label="Редактировать вариант">
<span class="cell-ico ico ico-pencil" aria-hidden="true"></span>
<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})}"
@@ -116,7 +116,7 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить вариант"
aria-label="Удалить вариант">
<span class="cell-ico ico ico-trash" aria-hidden="true"></span>
<i class="cell-ico ico bi bi-trash" aria-hidden="true"></i>
</button>
</form>
</div>
@@ -154,12 +154,12 @@
method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="labLevelId" th:value="${level.id}">
<div class="grid grid-2">
<div><label class="label" for="|va-number-${level.id}|">Номер</label>
<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><label class="label" for="|va-title-${level.id}|">Название</label>
<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>
<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>
@@ -179,14 +179,14 @@
<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="grid grid-2">
<div><label class="label" for="|ve-number-${v.id}|">Номер</label>
<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><label class="label" for="|ve-title-${v.id}|">Название</label>
<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>
<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>

View File

@@ -79,20 +79,20 @@
</div>
<div class="dlg-body">
<form class="form-stack" th:action="@{/admin/labs/create}" method="post">
<div class="grid grid-2">
<div><label class="label" for="labNumber">Номер</label>
<div class="row g-3">
<div class="col-md-6"><label class="label" for="labNumber">Номер</label>
<input class="input" id="labNumber" name="number" type="number" min="1" required></div>
<div><label class="label" for="labPoints">Максимум баллов</label>
<div class="col-md-6"><label class="label" for="labPoints">Максимум баллов</label>
<input class="input" id="labPoints" name="pointsMax" type="number" step="0.1" min="0" value="5" required></div>
</div>
</div>
<div><label class="label" for="labTitle">Название</label>
<input class="input" id="labTitle" name="title" required></div>
<div class="grid grid-2">
<div><label class="label" for="labDeadline">Срок сдачи</label>
<div class="row g-3">
<div class="col-md-6"><label class="label" for="labDeadline">Срок сдачи</label>
<input class="input" id="labDeadline" name="deadlineAt" type="datetime-local"></div>
<div><label class="label" for="labDefense">Дата защиты</label>
<div class="col-md-6"><label class="label" for="labDefense">Дата защиты</label>
<input class="input" id="labDefense" name="defenseAt" type="datetime-local"></div>
</div>
</div>
<div><label class="label" for="labStatus">Статус</label>
<select class="input" id="labStatus" name="status">
<option value="draft">draft</option>

View File

@@ -16,84 +16,59 @@
<link rel="icon" type="image/svg+xml" th:href="@{/favicon.svg(v='2')}">
<!-- v=4: сброс кэша стилей после редизайна; при изменении css поднимать версию -->
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/css/app.css(v='17')}">
<link id="hljs-light" rel="stylesheet" th:href="@{/css/hljs-github.css(v='17')}">
<link id="hljs-dark" rel="stylesheet" th:href="@{/css/hljs-github-dark.css(v='17')}" disabled>
<script th:src="@{/js/highlight.min.js(v='17')}" defer></script>
<script th:src="@{/js/table-scroll.js(v='17')}" defer></script>
<script th:src="@{/js/tablekit.js(v='17')}" defer></script>
<script th:src="@{/js/theme-init.js(v='17')}"></script>
<script th:src="@{/js/app.js(v='17')}" defer></script>
<script th:src="@{/js/ui-state.js(v='17')}" defer></script>
<script th:src="@{/js/md-content.js(v='17')}" defer></script>
<script th:src="@{/js/submit-guard.js(v='17')}" defer></script>
<link rel="stylesheet" th:href="@{/css/bootstrap-icons.min.css}">
<link rel="stylesheet" th:href="@{/css/app.css(v='18')}">
<link id="hljs-light" rel="stylesheet" th:href="@{/css/hljs-github.css(v='18')}">
<link id="hljs-dark" rel="stylesheet" th:href="@{/css/hljs-github-dark.css(v='18')}" disabled>
<script th:src="@{/js/highlight.min.js(v='18')}" defer></script>
<script th:src="@{/js/table-scroll.js(v='18')}" defer></script>
<script th:src="@{/js/tablekit.js(v='18')}" defer></script>
<script th:src="@{/js/bootstrap.bundle.min.js}" defer></script>
<script th:src="@{/js/theme-init.js(v='18')}"></script>
<script th:src="@{/js/app.js(v='18')}" defer></script>
<script th:src="@{/js/ui-state.js(v='18')}" defer></script>
<script th:src="@{/js/md-content.js(v='18')}" defer></script>
<script th:src="@{/js/submit-guard.js(v='18')}" defer></script>
</head>
<body>
<div class="page">
<header class="page-header">
<div class="page-header-inner">
<a th:href="@{/}" class="logo">LMS<b>.</b></a>
<nav class="nav" id="top-nav">
<th:block th:if="${currentUser != null}">
<a th:if="${currentUser.admin}" th:href="@{/admin/groups}" class="nav-link">Группы</a>
<a th:if="${currentUser.admin}" th:href="@{/admin/users}" class="nav-link">Пользователи</a>
<a th:if="${currentUser.admin}" th:href="@{/admin/labs}" class="nav-link">Лабораторные</a>
<a th:unless="${currentUser.admin}" th:href="@{/student/labs}" class="nav-link">Лабораторные</a>
<a th:href="@{/change-password}" class="nav-link">Смена пароля</a>
</th:block>
</nav>
<button type="button" class="btn btn-secondary theme-btn"
aria-label="Сменить тему" title="Сменить тему">
<span class="theme-icon ico ico-theme" aria-hidden="true"></span>
<nav class="navbar b3-navbar navbar-expand-md sticky-top">
<div class="container">
<a th:href="@{/}" class="navbar-brand">LMS<b>.</b></a>
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#mobileNav"
aria-controls="mobileNav" aria-label="Меню">
<i class="bi bi-list"></i>
</button>
<button type="button" id="nav-burger" class="btn btn-sm nav-burger"
aria-label="Меню" aria-expanded="false">
<span class="ico ico-menu"></span>
</button>
<div class="user">
<th:block th:if="${currentUser != null}">
<a th:href="@{/admin}" th:if="${currentUser.admin}" class="nav-user" th:text="${currentUser.name}" title="Личный кабинет">—</a>
<a th:href="@{/student}" th:unless="${currentUser.admin}" class="nav-user" th:text="${currentUser.name}" title="Личный кабинет">—</a>
</th:block>
<a th:if="${currentUser == null}" th:href="@{/login}" class="btn btn-sm">Войти</a>
<form th:if="${currentUser != null}" th:action="@{/logout}" method="post">
<button class="btn btn-sm" type="submit">Выйти</button>
<input type="hidden" name="_csrf" th:value="${csrfToken}">
</form>
<div class="offcanvas offcanvas-end" tabindex="-1" id="mobileNav" aria-labelledby="mobileNavLabel">
<div class="offcanvas-header">
<span class="navbar-brand" id="mobileNavLabel">LMS<b>.</b></span>
<button type="button" class="navbar-close" data-bs-dismiss="offcanvas" aria-label="Закрыть">✕</button>
</div>
<div class="offcanvas-body">
<ul class="navbar-nav me-auto mb-2 mb-md-0">
<li class="nav-item" th:if="${currentUser != null and currentUser.admin}"><a class="nav-link" th:href="@{/admin/groups}">Группы</a></li>
<li class="nav-item" th:if="${currentUser != null and currentUser.admin}"><a class="nav-link" th:href="@{/admin/users}">Пользователи</a></li>
<li class="nav-item" th:if="${currentUser != null and currentUser.admin}"><a class="nav-link" th:href="@{/admin/labs}">Лабораторные</a></li>
<li class="nav-item" th:if="${currentUser != null and !currentUser.admin}"><a class="nav-link" th:href="@{/student/labs}">Лабораторные</a></li>
<li class="nav-item" th:if="${currentUser != null}"><a class="nav-link" th:href="@{/change-password}">Смена пароля</a></li>
</ul>
<div class="d-flex align-items-center gap-2">
<a th:href="@{/admin}" th:if="${currentUser != null and currentUser.admin}" class="nav-user" th:text="${currentUser.name}" title="Личный кабинет">—</a>
<a th:href="@{/student}" th:if="${currentUser != null and !currentUser.admin}" class="nav-user" th:text="${currentUser.name}" title="Личный кабинет">—</a>
<button type="button" class="btn btn-secondary btn-sm theme-btn" aria-label="Сменить тему" title="Сменить тему">
<i class="bi bi-circle-half"></i>
</button>
<a th:if="${currentUser == null}" th:href="@{/login}" class="btn btn-sm">Войти</a>
<form th:if="${currentUser != null}" th:action="@{/logout}" method="post" class="m-0">
<button class="btn btn-sm" type="submit">Выйти</button>
<input type="hidden" name="_csrf" th:value="${csrfToken}">
</form>
</div>
</div>
</div>
</div>
</header>
<div id="mobile-nav-overlay" class="mobile-nav-overlay" hidden></div>
<nav id="mobile-nav" class="mobile-nav offcanvas offcanvas-end" aria-label="Меню" tabindex="-1">
<div class="mobile-nav-head">
<span class="logo">LMS<b>.</b></span>
<button class="dlg-close mobile-nav-close" type="button" aria-label="Закрыть меню">✕</button>
</div>
<div class="mobile-nav-links nav">
<th:block th:if="${currentUser != null}">
<a th:if="${currentUser.admin}" th:href="@{/admin/groups}" class="nav-link">Группы</a>
<a th:if="${currentUser.admin}" th:href="@{/admin/users}" class="nav-link">Пользователи</a>
<a th:if="${currentUser.admin}" th:href="@{/admin/labs}" class="nav-link">Лабораторные</a>
<a th:unless="${currentUser.admin}" th:href="@{/student/labs}" class="nav-link">Лабораторные</a>
<a th:href="@{/change-password}" class="nav-link">Смена пароля</a>
</th:block>
</div>
<div class="mobile-nav-user">
<th:block th:if="${currentUser != null}">
<a th:href="@{/admin}" th:if="${currentUser.admin}" class="nav-user" th:text="${currentUser.name}" title="Личный кабинет">—</a>
<a th:href="@{/student}" th:unless="${currentUser.admin}" class="nav-user" th:text="${currentUser.name}" title="Личный кабинет">—</a>
</th:block>
<button type="button" class="btn btn-secondary btn-sm theme-btn" aria-label="Сменить тему" title="Сменить тему">
<span class="ico ico-theme"></span>
</button>
<form th:if="${currentUser != null}" th:action="@{/logout}" method="post">
<button class="btn btn-secondary btn-sm" type="submit">Выйти</button>
<input type="hidden" name="_csrf" th:value="${csrfToken}">
</form>
</div>
</nav>
<main class="page-main">
<main class="container page-main">
<th:block th:replace="${content}" />
</main>
<footer class="page-footer">

View File

@@ -5,52 +5,52 @@
<div th:fragment="toolbar(target)" class="md-toolbar" th:attr="data-md-target=${target}"
role="toolbar" aria-label="Форматирование текста">
<button type="button" class="md-btn" data-md="bold" title="Полужирный" aria-label="Полужирный">
<span class="md-ico ico ico-md-bold" aria-hidden="true"></span>
<i class="md-ico ico bi bi-type-bold" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="italic" title="Курсив" aria-label="Курсив">
<span class="md-ico ico ico-md-italic" aria-hidden="true"></span>
<i class="md-ico ico bi bi-type-italic" aria-hidden="true"></i>
</button>
<span class="md-sep"></span>
<button type="button" class="md-btn" data-md="h2" title="Заголовок раздела" aria-label="Заголовок раздела">
<span class="md-ico ico ico-md-h2" aria-hidden="true"></span>
<i class="md-ico ico bi bi-type-h2" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="ul" title="Маркированный список" aria-label="Маркированный список">
<span class="md-ico ico ico-md-ul" aria-hidden="true"></span>
<i class="md-ico ico bi bi-list-ul" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="ol" title="Нумерованный список" aria-label="Нумерованный список">
<span class="md-ico ico ico-md-ol" aria-hidden="true"></span>
<i class="md-ico ico bi bi-list-ol" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="quote" title="Цитата" aria-label="Цитата">
<span class="md-ico ico ico-md-quote" aria-hidden="true"></span>
<i class="md-ico ico bi bi-quote" aria-hidden="true"></i>
</button>
<span class="md-sep"></span>
<button type="button" class="md-btn" data-md="link" title="Ссылка" aria-label="Ссылка">
<span class="md-ico ico ico-md-link" aria-hidden="true"></span>
<i class="md-ico ico bi bi-link-45deg" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="code" title="Код в строке" aria-label="Код в строке">
<span class="md-ico ico ico-md-code" aria-hidden="true"></span>
<i class="md-ico ico bi bi-code" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="codeblock" title="Блок кода" aria-label="Блок кода">
<span class="md-ico ico ico-md-codeblock" aria-hidden="true"></span>
<i class="md-ico ico bi bi-code-square" aria-hidden="true"></i>
</button>
<span class="md-sep"></span>
<button type="button" class="md-btn" data-md="table" title="Таблица" aria-label="Таблица">
<span class="md-ico ico ico-md-table" aria-hidden="true"></span>
<i class="md-ico ico bi bi-table" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="figure" title="Картинка с подписью" aria-label="Картинка с подписью">
<span class="md-ico ico ico-md-figure" aria-hidden="true"></span>
<i class="md-ico ico bi bi-image" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="video" title="Видео" aria-label="Видео">
<span class="md-ico ico ico-md-video" aria-hidden="true"></span>
<i class="md-ico ico bi bi-camera-video" aria-hidden="true"></i>
</button>
<button type="button" class="md-btn" data-md="spoiler" title="Спойлер (скрытый текст)" aria-label="Спойлер">
<span class="md-ico ico ico-md-spoiler" aria-hidden="true"></span>
<i class="md-ico ico bi bi-eye-slash" aria-hidden="true"></i>
</button>
</div>
</body>

View File

@@ -46,21 +46,21 @@
</h1>
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
<button class="mini-btn" type="button" data-action="article-edit" title="Править статью" aria-label="Править статью"><span class="ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" data-action="article-edit" title="Править статью" aria-label="Править статью"><i class="ico bi bi-pencil" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/articles/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${article.id}">
<input type="hidden" name="published" th:value="${!article.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<a class="btn btn-secondary"
th:href="@{/learn/{c}(c=${article.chapterSlug})}">К главе</a>
<form class="inline-form" th:action="@{/admin/content/articles/{id}/delete(id=${article.id})}" method="post" data-confirm="Удалить статью?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${backParent}">
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>

View File

@@ -16,19 +16,19 @@
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
<button class="btn btn-primary" type="button" data-modal="modal-new-section">+ Раздел</button>
<button class="mini-btn" type="button" data-modal="modal-rename-chapter" title="Переименовать главу" aria-label="Переименовать главу"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" data-modal="modal-rename-chapter" title="Переименовать главу" aria-label="Переименовать главу"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/chapters/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${chapter.id}">
<input type="hidden" name="published" th:value="${!chapter.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<form class="inline-form" th:action="@{/admin/content/chapters/{id}/delete(id=${chapter.id})}" method="post" data-confirm="Удалить главу со всеми разделами и статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${backParent}">
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
@@ -50,30 +50,30 @@
<div class="sub-head sort-row">
<span th:if="${currentUser != null and currentUser.admin}" class="drag-handle" draggable="true"
th:attr="data-drag-kind='section',data-drag-id=${section.id}"
title="Перетащите мышью: изменить порядок разделов">⠿</span>
title="Перетащите мышью: изменить порядок разделов"><i class="bi bi-grip-vertical"></i></span>
<button type="button" class="sub-toggle" aria-expanded="false"
title="Свернуть или развернуть раздел">
<span class="sub-arrow" aria-hidden="true"><span class="cell-ico ico ico-chevron-down" aria-hidden="true"></span></span>
<span class="sub-arrow" aria-hidden="true"><i class="cell-ico ico bi bi-chevron-down" aria-hidden="true"></i></span>
</button>
<a class="group-title sort-title m-0"
th:href="@{/learn/{c}/{s}(c=${chapter.slug}, s=${section.slug})}"
th:text="${section.title}">Раздел</a>
<span th:if="${!section.published}" class="badge badge-warn">скрыто</span>
<div th:if="${currentUser != null and currentUser.admin}" class="sort-actions">
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/sections/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${section.id}">
<input type="hidden" name="published" th:value="${!section.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><span class="cell-ico ico ico-hand" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><i class="cell-ico ico bi bi-arrow-left-right" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post" data-confirm="Удалить раздел вместе со статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
</div>
@@ -83,21 +83,21 @@
<li th:each="article : ${section.articles}" th:attr="data-article-id=${article.id}">
<span th:if="${currentUser != null and currentUser.admin}" class="drag-handle" draggable="true"
th:attr="data-drag-kind='article',data-drag-id=${article.id}"
title="Перетащите мышью: изменить порядок или перенести в другой раздел">⠿</span>
title="Перетащите мышью: изменить порядок или перенести в другой раздел"><i class="bi bi-grip-vertical"></i></span>
<a class="sort-title"
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug})}"
th:text="${article.title}">Статья</a>
<span th:if="${!article.published}" class="badge badge-warn">скрыто</span>
<div th:if="${currentUser != null and currentUser.admin}" class="sort-actions">
<a class="mini-btn" title="Править статью" aria-label="Править статью"
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}"><span class="ico ico-pencil" aria-hidden="true"></span></a>
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}"><i class="ico bi bi-pencil" aria-hidden="true"></i></a>
<form class="inline-form" th:action="@{/admin/content/articles/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${article.id}">
<input type="hidden" name="published" th:value="${!article.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
</div>
</li>

View File

@@ -24,7 +24,7 @@
<div class="sort-row">
<span th:if="${currentUser != null and currentUser.admin}" class="drag-handle" draggable="true"
th:attr="data-drag-kind='chapter',data-drag-id=${chapter.id}"
title="Перетащите мышью: изменить порядок глав">⠿</span>
title="Перетащите мышью: изменить порядок глав"><i class="bi bi-grip-vertical"></i></span>
<h2 class="section-title sort-title m-0" th:text="${chapter.title}">Глава</h2>
<span th:if="${!chapter.published}" class="badge badge-warn">скрыто</span>
<div th:if="${currentUser != null and currentUser.admin}" class="sort-actions">
@@ -32,13 +32,13 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${chapter.id}">
<input type="hidden" name="published" th:value="${!chapter.published}">
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${chapter.published}" title="Скрыть главу" aria-label="Скрыть главу"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${chapter.published}" title="Опубликовать главу" aria-label="Опубликовать главу"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-chapter-${chapter.id}|" title="Переименовать главу" aria-label="Переименовать главу"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-chapter-${chapter.id}|" title="Переименовать главу" aria-label="Переименовать главу"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/chapters/{id}/delete(id=${chapter.id})}" method="post" data-confirm="Удалить главу со всеми разделами и статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить главу со всеми разделами и статьями" aria-label="Удалить главу со всеми разделами и статьями"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
</div>
@@ -51,10 +51,10 @@
<div class="sub-head sort-row">
<span th:if="${currentUser != null and currentUser.admin}" class="drag-handle" draggable="true"
th:attr="data-drag-kind='section',data-drag-id=${section.id}"
title="Перетащите мышью: изменить порядок или перенести раздел">⠿</span>
title="Перетащите мышью: изменить порядок или перенести раздел"><i class="bi bi-grip-vertical"></i></span>
<button type="button" class="sub-toggle" aria-expanded="false"
title="Свернуть или развернуть раздел">
<span class="sub-arrow" aria-hidden="true"><span class="cell-ico ico ico-chevron-down" aria-hidden="true"></span></span>
<span class="sub-arrow" aria-hidden="true"><i class="cell-ico ico bi bi-chevron-down" aria-hidden="true"></i></span>
</button>
<a class="group-title sort-title m-0"
th:href="@{/learn/{c}/{s}(c=${chapter.slug}, s=${section.slug})}"
@@ -65,14 +65,14 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${section.id}">
<input type="hidden" name="published" th:value="${!section.published}">
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи в другой раздел" aria-label="Перенести статьи в другой раздел"><span class="cell-ico ico ico-hand" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-rename-section-${section.id}|" title="Переименовать раздел" aria-label="Переименовать раздел"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
<button class="mini-btn" type="button" th:attr="data-modal=|modal-move-${section.id}|" title="Перенести статьи в другой раздел" aria-label="Перенести статьи в другой раздел"><i class="cell-ico ico bi bi-arrow-left-right" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post" data-confirm="Удалить раздел со всеми статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить раздел со всеми статьями" aria-label="Удалить раздел со всеми статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить раздел со всеми статьями" aria-label="Удалить раздел со всеми статьями"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
</div>
@@ -82,7 +82,7 @@
<li th:each="article : ${section.articles}" th:attr="data-article-id=${article.id}">
<span th:if="${currentUser != null and currentUser.admin}" class="drag-handle" draggable="true"
th:attr="data-drag-kind='article',data-drag-id=${article.id}"
title="Перетащите мышью: изменить порядок или перенести статью">⠿</span>
title="Перетащите мышью: изменить порядок или перенести статью"><i class="bi bi-grip-vertical"></i></span>
<a class="sort-title"
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug})}"
th:text="${article.title}">Статья</a>
@@ -92,14 +92,14 @@
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${article.id}">
<input type="hidden" name="published" th:value="${!article.published}">
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<a class="mini-btn" title="Править статью" aria-label="Править статью"
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}"><span class="ico ico-pencil" aria-hidden="true"></span></a>
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}"><i class="ico bi bi-pencil" aria-hidden="true"></i></a>
<form class="inline-form" th:action="@{/admin/content/articles/{id}/delete(id=${article.id})}" method="post" data-confirm="Удалить статью?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить статью" aria-label="Удалить статью"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
</li>

View File

@@ -18,20 +18,20 @@
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2 mb-4">
<button class="btn btn-primary" type="button" data-modal="modal-new-article">+ Статья</button>
<button class="mini-btn" type="button" data-modal="modal-rename-section" title="Переименовать раздел" aria-label="Переименовать раздел"><span class="cell-ico ico ico-pencil" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" data-modal="modal-rename-section" title="Переименовать раздел" aria-label="Переименовать раздел"><i class="cell-ico ico bi bi-pencil" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/sections/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${section.id}">
<input type="hidden" name="published" th:value="${!section.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${section.published}" title="Скрыть раздел" aria-label="Скрыть раздел"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${section.published}" title="Опубликовать раздел" aria-label="Опубликовать раздел"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<button class="mini-btn" type="button" data-modal="modal-move-section" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><span class="cell-ico ico ico-hand" aria-hidden="true"></span></button>
<button class="mini-btn" type="button" data-modal="modal-move-section" title="Перенести статьи и удалить" aria-label="Перенести статьи и удалить"><i class="cell-ico ico bi bi-arrow-left-right" aria-hidden="true"></i></button>
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post" data-confirm="Удалить раздел вместе со статьями?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="back" th:value="${backParent}">
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><span class="cell-ico ico ico-trash" aria-hidden="true"></span></button>
<button class="mini-btn danger" type="submit" title="Удалить раздел вместе со статьями" aria-label="Удалить раздел вместе со статьями"><i class="cell-ico ico bi bi-trash" aria-hidden="true"></i></button>
</form>
</div>
@@ -50,21 +50,21 @@
<td>
<span th:if="${currentUser != null and currentUser.admin}" class="drag-handle" draggable="true"
th:attr="data-drag-kind='article',data-drag-id=${article.id}"
title="Перетащите, чтобы изменить порядок статей">⠿</span>
title="Перетащите, чтобы изменить порядок статей"><i class="bi bi-grip-vertical"></i></span>
<a th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug})}"
th:text="${article.title}">—</a>
<span th:if="${!article.published}" class="badge badge-warn ml-1">скрыто</span>
</td>
<td class="text-right actions-cell">
<a class="mini-btn" title="Править статью" aria-label="Править статью"
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}"><span class="ico ico-pencil" aria-hidden="true"></span></a>
th:href="@{/learn/{c}/{s}/{a}(c=${chapter.slug}, s=${section.slug}, a=${article.slug}, edit=1)}"><i class="ico bi bi-pencil" aria-hidden="true"></i></a>
<form class="inline-form" th:action="@{/admin/content/articles/toggle-published}" method="post">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<input type="hidden" name="id" th:value="${article.id}">
<input type="hidden" name="published" th:value="${!article.published}">
<input type="hidden" name="back" th:value="${back}">
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><span class="cell-ico ico ico-eye-open" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><span class="cell-ico ico ico-eye-closed" aria-hidden="true"></span></button>
<button class="mini-btn" type="submit" th:if="${article.published}" title="Скрыть статью" aria-label="Скрыть статью"><i class="cell-ico ico bi bi-eye" aria-hidden="true"></i></button>
<button class="mini-btn" type="submit" th:unless="${article.published}" title="Опубликовать статью" aria-label="Опубликовать статью"><i class="cell-ico ico bi bi-eye-slash" aria-hidden="true"></i></button>
</form>
<form class="inline-form" th:action="@{/admin/content/articles/{id}/delete(id=${article.id})}" method="post" data-confirm="Удалить статью?">
<input type="hidden" name="_csrf" th:value="${csrfToken}">

View File

@@ -5,19 +5,19 @@
<div id="content">
<h1 class="page-title">Здравствуйте, <span th:text="${currentUser.name}">студент</span></h1>
<div class="grid grid-stats">
<div class="card card-body"><p class="stat-title">Сдано работ</p>
<p class="stat-value" th:text="${submittedCount}">0</p></div>
<div class="card card-body"><p class="stat-title">Средний балл</p>
<div class="row row-cols-1 row-cols-sm-2 row-cols-xl-4 g-3">
<div class="col"><div class="card card-body h-100"><p class="stat-title">Сдано работ</p>
<p class="stat-value" th:text="${submittedCount}">0</p></div></div>
<div class="col"><div class="card card-body h-100"><p class="stat-title">Средний балл</p>
<p class="stat-value" th:if="${average != null}" th:text="${average}">—</p>
<p class="stat-value text-muted" th:if="${average == null}">—</p></div>
<div class="card card-body"><p class="stat-title">Лучшая оценка</p>
<p class="stat-value text-muted" th:if="${average == null}">—</p></div></div>
<div class="col"><div class="card card-body h-100"><p class="stat-title">Лучшая оценка</p>
<p class="stat-value" th:if="${best != null}" th:text="${best.toPlainString()}">—</p>
<p class="stat-value text-muted" th:if="${best == null}">—</p></div>
<div class="card card-body"><p class="stat-title">Слабое место</p>
<p class="stat-value text-muted" th:if="${best == null}">—</p></div></div>
<div class="col"><div class="card card-body h-100"><p class="stat-title">Слабое место</p>
<p class="stat-value" th:if="${worst != null}" th:text="${worst.toPlainString()}">—</p>
<p class="stat-value text-muted" th:if="${worst == null}">—</p></div>
</div>
<p class="stat-value text-muted" th:if="${worst == null}">—</p></div></div>
</div>
<div class="flex flex-wrap gap-2 mt-8">
<a class="btn btn-primary" th:href="@{/student/labs}">Лабораторные</a>
@@ -25,8 +25,8 @@
</div>
<h2 class="section-title mt-8">Мои лабораторные</h2>
<div class="grid grid-2">
<div th:each="c : ${cards}" class="card">
<div class="row row-cols-1 row-cols-lg-2 g-4">
<div th:each="c : ${cards}" class="col"><div class="card h-100">
<div class="card-body">
<h3 class="section-title">
<a th:href="@{/student/labs/{id}(id=${c.id})}" th:text="|Лабораторная работа ${c.number}|">ЛР</a>
@@ -40,6 +40,7 @@
</div>
</div>
</div>
</div>
<div th:if="${#lists.isEmpty(cards)}" class="card card-body text-muted">Лабораторных пока нет.</div>
</div>
</div>

View File

@@ -4,8 +4,8 @@
<body>
<div id="content">
<h1 class="page-title">Лабораторные работы</h1>
<div class="grid grid-2">
<div th:each="c : ${cards}" class="card">
<div class="row row-cols-1 row-cols-lg-2 g-4">
<div th:each="c : ${cards}" class="col"><div class="card h-100">
<div class="card-body">
<h2 class="section-title">
<a th:href="@{/student/labs/{id}(id=${c.id})}" th:text="|Лабораторная работа ${c.number}. ${c.title}|">ЛР</a>
@@ -23,6 +23,7 @@
</p>
</div>
</div>
</div>
<div th:if="${#lists.isEmpty(cards)}" class="card card-body text-muted">Опубликованных лабораторных пока нет.</div>
</div>
</div>