Content: drag & drop reordering and moving of chapters, sections and articles
- academic: POST /api/v1/content/reorder applies a full tree arrangement in one request — chapter order, section order and section->chapter moves, article order and article->section moves (positions and parent ids updated) - web: POST /admin/content/reorder accepts the tree from JS (CSRF via query param), calls the gateway with the admin token - /learn admin view: draggable handles (⠿) for chapters, sections and articles; drop before/after to reorder, drop into a chapter/section to move; visual indicators; structure is collected from DOM and saved, then page reloads - docs: class diagram method + new sequence diagram for drag & drop
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<html lang="ru" xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/base :: layout(~{::title}, ~{::#content})}">
|
||||
<head><title>Материалы — LMS</title></head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="content" th:attr="data-csrf=${csrfToken}">
|
||||
<div class="flex items-center justify-between gap-4 mb-6">
|
||||
<h1 class="page-title">Учебные материалы</h1>
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex gap-2">
|
||||
@@ -14,6 +14,8 @@
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="card card-body text-sm text-muted mb-4">
|
||||
Режим администратора: скрытые главы, разделы и статьи помечены значком «скрыто»
|
||||
и видны только вам. Кнопка-значок публикует материал для студентов или снова скрывает его.
|
||||
Перетаскивайте за значок ⠿, чтобы менять порядок глав, разделов и статей, а также
|
||||
переносить разделы в другую главу и статьи — в другой раздел.
|
||||
</div>
|
||||
<div th:if="${notice}" class="alert alert-success" th:text="${notice}"></div>
|
||||
<div th:if="${error}" class="alert alert-error" th:text="${error}"></div>
|
||||
@@ -22,7 +24,8 @@
|
||||
Материалы ещё не добавлены.
|
||||
</div>
|
||||
|
||||
<details th:each="chapter, chStat : ${tree?.chapters}" class="card mb-4 accordion" th:attr="open=${chStat.last}">
|
||||
<details th:each="chapter, chStat : ${tree?.chapters}" class="card mb-4 accordion"
|
||||
th:attr="open=${chStat.last},data-chapter-id=${chapter.id}">
|
||||
<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>
|
||||
@@ -34,6 +37,9 @@
|
||||
<span th:if="${!chapter.published}" class="badge badge-warn ml-2">скрыто</span>
|
||||
</h2>
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap items-center gap-2">
|
||||
<span class="drag-handle" draggable="true"
|
||||
th:attr="data-drag-kind='chapter',data-drag-id=${chapter.id}"
|
||||
title="Перетащите мышью: изменить порядок или перенести главу">⠿</span>
|
||||
<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}">
|
||||
@@ -54,7 +60,8 @@
|
||||
|
||||
<p th:if="${#lists.isEmpty(chapter.sections)}" class="text-sm text-muted">Разделов пока нет</p>
|
||||
|
||||
<details th:each="section : ${chapter.sections}" class="mt-4 sub-acc">
|
||||
<details th:each="section : ${chapter.sections}" class="mt-4 sub-acc"
|
||||
th:attr="data-section-id=${section.id}">
|
||||
<summary class="sub-head">
|
||||
<h3 class="group-title m-0">
|
||||
<span class="sub-arrow" aria-hidden="true"></span>
|
||||
@@ -64,7 +71,10 @@
|
||||
</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">
|
||||
<div th:if="${currentUser != null and currentUser.admin}" class="flex flex-wrap items-center gap-2">
|
||||
<span class="drag-handle" draggable="true"
|
||||
th:attr="data-drag-kind='section',data-drag-id=${section.id}"
|
||||
title="Перетащите мышью: изменить порядок или перенести раздел">⠿</span>
|
||||
<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}">
|
||||
@@ -84,12 +94,16 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-pad text-sm">
|
||||
<li th:each="article : ${section.articles}" class="flex items-center gap-2">
|
||||
<ul class="list-pad text-sm" data-article-list>
|
||||
<li th:each="article : ${section.articles}" class="flex items-center gap-2"
|
||||
th:attr="data-article-id=${article.id}">
|
||||
<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">скрыто</span>
|
||||
<th:block th:if="${currentUser != null and currentUser.admin}">
|
||||
<span class="drag-handle" draggable="true"
|
||||
th:attr="data-drag-kind='article',data-drag-id=${article.id}"
|
||||
title="Перетащите мышью: изменить порядок или перенести статью">⠿</span>
|
||||
<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}">
|
||||
@@ -243,6 +257,7 @@
|
||||
</div>
|
||||
</dialog>
|
||||
</th:block>
|
||||
<script src="/js/content-sort.js" defer></script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user