Merge branch 'develop' of https://github.com/frappe/lms into issues-115
This commit is contained in:
61
frontend/src/components/RelatedCourses.vue
Normal file
61
frontend/src/components/RelatedCourses.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<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>
|
||||||
|
</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({
|
||||||
|
|||||||
@@ -197,6 +197,14 @@ export function getEditorTools() {
|
|||||||
window.innerWidth < 640 ? '15rem' : '30rem'
|
window.innerWidth < 640 ? '15rem' : '30rem'
|
||||||
};" frameborder="0" allowfullscreen></iframe>`,
|
};" frameborder="0" allowfullscreen></iframe>`,
|
||||||
},
|
},
|
||||||
|
bunnyStream: {
|
||||||
|
regex: /https:\/\/(?:iframe\.mediadelivery\.net|video\.bunnycdn\.com)\/play\/([a-zA-Z0-9]+\/[a-zA-Z0-9-]+)/,
|
||||||
|
embedUrl:
|
||||||
|
'https://iframe.mediadelivery.net/embed/<%= remote_id %>',
|
||||||
|
html: `<iframe style="width:100%; height: ${
|
||||||
|
window.innerWidth < 640 ? '15rem' : '30rem'
|
||||||
|
};" frameborder="0" allowfullscreen></iframe>`,
|
||||||
|
},
|
||||||
codepen: true,
|
codepen: true,
|
||||||
aparat: {
|
aparat: {
|
||||||
regex: /(?:http[s]?:\/\/)?(?:www.)?aparat\.com\/v\/([^\/\?\&]+)\/?/,
|
regex: /(?:http[s]?:\/\/)?(?:www.)?aparat\.com\/v\/([^\/\?\&]+)\/?/,
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ def get_lesson_icon(body, content):
|
|||||||
"youtube",
|
"youtube",
|
||||||
"vimeo",
|
"vimeo",
|
||||||
"cloudflareStream",
|
"cloudflareStream",
|
||||||
|
"bunnyStream",
|
||||||
]:
|
]:
|
||||||
return "icon-youtube"
|
return "icon-youtube"
|
||||||
|
|
||||||
@@ -2171,5 +2172,17 @@ def get_palette(full_name):
|
|||||||
return palette[idx % 8]
|
return palette[idx % 8]
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist(allow_guest=True)
|
||||||
|
def get_related_courses(course):
|
||||||
|
related_course_details = []
|
||||||
|
related_courses = frappe.get_all(
|
||||||
|
"Related Courses", {"parent": course}, order_by="idx", pluck="course"
|
||||||
|
)
|
||||||
|
|
||||||
|
for related_course in related_courses:
|
||||||
|
related_course_details.append(get_course_details(related_course))
|
||||||
|
return related_course_details
|
||||||
|
|
||||||
|
|
||||||
def persona_captured():
|
def persona_captured():
|
||||||
frappe.db.set_single_value("LMS Settings", "persona_captured", 1)
|
frappe.db.set_single_value("LMS Settings", "persona_captured", 1)
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Frappe LMS VERSION\n"
|
"Project-Id-Version: Frappe LMS VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||||
"POT-Creation-Date: 2025-06-06 16:04+0000\n"
|
"POT-Creation-Date: 2025-06-13 16:04+0000\n"
|
||||||
"PO-Revision-Date: 2025-06-06 16:04+0000\n"
|
"PO-Revision-Date: 2025-06-13 16:04+0000\n"
|
||||||
"Last-Translator: jannat@frappe.io\n"
|
"Last-Translator: jannat@frappe.io\n"
|
||||||
"Language-Team: jannat@frappe.io\n"
|
"Language-Team: jannat@frappe.io\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@@ -410,7 +410,7 @@ msgstr ""
|
|||||||
msgid "Archived"
|
msgid "Archived"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:169
|
#: frontend/src/components/UpcomingEvaluations.vue:172
|
||||||
msgid "Are you sure you want to cancel this evaluation? This action cannot be undone."
|
msgid "Are you sure you want to cancel this evaluation? This action cannot be undone."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -760,12 +760,12 @@ msgstr ""
|
|||||||
msgid "CGPA/4"
|
msgid "CGPA/4"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:60
|
#: frontend/src/components/UpcomingEvaluations.vue:57
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:174
|
#: frontend/src/components/UpcomingEvaluations.vue:177
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:168
|
#: frontend/src/components/UpcomingEvaluations.vue:171
|
||||||
msgid "Cancel this evaluation?"
|
msgid "Cancel this evaluation?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1274,7 +1274,7 @@ msgid "Continue Learning"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/Jobs.vue:177
|
#: frontend/src/pages/Jobs.vue:178
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Contract"
|
msgid "Contract"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2309,7 +2309,7 @@ msgid "Free"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/Jobs.vue:178
|
#: frontend/src/pages/Jobs.vue:179
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Freelance"
|
msgid "Freelance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2353,7 +2353,7 @@ msgid "Full Name"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/Jobs.vue:175
|
#: frontend/src/pages/Jobs.vue:176
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Full Time"
|
msgid "Full Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2853,7 +2853,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the jobs (Check) field in DocType 'LMS Settings'
|
#. Label of the jobs (Check) field in DocType 'LMS Settings'
|
||||||
#: frontend/src/pages/JobDetail.vue:10 frontend/src/pages/Jobs.vue:8
|
#: frontend/src/pages/JobDetail.vue:10 frontend/src/pages/Jobs.vue:8
|
||||||
#: frontend/src/pages/Jobs.vue:184
|
#: frontend/src/pages/Jobs.vue:185
|
||||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||||
msgid "Jobs"
|
msgid "Jobs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2863,7 +2863,7 @@ msgstr ""
|
|||||||
msgid "Join"
|
msgid "Join"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:93
|
#: frontend/src/components/UpcomingEvaluations.vue:90
|
||||||
msgid "Join Call"
|
msgid "Join Call"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3350,10 +3350,6 @@ msgstr ""
|
|||||||
msgid "Mark all as read"
|
msgid "Mark all as read"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Notifications.vue:40
|
|
||||||
msgid "Mark as read"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. Label of the marks (Int) field in DocType 'LMS Quiz Question'
|
#. Label of the marks (Int) field in DocType 'LMS Quiz Question'
|
||||||
#. Label of the marks (Int) field in DocType 'LMS Quiz Result'
|
#. Label of the marks (Int) field in DocType 'LMS Quiz Result'
|
||||||
#: frontend/src/components/Modals/Question.vue:40
|
#: frontend/src/components/Modals/Question.vue:40
|
||||||
@@ -3888,7 +3884,7 @@ msgstr ""
|
|||||||
msgid "Not Saved"
|
msgid "Not Saved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Notifications.vue:54
|
#: frontend/src/pages/Notifications.vue:53
|
||||||
msgid "Nothing to see here."
|
msgid "Nothing to see here."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4075,7 +4071,7 @@ msgid "Pan Number"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/Jobs.vue:176
|
#: frontend/src/pages/Jobs.vue:177
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Part Time"
|
msgid "Part Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -4312,7 +4308,7 @@ msgstr ""
|
|||||||
msgid "Please prepare well and be on time for the evaluations."
|
msgid "Please prepare well and be on time for the evaluations."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:101
|
#: frontend/src/components/UpcomingEvaluations.vue:98
|
||||||
msgid "Please schedule an evaluation to get certified."
|
msgid "Please schedule an evaluation to get certified."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4927,7 +4923,7 @@ msgid "Schedule"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/EvaluationModal.vue:5
|
#: frontend/src/components/Modals/EvaluationModal.vue:5
|
||||||
#: frontend/src/components/UpcomingEvaluations.vue:14
|
#: frontend/src/components/UpcomingEvaluations.vue:11
|
||||||
msgid "Schedule Evaluation"
|
msgid "Schedule Evaluation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -6073,7 +6069,7 @@ msgstr ""
|
|||||||
msgid "Video Embed Link"
|
msgid "Video Embed Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Notifications.vue:38
|
#: frontend/src/pages/Notifications.vue:39
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -6208,7 +6204,7 @@ msgstr ""
|
|||||||
msgid "Write your answer here"
|
msgid "Write your answer here"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:96
|
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:95
|
||||||
msgid "You already have an evaluation on {0} at {1} for the course {2}."
|
msgid "You already have an evaluation on {0} at {1} for the course {2}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -6261,11 +6257,11 @@ msgstr ""
|
|||||||
msgid "You cannot change the roles in read-only mode."
|
msgid "You cannot change the roles in read-only mode."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:116
|
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:115
|
||||||
msgid "You cannot schedule evaluations after {0}."
|
msgid "You cannot schedule evaluations after {0}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:105
|
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:104
|
||||||
msgid "You cannot schedule evaluations for past slots."
|
msgid "You cannot schedule evaluations for past slots."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -6387,7 +6383,7 @@ msgstr ""
|
|||||||
msgid "Your evaluation for the course {0} has been scheduled on {1} at {2} {3}."
|
msgid "Your evaluation for the course {0} has been scheduled on {1} at {2} {3}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:126
|
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:125
|
||||||
msgid "Your evaluation slot has been booked"
|
msgid "Your evaluation slot has been booked"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user