Phase 6: accordion in /learn; README feature notes; tests green

- collapse chapters/sections via styled <details> with chevrons
- identity testcontainers smoke passes on JDK 21
- README: student UX, comments, JDK note (logback ThrowableProxy in
  fat-jars → restart recovery), data dirs
This commit is contained in:
2026-09-12 02:00:45 +03:00
parent 76a9a915c2
commit 5e60cc3819
3 changed files with 51 additions and 13 deletions

View File

@@ -231,3 +231,16 @@ code.code { padding: 0.125rem 0.5rem; border-radius: 0.25rem; font-family: monos
.chip-neg { background: #b3283b; color: #fff; }
/* Defense timer */
/* Accordion (главы/разделы в /learn) */
details.accordion > summary.acc-head { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; }
details.accordion > summary::-webkit-details-marker { display: none; }
details.accordion[open] > .acc-head, details.accordion > summary { margin-bottom: 0; }
.acc-arrow { width: 10px; height: 10px; border-right: 2px solid var(--c-text-muted); border-bottom: 2px solid var(--c-text-muted); transform: rotate(45deg); transition: transform 0.2s; margin-left: .75rem;}
details.accordion[open] > summary > .acc-arrow { transform: rotate(-135deg); }
.acc-title { margin: 0; font-size: 1.0625rem; }
.acc-body { border-top: 1px solid var(--c-border); }
details.sub-acc > summary.sub-head { list-style: none; cursor: pointer; }
details.sub-acc > summary::-webkit-details-marker { display: none; }
.sub-arrow { display: inline-block; width: 8px; height: 8px; border-right: 2px solid var(--c-text-muted); border-bottom: 2px solid var(--c-text-muted); transform: rotate(45deg); transition: transform 0.2s; margin-right: 6px; vertical-align: 2px;}
details.sub-acc[open] > summary .sub-arrow { transform: rotate(-135deg); }
.sub-actions { margin: 0 0 2px 18px; }

View File

@@ -18,8 +18,12 @@
Материалы ещё не добавлены.
</div>
<div th:each="chapter : ${tree?.chapters}" class="card mb-4">
<div class="card-body">
<details th:each="chapter : ${tree?.chapters}" class="card mb-4 accordion" open>
<summary class="card-body acc-head">
<h2 class="section-title acc-title" th:text="${chapter.title}">Глава</h2>
<span class="acc-arrow" aria-hidden="true"></span>
</summary>
<div class="card-body acc-body">
<div class="flex flex-wrap items-center justify-between gap-4 mb-4">
<h2 class="section-title" th:text="${chapter.title}">Глава</h2>
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2">
@@ -35,20 +39,24 @@
<p th:if="${#lists.isEmpty(chapter.sections)}" class="text-sm text-muted">Разделов пока нет</p>
<div th:each="section : ${chapter.sections}" class="mt-4">
<div class="flex flex-wrap items-center justify-between gap-4">
<h3 class="group-title">
<details th:each="section : ${chapter.sections}" class="mt-4 sub-acc" open>
<summary class="sub-head">
<h3 class="group-title m-0">
<span class="sub-arrow" aria-hidden="true"></span>
<a th:href="@{/learn/{c}/{s}(c=${chapter.slug}, s=${section.slug})}" th:text="${section.title}">Раздел</a>
</h3>
</summary>
<div class="flex flex-wrap items-center justify-between gap-4 sub-actions">
<div class="spacer"></div>
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap gap-2">
<button class="btn btn-secondary btn-sm" type="button"
th:data-modal="|modal-rename-section-${section.id}|" onclick="openModalData(this)">Переименовать</button>
<button class="btn btn-secondary btn-sm" type="button"
th:data-modal="|modal-move-${section.id}|" onclick="openModalData(this)">Перенести статьи</button>
<form th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post"
<form class="inline-form" th:action="@{/admin/content/sections/{id}/delete(id=${section.id})}" method="post"
onsubmit="return confirm('Удалить раздел со всеми статьями?')">
<input type="hidden" name="_csrf" th:value="${csrfToken}">
<button class="btn btn-danger btn-sm" type="submit">Удалить</button>
<button class="btn btn-danger btn-sm" type="submit">Удалить раздел</button>
</form>
</div>
</div>
@@ -105,8 +113,7 @@
</div>
</dialog>
</th:block>
</div>
</details>
<th:block th:if="${currentUser != null and currentUser.admin}">
<dialog th:id="|modal-rename-chapter-${chapter.id}|" class="modal">
<div class="modal-header">
@@ -125,7 +132,7 @@
</dialog>
</th:block>
</div>
</div>
</details>
<dialog id="modal-import" class="modal">
<div class="modal-header">