feat(related-courses): add related courses frontend
This commit is contained in:
73
frontend/src/components/RelatedCourses.vue
Normal file
73
frontend/src/components/RelatedCourses.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="relatedCourses.data?.length" class="mt-10">
|
||||||
|
<div class="flex items-center justify-between mb-6">
|
||||||
|
<div class="text-2xl font-semibold text-ink-gray-9">
|
||||||
|
{{ __('Related Courses') }}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-ink-gray-7">
|
||||||
|
{{ relatedCourses.data.length }} {{ __('courses') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4">
|
||||||
|
<router-link
|
||||||
|
v-for="course in relatedCourses.data"
|
||||||
|
:to="{ name: 'CourseDetail', params: { courseName: course.name } }"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<CourseCard :course="course" />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="relatedCourses.loading" class="mt-10">
|
||||||
|
<div class="text-2xl font-semibold text-ink-gray-9 mb-6">
|
||||||
|
{{ __('Related Courses') }}
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<div
|
||||||
|
v-for="n in 3"
|
||||||
|
:key="n"
|
||||||
|
class="animate-pulse bg-gray-200 rounded-md h-80"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { createResource } from 'frappe-ui'
|
||||||
|
import CourseCard from '@/components/CourseCard.vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { watch } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
courseName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const relatedCourses = createResource({
|
||||||
|
url: 'lms.lms.utils.get_related_courses',
|
||||||
|
cache: ['related_courses', props.courseName],
|
||||||
|
params: {
|
||||||
|
course: props.courseName,
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.courseName,
|
||||||
|
(newCourseName, oldCourseName) => {
|
||||||
|
if (newCourseName && newCourseName !== oldCourseName) {
|
||||||
|
|
||||||
|
relatedCourses.update({
|
||||||
|
cache: ['related_courses', newCourseName],
|
||||||
|
params: { course: newCourseName }
|
||||||
|
})
|
||||||
|
relatedCourses.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
</script>
|
||||||
@@ -83,6 +83,7 @@
|
|||||||
: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" />
|
||||||
@@ -99,7 +100,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
usePageMeta,
|
usePageMeta,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { computed } from 'vue'
|
import { computed, watch } from 'vue'
|
||||||
import { Users, Star } from 'lucide-vue-next'
|
import { Users, Star } from 'lucide-vue-next'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import CourseCardOverlay from '@/components/CourseCardOverlay.vue'
|
import CourseCardOverlay from '@/components/CourseCardOverlay.vue'
|
||||||
@@ -107,8 +108,11 @@ import CourseOutline from '@/components/CourseOutline.vue'
|
|||||||
import CourseReviews from '@/components/CourseReviews.vue'
|
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 { useRoute } from 'vue-router'
|
||||||
|
|
||||||
const { brand } = sessionStore()
|
const { brand } = sessionStore()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
courseName: {
|
courseName: {
|
||||||
@@ -126,6 +130,19 @@ const course = createResource({
|
|||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.courseName,
|
||||||
|
(newCourseName, oldCourseName) => {
|
||||||
|
if (newCourseName && newCourseName !== oldCourseName) {
|
||||||
|
course.update({
|
||||||
|
cache: ['course', newCourseName],
|
||||||
|
params: { course: newCourseName }
|
||||||
|
})
|
||||||
|
course.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
let items = [{ label: 'Courses', route: { name: 'Courses' } }]
|
let items = [{ label: 'Courses', route: { name: 'Courses' } }]
|
||||||
items.push({
|
items.push({
|
||||||
|
|||||||
Reference in New Issue
Block a user