Learn: drag & drop sections and articles on chapter and section pages

This commit is contained in:
2026-09-17 19:15:56 +03:00
parent aa2b1638b2
commit a4ea358d27
10 changed files with 321 additions and 126 deletions

View File

@@ -73,6 +73,12 @@ data class ReorderSection(val id: String, val articles: List<ReorderArticle> = e
data class ReorderChapter(val id: String, val sections: List<ReorderSection> = emptyList())
data class ReorderTree(val chapters: List<ReorderChapter> = emptyList())
/** Порядок разделов внутри одной главы (drag & drop на странице главы). */
data class ReorderSections(val sections: List<ReorderSection> = emptyList())
/** Порядок статей внутри одного раздела (drag & drop на странице раздела). */
data class ReorderArticles(val articles: List<ReorderArticle> = emptyList())
private data class ParsedArticle(
val path: String,
val chapterPos: Int,
@@ -184,6 +190,38 @@ class ContentService(
}
}
/** Порядок разделов внутри главы и статей внутри них (drag & drop на странице главы). */
@Transactional
fun reorderSections(chapterId: UUID, tree: List<ReorderSection>) {
tree.forEachIndexed { sectionIndex, sectionDto ->
val section = sections.findById(uuidOrNull(sectionDto.id) ?: return@forEachIndexed).orElse(null)
?: return@forEachIndexed
if (section.chapterId != chapterId) return@forEachIndexed
section.position = sectionIndex + 1
sections.save(section)
sectionDto.articles.forEachIndexed { articleIndex, articleDto ->
val article = articles.findById(uuidOrNull(articleDto.id) ?: return@forEachIndexed).orElse(null)
?: return@forEachIndexed
article.sectionId = section.id
article.position = articleIndex + 1
articles.save(article)
}
}
}
/** Порядок статей внутри одного раздела (drag & drop на странице раздела). */
@Transactional
fun reorderArticles(sectionId: UUID, tree: List<ReorderArticle>) {
tree.forEachIndexed { articleIndex, articleDto ->
val article = articles.findById(uuidOrNull(articleDto.id) ?: return@forEachIndexed).orElse(null)
?: return@forEachIndexed
if (article.sectionId != sectionId) return@forEachIndexed
article.position = articleIndex + 1
articles.save(article)
}
}
private fun uuidOrNull(value: String): UUID? =
runCatching { UUID.fromString(value) }.getOrNull()

View File

@@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import ru.ysu.lms.academic.content.ContentImportResult
import ru.ysu.lms.academic.content.ContentService
import ru.ysu.lms.academic.content.ReorderArticles
import ru.ysu.lms.academic.content.ReorderSections
import ru.ysu.lms.academic.content.ReorderTree
import ru.ysu.lms.common.BadRequestException
import java.util.UUID
@@ -78,6 +80,18 @@ class ContentAdminController(private val contentService: ContentService) {
contentService.reorder(body)
}
/** Порядок разделов внутри главы (drag & drop на странице главы). */
@PostMapping("/chapters/{id}/sections/reorder")
fun reorderSections(@PathVariable id: UUID, @RequestBody body: ReorderSections) {
contentService.reorderSections(id, body.sections)
}
/** Порядок статей внутри раздела (drag & drop на странице раздела). */
@PostMapping("/sections/{id}/articles/reorder")
fun reorderArticles(@PathVariable id: UUID, @RequestBody body: ReorderArticles) {
contentService.reorderArticles(id, body.articles)
}
@PostMapping("/import")
fun import(
@RequestParam("file") file: MultipartFile,