feat: rating component
This commit is contained in:
@@ -2,16 +2,28 @@
|
||||
<div class="shadow rounded-md" style="width: 300px;">
|
||||
<iframe v-if="course.data.video_link" :src="video_link" class="rounded-t-md" />
|
||||
<div class="p-5">
|
||||
<Button v-if="course.data.membership" variant="solid" class="w-full mb-3">
|
||||
<span>
|
||||
{{ __("Continue Learning") }}
|
||||
</span>
|
||||
</Button>
|
||||
<Button v-else variant="solid" class="w-full mb-3" >
|
||||
<router-link v-if="course.data.membership && course.data.current_lesson"
|
||||
:to="{name: 'Lesson', params: {
|
||||
courseName: course.name,
|
||||
chapterNumber: course.data.current_lesson.split('.')[0],
|
||||
lessonNumber: course.data.current_lesson.split('.')[1]
|
||||
}}">
|
||||
<Button variant="solid" class="w-full mb-3">
|
||||
<span>
|
||||
{{ __("Continue Learning") }}
|
||||
</span>
|
||||
</Button>
|
||||
</router-link>
|
||||
<Button v-else @click="enrollStudent()" variant="solid" class="w-full mb-3">
|
||||
<span>
|
||||
{{ __("Start Learning") }}
|
||||
</span>
|
||||
</Button>
|
||||
<Button v-if="user?.data?.is_moderator" variant="subtle" class="w-full mb-3">
|
||||
<span>
|
||||
{{ __("Edit") }}
|
||||
</span>
|
||||
</Button>
|
||||
<div class="flex items-center mb-3">
|
||||
<Users class="h-4 w-4 text-gray-700"/>
|
||||
<span class="ml-1">
|
||||
@@ -35,8 +47,14 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { BookOpen, Users, Star } from 'lucide-vue-next'
|
||||
import { computed } from 'vue'
|
||||
import { Button } from "frappe-ui"
|
||||
import { computed, inject } from 'vue'
|
||||
import { Button, createResource } from "frappe-ui"
|
||||
import { createToast } from "@/utils/"
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
|
||||
const user = inject("$user");
|
||||
|
||||
const props = defineProps({
|
||||
course: {
|
||||
type: Object,
|
||||
@@ -50,4 +68,34 @@ const video_link = computed(() => {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
function enrollStudent() {
|
||||
if (!user.data) {
|
||||
createToast({
|
||||
title: "Please Login",
|
||||
icon: 'alert-circle',
|
||||
iconClasses: 'text-yellow-600 bg-yellow-100',
|
||||
})
|
||||
setTimeout(() => {
|
||||
window.location.href = `/login?redirect-to=${window.location.pathname}`
|
||||
}, 3000)
|
||||
} else {
|
||||
const enrollStudentResource = createResource({
|
||||
url: "lms.lms.doctype.lms_enrollment.lms_enrollment.create_membership"
|
||||
})
|
||||
console.log(props.course)
|
||||
enrollStudentResource.submit({
|
||||
course: props.course.data.name
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: "Enrolled Successfully",
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600 bg-green-100',
|
||||
})
|
||||
setTimeout(() => {
|
||||
router.push({ name: 'Lesson', params: { courseName: props.course.data.name, chapterNumber: 1, lessonNumber: 1 } })
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="text-base">
|
||||
<div class="mt-4">
|
||||
<Disclosure v-slot="{ open }" v-for="(chapter, index) in outline.data" :key="chapter.name">
|
||||
<Disclosure v-slot="{ open }" v-for="(chapter, index) in outline.data" :key="chapter.name" :defaultOpen="index == 0">
|
||||
<DisclosureButton class="flex w-full px-2 pt-2 pb-3">
|
||||
<ChevronRight
|
||||
:class="{'rotate-90 transform duration-200' : open, 'duration-200' : !open, 'open': index == 1}"
|
||||
@@ -11,7 +11,7 @@
|
||||
{{ chapter.title }}
|
||||
</div>
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel class="px-10 pb-4" :static="index == 0">
|
||||
<DisclosurePanel class="px-10 pb-4">
|
||||
<div v-for="lesson in chapter.lessons" :key="lesson.name">
|
||||
<div class="flex items-center text-base mb-3">
|
||||
<MonitorPlay v-if="lesson.icon === 'icon-youtube'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="mb-2">
|
||||
{{ reviews.data.length }} {{ __("reviews") }}
|
||||
</div>
|
||||
<Button>
|
||||
<Button @click="openReviewModal()">
|
||||
<span>
|
||||
{{ __("Write a review") }}
|
||||
</span>
|
||||
@@ -53,11 +53,8 @@
|
||||
<Star v-for="index in 5" class="h-5 w-5 text-gray-100 bg-gray-200 rounded-sm mr-2" :class="(index <= Math.ceil(review.rating)) ? 'fill-orange-500' : 'fill-gray-600'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="mt-4 leading-5">
|
||||
{{ review.review }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,12 +62,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ showReviewModal }}
|
||||
<ReviewModal v-model="showReviewModal"/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Star } from 'lucide-vue-next'
|
||||
import { createResource, Button } from "frappe-ui";
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import UserAvatar from '@/components/UserAvatar.vue';
|
||||
import ReviewModal from '@/components/ReviewModal.vue';
|
||||
|
||||
const props = defineProps({
|
||||
courseName: {
|
||||
@@ -111,4 +111,10 @@ const rating_percent = computed(() => {
|
||||
});
|
||||
return rating_percent;
|
||||
});
|
||||
const showReviewModal = ref(false)
|
||||
|
||||
function openReviewModal() {
|
||||
console.log("called")
|
||||
showReviewModal.value = true;
|
||||
}
|
||||
</script>
|
||||
34
frontend/src/components/Rating.vue
Normal file
34
frontend/src/components/Rating.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="flex text-center">
|
||||
<div v-for="index in 5">
|
||||
<Star class="h-5 w-5 fill-gray-400 text-gray-200 mr-1 cursor-pointer hover:fill-orange-500" @input="handleChange"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, useAttrs } from 'vue'
|
||||
import { Star } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const attrs = useAttrs()
|
||||
|
||||
let emitChange = (value: string) => {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
|
||||
let handleChange = (e: Event) => {
|
||||
emitChange((e.target as HTMLInputElement).value)
|
||||
}
|
||||
</script>
|
||||
26
frontend/src/components/ReviewModal.vue
Normal file
26
frontend/src/components/ReviewModal.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options='{
|
||||
title: __("Write a Review"),
|
||||
size: "xl",
|
||||
actions: [
|
||||
{
|
||||
label: "Submit",
|
||||
variant: "solid",
|
||||
onClick: ({ close }) => submitReview(close)
|
||||
}
|
||||
]
|
||||
}'>
|
||||
<template #body-content>
|
||||
<Textarea type="text" size="md" rows="5" />
|
||||
<Rating />
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Dialog, Textarea } from 'frappe-ui'
|
||||
import { defineModel } from "vue"
|
||||
import Rating from '@/components/Rating.vue';
|
||||
|
||||
const show = defineModel()
|
||||
console.log(show)
|
||||
</script>
|
||||
Reference in New Issue
Block a user