feat: edit related courses from frontend
This commit is contained in:
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -83,6 +83,7 @@ declare module 'vue' {
|
|||||||
QuizBlock: typeof import('./src/components/QuizBlock.vue')['default']
|
QuizBlock: typeof import('./src/components/QuizBlock.vue')['default']
|
||||||
QuizInVideo: typeof import('./src/components/Modals/QuizInVideo.vue')['default']
|
QuizInVideo: typeof import('./src/components/Modals/QuizInVideo.vue')['default']
|
||||||
Rating: typeof import('./src/components/Controls/Rating.vue')['default']
|
Rating: typeof import('./src/components/Controls/Rating.vue')['default']
|
||||||
|
RelatedCourses: typeof import('./src/components/RelatedCourses.vue')['default']
|
||||||
ReviewModal: typeof import('./src/components/Modals/ReviewModal.vue')['default']
|
ReviewModal: typeof import('./src/components/Modals/ReviewModal.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Button, createResource, Tooltip, toast } from 'frappe-ui'
|
import { Button, createResource, Tooltip, toast } from 'frappe-ui'
|
||||||
import { getCurrentInstance, inject, ref } from 'vue'
|
import { getCurrentInstance, inject, ref, watch } from 'vue'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
|
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
|
||||||
import {
|
import {
|
||||||
@@ -197,13 +197,22 @@ const props = defineProps({
|
|||||||
const outline = createResource({
|
const outline = createResource({
|
||||||
url: 'lms.lms.utils.get_course_outline',
|
url: 'lms.lms.utils.get_course_outline',
|
||||||
cache: ['course_outline', props.courseName],
|
cache: ['course_outline', props.courseName],
|
||||||
params: {
|
makeParams() {
|
||||||
|
return {
|
||||||
course: props.courseName,
|
course: props.courseName,
|
||||||
progress: props.getProgress,
|
progress: props.getProgress,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.courseName,
|
||||||
|
() => {
|
||||||
|
outline.reload()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const deleteLesson = createResource({
|
const deleteLesson = createResource({
|
||||||
url: 'lms.lms.api.delete_lesson',
|
url: 'lms.lms.api.delete_lesson',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Star } from 'lucide-vue-next'
|
import { Star } from 'lucide-vue-next'
|
||||||
import { createResource, Button } from 'frappe-ui'
|
import { createResource, Button } from 'frappe-ui'
|
||||||
import { computed, ref, inject } from 'vue'
|
import { watch, ref, inject } from 'vue'
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import ReviewModal from '@/components/Modals/ReviewModal.vue'
|
import ReviewModal from '@/components/Modals/ReviewModal.vue'
|
||||||
|
|
||||||
@@ -101,12 +101,21 @@ const hasReviewed = createResource({
|
|||||||
const reviews = createResource({
|
const reviews = createResource({
|
||||||
url: 'lms.lms.utils.get_reviews',
|
url: 'lms.lms.utils.get_reviews',
|
||||||
cache: ['course_reviews', props.courseName],
|
cache: ['course_reviews', props.courseName],
|
||||||
params: {
|
makeParams() {
|
||||||
|
return {
|
||||||
course: props.courseName,
|
course: props.courseName,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.courseName,
|
||||||
|
() => {
|
||||||
|
reviews.reload()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const showReviewModal = ref(false)
|
const showReviewModal = ref(false)
|
||||||
|
|
||||||
function openReviewModal() {
|
function openReviewModal() {
|
||||||
|
|||||||
@@ -4,15 +4,13 @@
|
|||||||
<div class="text-2xl font-semibold text-ink-gray-9">
|
<div class="text-2xl font-semibold text-ink-gray-9">
|
||||||
{{ __('Related Courses') }}
|
{{ __('Related Courses') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-ink-gray-7">
|
|
||||||
{{ relatedCourses.data.length }} {{ __('courses') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4"
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-for="course in relatedCourses.data"
|
v-for="course in relatedCourses.data"
|
||||||
|
:key="course.name"
|
||||||
:to="{ name: 'CourseDetail', params: { courseName: course.name } }"
|
:to="{ name: 'CourseDetail', params: { courseName: course.name } }"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
>
|
>
|
||||||
@@ -24,11 +22,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { createResource } from 'frappe-ui'
|
import { createResource } from 'frappe-ui'
|
||||||
import CourseCard from '@/components/CourseCard.vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { watch } from 'vue'
|
import { watch } from 'vue'
|
||||||
|
import CourseCard from '@/components/CourseCard.vue'
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
courseName: {
|
courseName: {
|
||||||
@@ -40,22 +35,18 @@ const props = defineProps({
|
|||||||
const relatedCourses = createResource({
|
const relatedCourses = createResource({
|
||||||
url: 'lms.lms.utils.get_related_courses',
|
url: 'lms.lms.utils.get_related_courses',
|
||||||
cache: ['related_courses', props.courseName],
|
cache: ['related_courses', props.courseName],
|
||||||
params: {
|
makeParams() {
|
||||||
|
return {
|
||||||
course: props.courseName,
|
course: props.courseName,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.params.courseName,
|
() => props.courseName,
|
||||||
(newCourseName, oldCourseName) => {
|
() => {
|
||||||
if (newCourseName && newCourseName !== oldCourseName) {
|
|
||||||
relatedCourses.update({
|
|
||||||
cache: ['related_courses', newCourseName],
|
|
||||||
params: { course: newCourseName },
|
|
||||||
})
|
|
||||||
relatedCourses.reload()
|
relatedCourses.reload()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
v-for="(quiz, index) in quizzes"
|
v-for="(quiz, index) in quizzes"
|
||||||
:key="index"
|
:key="index"
|
||||||
:style="getQuizMarkerStyle(quiz.time)"
|
:style="getQuizMarkerStyle(quiz.time)"
|
||||||
class="absolute top-0 h-full w-0.5 bg-red-500"
|
class="absolute top-0 h-full w-2 bg-surface-amber-3"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -303,7 +303,7 @@ const toggleFullscreen = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getQuizMarkerStyle = (time) => {
|
const getQuizMarkerStyle = (time) => {
|
||||||
const percentage = ((time - 5) / Math.ceil(duration.value)) * 100
|
const percentage = ((time - 7) / Math.ceil(duration.value)) * 100
|
||||||
return {
|
return {
|
||||||
left: `${percentage}%`,
|
left: `${percentage}%`,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,12 +83,12 @@
|
|||||||
:avg_rating="course.data.rating"
|
:avg_rating="course.data.rating"
|
||||||
:membership="course.data.membership"
|
:membership="course.data.membership"
|
||||||
/>
|
/>
|
||||||
<RelatedCourses :courseName="course.data.name" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden md:block">
|
<div class="hidden md:block">
|
||||||
<CourseCardOverlay :course="course" />
|
<CourseCardOverlay :course="course" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<RelatedCourses :courseName="course.data.name" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -109,10 +109,8 @@ import CourseReviews from '@/components/CourseReviews.vue'
|
|||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import CourseInstructors from '@/components/CourseInstructors.vue'
|
import CourseInstructors from '@/components/CourseInstructors.vue'
|
||||||
import RelatedCourses from '@/components/RelatedCourses.vue'
|
import RelatedCourses from '@/components/RelatedCourses.vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const { brand } = sessionStore()
|
const { brand } = sessionStore()
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
courseName: {
|
courseName: {
|
||||||
@@ -124,23 +122,19 @@ const props = defineProps({
|
|||||||
const course = createResource({
|
const course = createResource({
|
||||||
url: 'lms.lms.utils.get_course_details',
|
url: 'lms.lms.utils.get_course_details',
|
||||||
cache: ['course', props.courseName],
|
cache: ['course', props.courseName],
|
||||||
params: {
|
makeParams() {
|
||||||
|
return {
|
||||||
course: props.courseName,
|
course: props.courseName,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.params.courseName,
|
() => props.courseName,
|
||||||
(newCourseName, oldCourseName) => {
|
() => {
|
||||||
if (newCourseName && newCourseName !== oldCourseName) {
|
|
||||||
course.update({
|
|
||||||
cache: ['course', newCourseName],
|
|
||||||
params: { course: newCourseName },
|
|
||||||
})
|
|
||||||
course.reload()
|
course.reload()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
|
|||||||
@@ -199,6 +199,21 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<MultiSelect
|
||||||
|
v-model="related_courses"
|
||||||
|
doctype="LMS Course"
|
||||||
|
:label="__('Related Courses')"
|
||||||
|
:filters="{ name: ['!=', courseResource.data?.name] }"
|
||||||
|
:onCreate="
|
||||||
|
(close) => {
|
||||||
|
router.push({
|
||||||
|
name: 'CourseForm',
|
||||||
|
params: { courseName: 'new' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-10 pb-5 space-y-5 border-b">
|
<div class="px-10 pb-5 space-y-5 border-b">
|
||||||
@@ -319,6 +334,7 @@ const newTag = ref('')
|
|||||||
const { brand } = sessionStore()
|
const { brand } = sessionStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const instructors = ref([])
|
const instructors = ref([])
|
||||||
|
const related_courses = ref([])
|
||||||
const app = getCurrentInstance()
|
const app = getCurrentInstance()
|
||||||
const { updateOnboardingStep } = useOnboarding('learning')
|
const { updateOnboardingStep } = useOnboarding('learning')
|
||||||
const { $dialog } = app.appContext.config.globalProperties
|
const { $dialog } = app.appContext.config.globalProperties
|
||||||
@@ -400,6 +416,9 @@ const courseCreationResource = createResource({
|
|||||||
instructors: instructors.value.map((instructor) => ({
|
instructors: instructors.value.map((instructor) => ({
|
||||||
instructor: instructor,
|
instructor: instructor,
|
||||||
})),
|
})),
|
||||||
|
related_courses: related_courses.value.map((course) => ({
|
||||||
|
course: course,
|
||||||
|
})),
|
||||||
...values,
|
...values,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -418,6 +437,9 @@ const courseEditResource = createResource({
|
|||||||
instructors: instructors.value.map((instructor) => ({
|
instructors: instructors.value.map((instructor) => ({
|
||||||
instructor: instructor,
|
instructor: instructor,
|
||||||
})),
|
})),
|
||||||
|
related_courses: related_courses.value.map((course) => ({
|
||||||
|
course: course,
|
||||||
|
})),
|
||||||
...course,
|
...course,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -440,6 +462,11 @@ const courseResource = createResource({
|
|||||||
data.instructors.forEach((instructor) => {
|
data.instructors.forEach((instructor) => {
|
||||||
instructors.value.push(instructor.instructor)
|
instructors.value.push(instructor.instructor)
|
||||||
})
|
})
|
||||||
|
} else if (key == 'related_courses') {
|
||||||
|
related_courses.value = []
|
||||||
|
data.related_courses.forEach((course) => {
|
||||||
|
related_courses.value.push(course.course)
|
||||||
|
})
|
||||||
} else if (Object.hasOwn(course, key)) course[key] = data[key]
|
} else if (Object.hasOwn(course, key)) course[key] = data[key]
|
||||||
})
|
})
|
||||||
let checkboxes = [
|
let checkboxes = [
|
||||||
|
|||||||
Reference in New Issue
Block a user