Merge pull request #1623 from JoeBrar/feature/reorder-chapters
feat: added chapter re-ordering functionality for courses
This commit is contained in:
@@ -23,119 +23,135 @@
|
|||||||
'border-2 rounded-md py-2 px-2': showOutline && outline.data?.length,
|
'border-2 rounded-md py-2 px-2': showOutline && outline.data?.length,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Disclosure
|
<Draggable
|
||||||
v-slot="{ open }"
|
:list="outline.data"
|
||||||
v-for="(chapter, index) in outline.data"
|
:disabled="!allowEdit"
|
||||||
:key="chapter.name"
|
item-key="name"
|
||||||
:defaultOpen="openChapterDetail(chapter.idx)"
|
group="chapters"
|
||||||
|
@end="updateChapterOrder"
|
||||||
>
|
>
|
||||||
<DisclosureButton ref="" class="flex items-center w-full p-2 group">
|
<template #item="{ element: chapter, index }">
|
||||||
<ChevronRight
|
<div class="chapter-item">
|
||||||
:class="{
|
<Disclosure
|
||||||
'rotate-90 transform duration-200': open,
|
v-slot="{ open }"
|
||||||
'duration-200': !open,
|
:key="chapter.name"
|
||||||
hidden: chapter.is_scorm_package,
|
:defaultOpen="openChapterDetail(chapter.idx)"
|
||||||
open: index == 1,
|
>
|
||||||
}"
|
<DisclosureButton
|
||||||
class="h-4 w-4 text-ink-gray-9 stroke-1"
|
ref=""
|
||||||
/>
|
class="flex items-center w-full p-2 group"
|
||||||
<div
|
|
||||||
class="text-base text-left text-ink-gray-9 font-medium leading-5 ml-2"
|
|
||||||
@click="redirectToChapter(chapter)"
|
|
||||||
>
|
|
||||||
{{ chapter.title }}
|
|
||||||
</div>
|
|
||||||
<div class="flex ml-auto space-x-4">
|
|
||||||
<Tooltip :text="__('Edit Chapter')" placement="bottom">
|
|
||||||
<FilePenLine
|
|
||||||
v-if="allowEdit"
|
|
||||||
@click.prevent="openChapterModal(chapter)"
|
|
||||||
class="h-4 w-4 text-ink-gray-9 invisible group-hover:visible"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip :text="__('Delete Chapter')" placement="bottom">
|
|
||||||
<Trash2
|
|
||||||
v-if="allowEdit"
|
|
||||||
@click.prevent="trashChapter(chapter.name)"
|
|
||||||
class="h-4 w-4 text-ink-red-3 invisible group-hover:visible"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</DisclosureButton>
|
|
||||||
<DisclosurePanel v-if="!chapter.is_scorm_package">
|
|
||||||
<Draggable
|
|
||||||
v-if="!chapter.is_scorm_package"
|
|
||||||
:list="chapter.lessons"
|
|
||||||
:disabled="!allowEdit"
|
|
||||||
item-key="name"
|
|
||||||
group="items"
|
|
||||||
@end="updateOutline"
|
|
||||||
:data-chapter="chapter.name"
|
|
||||||
>
|
|
||||||
<template #item="{ element: lesson }">
|
|
||||||
<div
|
|
||||||
class="outline-lesson pl-8 py-2 pr-4 text-ink-gray-9"
|
|
||||||
:class="
|
|
||||||
isActiveLesson(lesson.number) ? 'bg-surface-gray-3' : ''
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<router-link
|
<ChevronRight
|
||||||
:to="{
|
:class="{
|
||||||
name: allowEdit ? 'LessonForm' : 'Lesson',
|
'rotate-90 transform duration-200': open,
|
||||||
params: {
|
'duration-200': !open,
|
||||||
courseName: courseName,
|
hidden: chapter.is_scorm_package,
|
||||||
chapterNumber: lesson.number.split('.')[0],
|
open: index == 1,
|
||||||
lessonNumber: lesson.number.split('.')[1],
|
|
||||||
},
|
|
||||||
}"
|
}"
|
||||||
|
class="h-4 w-4 text-ink-gray-9 stroke-1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="text-base text-left text-ink-gray-9 font-medium leading-5 ml-2"
|
||||||
|
@click="redirectToChapter(chapter)"
|
||||||
>
|
>
|
||||||
<div class="flex items-center text-sm leading-5 group">
|
{{ chapter.title }}
|
||||||
<MonitorPlay
|
</div>
|
||||||
v-if="lesson.icon === 'icon-youtube'"
|
<div class="flex ml-auto space-x-4">
|
||||||
class="h-4 w-4 stroke-1 mr-2"
|
<Tooltip :text="__('Edit Chapter')" placement="bottom">
|
||||||
|
<FilePenLine
|
||||||
|
v-if="allowEdit"
|
||||||
|
@click.prevent="openChapterModal(chapter)"
|
||||||
|
class="h-4 w-4 text-ink-gray-9 invisible group-hover:visible"
|
||||||
/>
|
/>
|
||||||
<HelpCircle
|
</Tooltip>
|
||||||
v-else-if="lesson.icon === 'icon-quiz'"
|
<Tooltip :text="__('Delete Chapter')" placement="bottom">
|
||||||
class="h-4 w-4 stroke-1 mr-2"
|
|
||||||
/>
|
|
||||||
<FileText
|
|
||||||
v-else-if="lesson.icon === 'icon-list'"
|
|
||||||
class="h-4 w-4 text-ink-gray-9 stroke-1 mr-2"
|
|
||||||
/>
|
|
||||||
{{ lesson.title }}
|
|
||||||
<Trash2
|
<Trash2
|
||||||
v-if="allowEdit"
|
v-if="allowEdit"
|
||||||
@click.prevent="trashLesson(lesson.name, chapter.name)"
|
@click.prevent="trashChapter(chapter.name)"
|
||||||
class="h-4 w-4 text-ink-red-3 ml-auto invisible group-hover:visible"
|
class="h-4 w-4 text-ink-red-3 invisible group-hover:visible"
|
||||||
/>
|
/>
|
||||||
<Check
|
</Tooltip>
|
||||||
v-if="lesson.is_complete"
|
</div>
|
||||||
class="h-4 w-4 text-green-700 ml-2"
|
</DisclosureButton>
|
||||||
/>
|
<DisclosurePanel v-if="!chapter.is_scorm_package">
|
||||||
</div>
|
<Draggable
|
||||||
</router-link>
|
v-if="!chapter.is_scorm_package"
|
||||||
</div>
|
:list="chapter.lessons"
|
||||||
</template>
|
:disabled="!allowEdit"
|
||||||
</Draggable>
|
item-key="name"
|
||||||
<div v-if="allowEdit" class="flex mt-2 mb-4 pl-8">
|
group="items"
|
||||||
<router-link
|
@end="updateOutline"
|
||||||
v-if="!chapter.is_scorm_package"
|
:data-chapter="chapter.name"
|
||||||
:to="{
|
>
|
||||||
name: 'LessonForm',
|
<template #item="{ element: lesson }">
|
||||||
params: {
|
<div
|
||||||
courseName: courseName,
|
class="outline-lesson pl-8 py-2 pr-4 text-ink-gray-9"
|
||||||
chapterNumber: chapter.idx,
|
:class="
|
||||||
lessonNumber: chapter.lessons.length + 1,
|
isActiveLesson(lesson.number) ? 'bg-surface-gray-3' : ''
|
||||||
},
|
"
|
||||||
}"
|
>
|
||||||
>
|
<router-link
|
||||||
<Button>
|
:to="{
|
||||||
{{ __('Add Lesson') }}
|
name: allowEdit ? 'LessonForm' : 'Lesson',
|
||||||
</Button>
|
params: {
|
||||||
</router-link>
|
courseName: courseName,
|
||||||
|
chapterNumber: lesson.number.split('.')[0],
|
||||||
|
lessonNumber: lesson.number.split('.')[1],
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="flex items-center text-sm leading-5 group">
|
||||||
|
<MonitorPlay
|
||||||
|
v-if="lesson.icon === 'icon-youtube'"
|
||||||
|
class="h-4 w-4 stroke-1 mr-2"
|
||||||
|
/>
|
||||||
|
<HelpCircle
|
||||||
|
v-else-if="lesson.icon === 'icon-quiz'"
|
||||||
|
class="h-4 w-4 stroke-1 mr-2"
|
||||||
|
/>
|
||||||
|
<FileText
|
||||||
|
v-else-if="lesson.icon === 'icon-list'"
|
||||||
|
class="h-4 w-4 text-ink-gray-9 stroke-1 mr-2"
|
||||||
|
/>
|
||||||
|
{{ lesson.title }}
|
||||||
|
<Trash2
|
||||||
|
v-if="allowEdit"
|
||||||
|
@click.prevent="
|
||||||
|
trashLesson(lesson.name, chapter.name)
|
||||||
|
"
|
||||||
|
class="h-4 w-4 text-ink-red-3 ml-auto invisible group-hover:visible"
|
||||||
|
/>
|
||||||
|
<Check
|
||||||
|
v-if="lesson.is_complete"
|
||||||
|
class="h-4 w-4 text-green-700 ml-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
|
<div v-if="allowEdit" class="flex mt-2 mb-4 pl-8">
|
||||||
|
<router-link
|
||||||
|
v-if="!chapter.is_scorm_package"
|
||||||
|
:to="{
|
||||||
|
name: 'LessonForm',
|
||||||
|
params: {
|
||||||
|
courseName: courseName,
|
||||||
|
chapterNumber: chapter.idx,
|
||||||
|
lessonNumber: chapter.lessons.length + 1,
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Button>
|
||||||
|
{{ __('Add Lesson') }}
|
||||||
|
</Button>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</DisclosurePanel>
|
||||||
|
</Disclosure>
|
||||||
</div>
|
</div>
|
||||||
</DisclosurePanel>
|
</template>
|
||||||
</Disclosure>
|
</Draggable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ChapterModal
|
<ChapterModal
|
||||||
@@ -242,6 +258,20 @@ const updateLessonIndex = createResource({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const updateChapterIndex = createResource({
|
||||||
|
url: 'lms.lms.api.update_chapter_index',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
chapter: values.chapter,
|
||||||
|
course: values.course,
|
||||||
|
idx: values.idx,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess() {
|
||||||
|
toast.success(__('Chapter moved successfully'))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const trashLesson = (lessonName, chapterName) => {
|
const trashLesson = (lessonName, chapterName) => {
|
||||||
$dialog({
|
$dialog({
|
||||||
title: __('Delete this lesson?'),
|
title: __('Delete this lesson?'),
|
||||||
@@ -287,6 +317,14 @@ const updateOutline = (e) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateChapterOrder = (e) => {
|
||||||
|
updateChapterIndex.submit({
|
||||||
|
chapter: e.item.__draggable_context.element.name,
|
||||||
|
course: props.courseName,
|
||||||
|
idx: e.newIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const deleteChapter = createResource({
|
const deleteChapter = createResource({
|
||||||
url: 'lms.lms.api.delete_chapter',
|
url: 'lms.lms.api.delete_chapter',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
|
|||||||
@@ -676,6 +676,27 @@ def update_index(lessons, chapter):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def update_chapter_index(chapter, course, idx):
|
||||||
|
"""Update the index of a chapter within a course"""
|
||||||
|
chapters = frappe.get_all(
|
||||||
|
"Chapter Reference",
|
||||||
|
{"parent": course},
|
||||||
|
pluck="chapter",
|
||||||
|
order_by="idx",
|
||||||
|
)
|
||||||
|
|
||||||
|
if chapter in chapters:
|
||||||
|
chapters.remove(chapter)
|
||||||
|
|
||||||
|
chapters.insert(idx, chapter)
|
||||||
|
|
||||||
|
for i, chapter_name in enumerate(chapters):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Chapter Reference", {"chapter": chapter_name, "parent": course}, "idx", i + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def get_categories(doctype, filters):
|
def get_categories(doctype, filters):
|
||||||
categoryOptions = []
|
categoryOptions = []
|
||||||
|
|||||||
Reference in New Issue
Block a user