diff --git a/frontend/src/components/RelatedCourses.vue b/frontend/src/components/RelatedCourses.vue
new file mode 100644
index 00000000..8a359294
--- /dev/null
+++ b/frontend/src/components/RelatedCourses.vue
@@ -0,0 +1,61 @@
+
+
+
+
+ {{ __('Related Courses') }}
+
+
+ {{ relatedCourses.data.length }} {{ __('courses') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/CourseDetail.vue b/frontend/src/pages/CourseDetail.vue
index b18eaefc..da16d37e 100644
--- a/frontend/src/pages/CourseDetail.vue
+++ b/frontend/src/pages/CourseDetail.vue
@@ -83,6 +83,7 @@
:avg_rating="course.data.rating"
:membership="course.data.membership"
/>
+
@@ -99,7 +100,7 @@ import {
Tooltip,
usePageMeta,
} from 'frappe-ui'
-import { computed } from 'vue'
+import { computed, watch } from 'vue'
import { Users, Star } from 'lucide-vue-next'
import { sessionStore } from '@/stores/session'
import CourseCardOverlay from '@/components/CourseCardOverlay.vue'
@@ -107,8 +108,11 @@ import CourseOutline from '@/components/CourseOutline.vue'
import CourseReviews from '@/components/CourseReviews.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import CourseInstructors from '@/components/CourseInstructors.vue'
+import RelatedCourses from '@/components/RelatedCourses.vue'
+import { useRoute } from 'vue-router'
const { brand } = sessionStore()
+const route = useRoute()
const props = defineProps({
courseName: {
@@ -126,6 +130,19 @@ const course = createResource({
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(() => {
let items = [{ label: 'Courses', route: { name: 'Courses' } }]
items.push({
diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js
index e69c7747..19c17a9b 100644
--- a/frontend/src/utils/index.js
+++ b/frontend/src/utils/index.js
@@ -197,6 +197,14 @@ export function getEditorTools() {
window.innerWidth < 640 ? '15rem' : '30rem'
};" frameborder="0" allowfullscreen>`,
},
+ 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: ``,
+ },
codepen: true,
aparat: {
regex: /(?:http[s]?:\/\/)?(?:www.)?aparat\.com\/v\/([^\/\?\&]+)\/?/,
diff --git a/lms/lms/utils.py b/lms/lms/utils.py
index 8e5f290e..dffb8d52 100644
--- a/lms/lms/utils.py
+++ b/lms/lms/utils.py
@@ -182,6 +182,7 @@ def get_lesson_icon(body, content):
"youtube",
"vimeo",
"cloudflareStream",
+ "bunnyStream",
]:
return "icon-youtube"
@@ -2171,5 +2172,17 @@ def get_palette(full_name):
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():
frappe.db.set_single_value("LMS Settings", "persona_captured", 1)
diff --git a/lms/locale/main.pot b/lms/locale/main.pot
index 81276ea4..7bbc869f 100644
--- a/lms/locale/main.pot
+++ b/lms/locale/main.pot
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Frappe LMS VERSION\n"
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
-"POT-Creation-Date: 2025-06-06 16:04+0000\n"
-"PO-Revision-Date: 2025-06-06 16:04+0000\n"
+"POT-Creation-Date: 2025-06-13 16:04+0000\n"
+"PO-Revision-Date: 2025-06-13 16:04+0000\n"
"Last-Translator: jannat@frappe.io\n"
"Language-Team: jannat@frappe.io\n"
"MIME-Version: 1.0\n"
@@ -410,7 +410,7 @@ msgstr ""
msgid "Archived"
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."
msgstr ""
@@ -760,12 +760,12 @@ msgstr ""
msgid "CGPA/4"
msgstr ""
-#: frontend/src/components/UpcomingEvaluations.vue:60
-#: frontend/src/components/UpcomingEvaluations.vue:174
+#: frontend/src/components/UpcomingEvaluations.vue:57
+#: frontend/src/components/UpcomingEvaluations.vue:177
msgid "Cancel"
msgstr ""
-#: frontend/src/components/UpcomingEvaluations.vue:168
+#: frontend/src/components/UpcomingEvaluations.vue:171
msgid "Cancel this evaluation?"
msgstr ""
@@ -1274,7 +1274,7 @@ msgid "Continue Learning"
msgstr ""
#. 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
msgid "Contract"
msgstr ""
@@ -2309,7 +2309,7 @@ msgid "Free"
msgstr ""
#. 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
msgid "Freelance"
msgstr ""
@@ -2353,7 +2353,7 @@ msgid "Full Name"
msgstr ""
#. 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
msgid "Full Time"
msgstr ""
@@ -2853,7 +2853,7 @@ msgstr ""
#. 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/Jobs.vue:184
+#: frontend/src/pages/Jobs.vue:185
#: lms/lms/doctype/lms_settings/lms_settings.json
msgid "Jobs"
msgstr ""
@@ -2863,7 +2863,7 @@ msgstr ""
msgid "Join"
msgstr ""
-#: frontend/src/components/UpcomingEvaluations.vue:93
+#: frontend/src/components/UpcomingEvaluations.vue:90
msgid "Join Call"
msgstr ""
@@ -3350,10 +3350,6 @@ msgstr ""
msgid "Mark all as read"
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 Result'
#: frontend/src/components/Modals/Question.vue:40
@@ -3888,7 +3884,7 @@ msgstr ""
msgid "Not Saved"
msgstr ""
-#: frontend/src/pages/Notifications.vue:54
+#: frontend/src/pages/Notifications.vue:53
msgid "Nothing to see here."
msgstr ""
@@ -4075,7 +4071,7 @@ msgid "Pan Number"
msgstr ""
#. 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
msgid "Part Time"
msgstr ""
@@ -4312,7 +4308,7 @@ msgstr ""
msgid "Please prepare well and be on time for the evaluations."
msgstr ""
-#: frontend/src/components/UpcomingEvaluations.vue:101
+#: frontend/src/components/UpcomingEvaluations.vue:98
msgid "Please schedule an evaluation to get certified."
msgstr ""
@@ -4927,7 +4923,7 @@ msgid "Schedule"
msgstr ""
#: frontend/src/components/Modals/EvaluationModal.vue:5
-#: frontend/src/components/UpcomingEvaluations.vue:14
+#: frontend/src/components/UpcomingEvaluations.vue:11
msgid "Schedule Evaluation"
msgstr ""
@@ -6073,7 +6069,7 @@ msgstr ""
msgid "Video Embed Link"
msgstr ""
-#: frontend/src/pages/Notifications.vue:38
+#: frontend/src/pages/Notifications.vue:39
msgid "View"
msgstr ""
@@ -6208,7 +6204,7 @@ msgstr ""
msgid "Write your answer here"
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}."
msgstr ""
@@ -6261,11 +6257,11 @@ msgstr ""
msgid "You cannot change the roles in read-only mode."
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}."
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."
msgstr ""
@@ -6387,7 +6383,7 @@ msgstr ""
msgid "Your evaluation for the course {0} has been scheduled on {1} at {2} {3}."
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"
msgstr ""