diff --git a/apps/dashboard/src/lib/components/Courses/api.js b/apps/dashboard/src/lib/components/Courses/api.js index 83350b1c..5804ec00 100644 --- a/apps/dashboard/src/lib/components/Courses/api.js +++ b/apps/dashboard/src/lib/components/Courses/api.js @@ -28,3 +28,20 @@ export async function fetchCourses(profileId, orgId) { return { allCourses }; } + +export async function fetchExploreCourses(profileId, orgId) { + if (!orgId || !profileId) return; + + const { data: allCourses } = await supabase.rpc('get_explore_courses', { + org_id_arg: orgId, + profile_id_arg: profileId + }); + + if (!Array.isArray(allCourses)) { + return { + allCourses: [] + }; + } + + return { allCourses }; +} diff --git a/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte b/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte index b39c4b49..92bc7347 100644 --- a/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte +++ b/apps/dashboard/src/lib/components/Courses/components/Card/index.svelte @@ -32,6 +32,7 @@ export let currency = 'NGN'; export let isOnLandingPage = false; export let isLMS = false; + export let isExplore = false; export let progressRate = 45; export let type: COURSE_TYPE; @@ -60,7 +61,7 @@ } function getCourseUrl() { - return isOnLandingPage + return isOnLandingPage || isExplore ? `/course/${slug}` : `/courses/${id}${isLMS ? '/lessons?next=true' : ''}`; } @@ -159,11 +160,11 @@
-

+

{totalLessons} {$t('courses.course_card.lessons_number')}

@@ -175,15 +176,17 @@ : formatter.format(cost)} {:else if isLMS} -
-
-
+ {#if !isExplore} +
+
+
+
+

{progressRate}%

-

{progressRate}%

-
+ {/if} {:else} {#if isPublished} @@ -198,13 +201,15 @@ {#if isLMS} {:else if !isOnLandingPage}
-

+

{totalStudents} {$t('courses.course_card.students')}

diff --git a/apps/dashboard/src/lib/components/Courses/index.svelte b/apps/dashboard/src/lib/components/Courses/index.svelte index 1daec6dd..ce2ba6cd 100644 --- a/apps/dashboard/src/lib/components/Courses/index.svelte +++ b/apps/dashboard/src/lib/components/Courses/index.svelte @@ -19,6 +19,7 @@ export let courses: Course[] = []; export let emptyTitle = $t('courses.course_card.empty_title'); export let emptyDescription = $t('courses.course_card.empty_description'); + export let isExplore = false; function calcProgressRate(progressRate?: number, totalLessons?: number): number { if (!progressRate || !totalLessons) { @@ -83,6 +84,7 @@ {#key courseData.id} {/key} diff --git a/apps/dashboard/src/lib/components/LMS/SideBar.svelte b/apps/dashboard/src/lib/components/LMS/SideBar.svelte index d65d236e..b7255b8c 100644 --- a/apps/dashboard/src/lib/components/LMS/SideBar.svelte +++ b/apps/dashboard/src/lib/components/LMS/SideBar.svelte @@ -2,6 +2,7 @@ import { page } from '$app/stores'; import HelpIcon from 'carbon-icons-svelte/lib/Help.svelte'; import LicenseDraft from 'carbon-icons-svelte/lib/LicenseDraft.svelte'; + import Explore from 'carbon-icons-svelte/lib/Explore.svelte'; import Settings from 'carbon-icons-svelte/lib/Settings.svelte'; import HomeIcon from '$lib/components/Icons/HomeIcon.svelte'; import CourseIcon from '$lib/components/Icons/CourseIcon.svelte'; @@ -59,6 +60,11 @@ show() { return $currentOrg?.customization?.dashboard?.community; } + }, + { + name: $t('lms_navigation.explore'), + icon: Explore, + link: '/lms/explore' } ].filter((link) => (link.show ? link.show() : true)); diff --git a/apps/dashboard/src/lib/utils/translations/de.json b/apps/dashboard/src/lib/utils/translations/de.json index 48bd5745..5c4940d7 100644 --- a/apps/dashboard/src/lib/utils/translations/de.json +++ b/apps/dashboard/src/lib/utils/translations/de.json @@ -185,7 +185,8 @@ "exercise": "Übung", "community": "Gemeinschaft", "help": "Helfen", - "settings": "Einstellungen" + "settings": "Einstellungen", + "explore": "Erkunden" }, "upgrade": { "certificate": "Aktualisieren Sie Ihren Plan, um Zertifikate zu generieren", @@ -843,7 +844,9 @@ "share": "Aktie", "invite": "Einladen", "delete": "Löschen" - } + }, + "get_course": "Holen Sie sich Kurs", + "learn_more": "Erfahren Sie mehr" }, "course_filter": { "date_created": "Datum erstellt", @@ -1369,5 +1372,10 @@ "min_char": "Die Organisation muss 5 oder mehr Zeichen enthalten", "hyphen_rule": "Der Name der Organisation darf nicht mit einem Bindestrich beginnen oder enden" } + }, + "explore": { + "empty_heading": "Kein Kurs verfügbar", + "empty_description": "Derzeit sind keine veröffentlichten Kurse verfügbar", + "heading": "Entdecken Sie unsere Kurse" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/lib/utils/translations/en.json b/apps/dashboard/src/lib/utils/translations/en.json index b67bfe68..9eceb968 100644 --- a/apps/dashboard/src/lib/utils/translations/en.json +++ b/apps/dashboard/src/lib/utils/translations/en.json @@ -428,6 +428,7 @@ "published": "Published", "unpublished": "Unpublished", "continue_course": "Continue Course", + "learn_more": "Learn more", "list_view": { "title": "Title", "description": "Description", @@ -1101,6 +1102,7 @@ "my_learning": "My Learning", "exercise": "Exercise", "community": "Community", + "explore": "Explore", "help": "Help", "settings": "Settings" }, @@ -1125,6 +1127,12 @@ "lesson": "Lesson" }, + "explore": { + "heading": "Explore our courses", + "empty_heading": "No course available", + "empty_description": "There are currently no published course available" + }, + "snackbar": { "success": "Success", "success_update": "Updated successfully", diff --git a/apps/dashboard/src/lib/utils/translations/es.json b/apps/dashboard/src/lib/utils/translations/es.json index e72e2bbd..e36054fa 100644 --- a/apps/dashboard/src/lib/utils/translations/es.json +++ b/apps/dashboard/src/lib/utils/translations/es.json @@ -185,7 +185,8 @@ "exercise": "Ejercicio", "community": "Comunidad", "help": "Ayuda", - "settings": "Ajustes" + "settings": "Ajustes", + "explore": "Explorar" }, "upgrade": { "certificate": "Actualice su plan para generar certificados", @@ -843,7 +844,9 @@ "share": "Compartir", "invite": "Invitar", "delete": "Borrar" - } + }, + "get_course": "Obtener curso", + "learn_more": "Aprende más" }, "course_filter": { "date_created": "fecha de creacion", @@ -1369,5 +1372,10 @@ "min_char": "La organización debe contener 5 o más caracteres.", "hyphen_rule": "El nombre de la organización no puede comenzar ni terminar con un guión." } + }, + "explore": { + "empty_heading": "No hay curso disponible", + "empty_description": "Actualmente no hay ningún curso publicado disponible", + "heading": "Explora nuestros cursos" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/lib/utils/translations/fr.json b/apps/dashboard/src/lib/utils/translations/fr.json index a4d30b10..62e18def 100644 --- a/apps/dashboard/src/lib/utils/translations/fr.json +++ b/apps/dashboard/src/lib/utils/translations/fr.json @@ -185,7 +185,8 @@ "exercise": "Exercice", "community": "Communauté", "help": "Aide", - "settings": "Paramètres" + "settings": "Paramètres", + "explore": "Explorer" }, "upgrade": { "certificate": "Mettez à niveau votre plan pour générer des certificats", @@ -843,7 +844,9 @@ "share": "Partager", "invite": "Inviter", "delete": "Supprimer" - } + }, + "get_course": "Obtenir un cours", + "learn_more": "Apprendre encore plus" }, "course_filter": { "date_created": "date créée", @@ -1369,5 +1372,10 @@ "min_char": "L'organisation doit contenir 5 caractères ou plus", "hyphen_rule": "Le nom de l'organisation ne peut pas commencer ou se terminer par un trait d'union" } + }, + "explore": { + "empty_heading": "Aucun cours disponible", + "empty_description": "Il n'y a actuellement aucun cours publié disponible", + "heading": "Découvrez nos cours" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/lib/utils/translations/hi.json b/apps/dashboard/src/lib/utils/translations/hi.json index dff1756a..9c3547f7 100644 --- a/apps/dashboard/src/lib/utils/translations/hi.json +++ b/apps/dashboard/src/lib/utils/translations/hi.json @@ -185,7 +185,8 @@ "exercise": "व्यायाम", "community": "समुदाय", "help": "मदद", - "settings": "समायोजन" + "settings": "समायोजन", + "explore": "अन्वेषण करना" }, "upgrade": { "certificate": "प्रमाणपत्र बनाने की अपनी योजना को अपग्रेड करें", @@ -843,7 +844,9 @@ "share": "शेयर करना", "invite": "आमंत्रित करना", "delete": "मिटाना" - } + }, + "get_course": "पाठ्यक्रम प्राप्त करें", + "learn_more": "और अधिक जानें" }, "course_filter": { "date_created": "बनाया गया दिनांक", @@ -1369,5 +1372,10 @@ "min_char": "संगठन में 5 या अधिक वर्ण होने चाहिए", "hyphen_rule": "संगठन का नाम हाइफ़न से प्रारंभ या समाप्त नहीं हो सकता" } + }, + "explore": { + "empty_heading": "कोई पाठ्यक्रम उपलब्ध नहीं है", + "empty_description": "वर्तमान में कोई प्रकाशित पाठ्यक्रम उपलब्ध नहीं है", + "heading": "हमारे पाठ्यक्रमों का अन्वेषण करें" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/lib/utils/translations/pt.json b/apps/dashboard/src/lib/utils/translations/pt.json index 6eb5fb1a..ce2c2fbb 100644 --- a/apps/dashboard/src/lib/utils/translations/pt.json +++ b/apps/dashboard/src/lib/utils/translations/pt.json @@ -185,7 +185,8 @@ "exercise": "Exercício", "community": "Comunidade", "help": "Ajuda", - "settings": "Configurações" + "settings": "Configurações", + "explore": "Explorar" }, "upgrade": { "certificate": "Atualize seu plano para gerar certificados", @@ -843,7 +844,9 @@ "share": "Compartilhar", "invite": "Convidar", "delete": "Excluir" - } + }, + "get_course": "Obter curso", + "learn_more": "Saber mais" }, "course_filter": { "date_created": "Data Criada", @@ -1369,5 +1372,10 @@ "min_char": "A organização deve conter 5 ou mais caracteres", "hyphen_rule": "O nome da organização não pode começar ou terminar com um hífen" } + }, + "explore": { + "empty_heading": "Nenhum curso disponível", + "empty_description": "Atualmente não há nenhum curso publicado disponível", + "heading": "Explore nossos cursos" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/lib/utils/translations/ru.json b/apps/dashboard/src/lib/utils/translations/ru.json index ff0f6022..4bfb73a8 100644 --- a/apps/dashboard/src/lib/utils/translations/ru.json +++ b/apps/dashboard/src/lib/utils/translations/ru.json @@ -185,7 +185,8 @@ "exercise": "Упражнение", "community": "Сообщество", "help": "Помощь", - "settings": "Настройки" + "settings": "Настройки", + "explore": "Исследовать" }, "upgrade": { "certificate": "Обновите свой план для создания сертификатов", @@ -843,7 +844,9 @@ "share": "Делиться", "invite": "Приглашать", "delete": "Удалить" - } + }, + "get_course": "Получить курс", + "learn_more": "Узнать больше" }, "course_filter": { "date_created": "Дата создания", @@ -1369,5 +1372,10 @@ "min_char": "Организация должна содержать 5 или более символов.", "hyphen_rule": "Название организации не может начинаться или заканчиваться дефисом." } + }, + "explore": { + "empty_heading": "Нет доступных курсов", + "empty_description": "На данный момент нет доступных опубликованных курсов", + "heading": "Ознакомьтесь с нашими курсами" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/lib/utils/translations/vi.json b/apps/dashboard/src/lib/utils/translations/vi.json index a01997a0..be79aca8 100644 --- a/apps/dashboard/src/lib/utils/translations/vi.json +++ b/apps/dashboard/src/lib/utils/translations/vi.json @@ -185,7 +185,8 @@ "exercise": "Bài tập", "community": "Cộng đồng", "help": "Giúp đỡ", - "settings": "Cài đặt" + "settings": "Cài đặt", + "explore": "Khám phá" }, "upgrade": { "certificate": "Nâng cấp gói của bạn để tạo chứng chỉ", @@ -843,7 +844,9 @@ "share": "Chia sẻ", "invite": "Mời", "delete": "Xóa bỏ" - } + }, + "get_course": "Nhận khóa học", + "learn_more": "Tìm hiểu thêm" }, "course_filter": { "date_created": "Ngày tạo", @@ -1369,5 +1372,10 @@ "min_char": "Tổ chức phải chứa 5 ký tự trở lên", "hyphen_rule": "Tên tổ chức không thể bắt đầu hoặc kết thúc bằng dấu gạch nối" } + }, + "explore": { + "empty_heading": "Không có khóa học nào", + "empty_description": "Hiện tại chưa có khóa học nào được xuất bản", + "heading": "Khám phá các khóa học của chúng tôi" } -} +} \ No newline at end of file diff --git a/apps/dashboard/src/routes/lms/explore/+page.svelte b/apps/dashboard/src/routes/lms/explore/+page.svelte new file mode 100644 index 00000000..8535a798 --- /dev/null +++ b/apps/dashboard/src/routes/lms/explore/+page.svelte @@ -0,0 +1,127 @@ + + +
+
+

{$t('explore.heading')}

+
+
+ + + + {#if $courseMetaDeta.view === 'list'} + setViewPreference('grid')}> + + + {:else} + setViewPreference('list')}> + + + {/if} +
+
+ +
+
diff --git a/apps/dashboard/src/routes/org/[slug]/courses/+page.svelte b/apps/dashboard/src/routes/org/[slug]/courses/+page.svelte index c80f0c5e..84c49347 100644 --- a/apps/dashboard/src/routes/org/[slug]/courses/+page.svelte +++ b/apps/dashboard/src/routes/org/[slug]/courses/+page.svelte @@ -57,7 +57,7 @@ } filteredCourses = courses.filter((course) => { - if (!searchValue || course.title.toLowerCase().includes(searchValue)) { + if (!searchValue || course.title.toLowerCase().includes(searchValue.toLowerCase())) { return true; } diff --git a/supabase/migrations/20240701085652_show_explore_courses.sql b/supabase/migrations/20240701085652_show_explore_courses.sql new file mode 100644 index 00000000..f2ae43d2 --- /dev/null +++ b/supabase/migrations/20240701085652_show_explore_courses.sql @@ -0,0 +1,17 @@ +CREATE OR REPLACE FUNCTION public.get_explore_courses(org_id_arg uuid, profile_id_arg uuid) + RETURNS TABLE(id uuid, org_id uuid, title character varying, slug character varying, description character varying, logo text, banner_image text, cost bigint, currency character varying, is_published boolean, total_lessons bigint, total_students bigint, progress_rate bigint, type "COURSE_TYPE", other_profile_id uuid) + LANGUAGE plpgsql +AS $function$ +BEGIN + Return query + select course.id, organization.id AS org_id, course.title, course.slug, course.description, course.logo, course.banner_image, course.cost, course.currency, course.is_published, (select COUNT(*) from lesson +as l where l.course_id = course.id) AS total_lessons, (select COUNT(*) from groupmember as gm where gm.group_id = course.group_id AND gm.role_id = 3) as total_students, (select COUNT(*) from lesson_completion as lc join lesson as l on l.id = lc.lesson_id where l.course_id = course.id and lc.is_complete = true and lc.profile_id = profile_id_arg) AS progress_rate, course.type as type, (select groupmember.profile_id from groupmember where groupmember.group_id = "group".id and groupmember.profile_id != profile_id_arg +limit 1) as other_profile_id + from course + join "group" on "group".id = course.group_id + join organization on organization.id = "group".organization_id + where course.status = 'ACTIVE' AND course.is_published = true AND organization.id = org_id_arg AND profile_id_arg NOT IN (SELECT groupmember.profile_id FROM groupmember WHERE groupmember.group_id = course.group_id) + ORDER BY course.created_at DESC; +END; +$function$ +; \ No newline at end of file