diff --git a/apps/dashboard/src/lib/features/course/components/card-dropdown.svelte b/apps/dashboard/src/lib/features/course/components/card-dropdown.svelte
index 4f48e376..e777dda8 100644
--- a/apps/dashboard/src/lib/features/course/components/card-dropdown.svelte
+++ b/apps/dashboard/src/lib/features/course/components/card-dropdown.svelte
@@ -1,16 +1,8 @@
{#if lmsPublicQuickOnly ? showPublicCourseLinks : true}
@@ -84,41 +39,7 @@
- {#if lmsPublicQuickOnly}
-
-
- {$t('courses.course_card.context_menu.open_public_course')}
-
- void handleCopyCourseUrl()}>
-
- {$t('courses.course_card.context_menu.copy_course_url')}
-
- {:else}
- {#if showPublicCourseLinks}
-
-
- {$t('courses.course_card.context_menu.open_public_course')}
-
- void handleCopyCourseUrl()}>
-
- {$t('courses.course_card.context_menu.copy_course_url')}
-
-
- {/if}
-
- {$t('courses.course_card.context_menu.clone')}
-
-
- {$t('courses.course_card.context_menu.share')}
-
-
- {$t('courses.course_card.context_menu.invite')}
-
-
-
- {$t('courses.course_card.context_menu.delete')}
-
- {/if}
+
{/if}
diff --git a/apps/dashboard/src/lib/features/course/components/course-context-menu-content.svelte b/apps/dashboard/src/lib/features/course/components/course-context-menu-content.svelte
new file mode 100644
index 00000000..dfd90821
--- /dev/null
+++ b/apps/dashboard/src/lib/features/course/components/course-context-menu-content.svelte
@@ -0,0 +1,155 @@
+
+
+{#if lmsPublicQuickOnly}
+
+
+ {$t('courses.course_card.context_menu.view_course_site')}
+
+ void handleCopyCourseUrl()}>
+
+ {$t('courses.course_card.context_menu.copy_course_url')}
+
+{:else}
+ {#if includeViewAsStudent}
+ onViewAsStudent?.()}>
+
+ {$t('course.header.view_as_student')}
+
+
+ {/if}
+
+ {#if isPublished}
+
+
+ {$t('courses.course_card.context_menu.view_course_site')}
+
+ {#if showPublicCourseLinks}
+ void handleCopyCourseUrl()}>
+
+ {$t('courses.course_card.context_menu.copy_course_url')}
+
+ {/if}
+ {#if !hideOrgActions || includeOpen}
+
+ {/if}
+ {:else}
+
+ {$t('courses.course_card.context_menu.publish_course')}
+
+ {#if !hideOrgActions || includeOpen}
+
+ {/if}
+ {/if}
+
+ {#if includeOpen}
+
+ {$t('courses.course_card.context_menu.open')}
+
+ {/if}
+
+ {#if !hideOrgActions}
+
+ {$t('courses.course_card.context_menu.clone')}
+
+
+ {$t('courses.course_card.context_menu.share')}
+
+
+ {$t('courses.course_card.context_menu.invite')}
+
+
+
+ {$t('courses.course_card.context_menu.delete')}
+
+ {/if}
+{/if}
diff --git a/apps/dashboard/src/lib/features/course/components/course-header.svelte b/apps/dashboard/src/lib/features/course/components/course-header.svelte
index 652af5da..35150164 100644
--- a/apps/dashboard/src/lib/features/course/components/course-header.svelte
+++ b/apps/dashboard/src/lib/features/course/components/course-header.svelte
@@ -1,7 +1,10 @@
+
{/if}
@@ -78,16 +104,47 @@
{#if !$isStudentExperience}
-
+
+
+
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+
+ {#if courseApi.course}
+ (viewAsStudentOpen = true)}
+ />
+ {/if}
+
+
+
{/if}
@@ -98,3 +155,5 @@
courseSlug={courseApi.course?.slug}
currentOrgDomain={$currentOrgDomain}
/>
+
+
diff --git a/apps/dashboard/src/lib/features/course/components/course-list-row.svelte b/apps/dashboard/src/lib/features/course/components/course-list-row.svelte
index 5c6c952f..a75a1ad7 100644
--- a/apps/dashboard/src/lib/features/course/components/course-list-row.svelte
+++ b/apps/dashboard/src/lib/features/course/components/course-list-row.svelte
@@ -10,14 +10,10 @@
import { ContentType } from '@cio/utils/constants/content';
import { Image } from '$features/ui';
import { t } from '$lib/utils/functions/translations';
- import { copyCourseModal, deleteCourseModal } from '$features/course/utils/store';
- import { copyPublicCoursePageUrl, openCoursePreview } from '$features/course/utils/course-preview';
- import { currentOrgDomain } from '$lib/utils/store/org';
import { buildCoursePlaceholderAvatarUrl } from '$features/course/utils/course-list-row-utils';
import CourseContentIcon from './course-content-icon.svelte';
import CoursePublicBadge from './course-public-badge.svelte';
- import ExternalLinkIcon from '@lucide/svelte/icons/external-link';
- import CopyIcon from '@lucide/svelte/icons/copy';
+ import CourseContextMenuContent from './course-context-menu-content.svelte';
interface Tag {
id: string;
@@ -133,30 +129,6 @@
}
goto(resolve(`/courses/${id}`, {}));
}
-
- function handleOpen(e: MouseEvent) {
- e.stopPropagation();
- goto(resolve(`/courses/${id}`, {}));
- }
-
- function handleClone(e: MouseEvent) {
- e.stopPropagation();
- $copyCourseModal.open = true;
- $copyCourseModal.id = id;
- $copyCourseModal.title = title;
- $copyCourseModal.description = description;
- }
-
- function handleDelete(e: MouseEvent) {
- e.stopPropagation();
- $deleteCourseModal.open = true;
- $deleteCourseModal.id = id;
- $deleteCourseModal.title = title;
- }
-
- async function handleCopyCourseUrl() {
- await copyPublicCoursePageUrl(slug, $currentOrgDomain);
- }
- {#if showPublicCourseLinks}
-
- openCoursePreview({
- courseId: id,
- courseSlug: slug,
- currentOrgDomain: $currentOrgDomain
- })}
- >
-
- {$t('courses.course_card.context_menu.open_public_course')}
-
- void handleCopyCourseUrl()}>
-
- {$t('courses.course_card.context_menu.copy_course_url')}
-
- {/if}
- {#if !isLMS}
- {#if showPublicCourseLinks}
-
- {/if}
-
- {$t('courses.course_card.context_menu.open')}
-
-
- {$t('courses.course_card.context_menu.clone')}
-
-
-
- {$t('courses.course_card.context_menu.delete')}
-
+ {#if isLMS}
+
+ {:else}
+
{/if}
diff --git a/apps/dashboard/src/lib/features/course/components/view-course-site-unpublished-modal.svelte b/apps/dashboard/src/lib/features/course/components/view-course-site-unpublished-modal.svelte
new file mode 100644
index 00000000..81b45306
--- /dev/null
+++ b/apps/dashboard/src/lib/features/course/components/view-course-site-unpublished-modal.svelte
@@ -0,0 +1,66 @@
+
+
+ {
+ if (!isOpen && !isPublishing) {
+ open = false;
+ }
+ }}
+>
+
+
+ {$t('course.view_course_site.unpublished_title')}
+ {$t('course.view_course_site.unpublished_description')}
+
+
+
+
+
+
+
+
diff --git a/apps/dashboard/src/lib/features/course/pages/settings.svelte b/apps/dashboard/src/lib/features/course/pages/settings.svelte
index d918bfcd..c6c4a594 100644
--- a/apps/dashboard/src/lib/features/course/pages/settings.svelte
+++ b/apps/dashboard/src/lib/features/course/pages/settings.svelte
@@ -794,7 +794,7 @@
-
+
{$t('course.navItem.settings.publish')}
{$t('course.navItem.settings.determines')}
diff --git a/apps/dashboard/src/lib/features/course/utils/publish-course.ts b/apps/dashboard/src/lib/features/course/utils/publish-course.ts
new file mode 100644
index 00000000..aba34016
--- /dev/null
+++ b/apps/dashboard/src/lib/features/course/utils/publish-course.ts
@@ -0,0 +1,32 @@
+import { generateSlug } from '@cio/utils/functions';
+import { isObject } from '$lib/utils/functions/isObject';
+import { courseApi } from '$features/course/api';
+import type { Course } from '$features/course/utils/types';
+
+export async function publishCourse(course: Course): Promise {
+ if (!course?.id) {
+ return false;
+ }
+
+ let slug = course.slug?.trim() ? course.slug : undefined;
+ if (!slug) {
+ slug = generateSlug(course.title, { appendTimestamp: true });
+ }
+
+ const metadataPayload = {
+ ...(isObject(course.metadata) ? course.metadata : {}),
+ allowNewStudent: true
+ };
+
+ const result = await courseApi.update(
+ course.id,
+ {
+ isPublished: true,
+ slug,
+ metadata: metadataPayload
+ },
+ { showSuccessToast: true }
+ );
+
+ return !!result;
+}
diff --git a/apps/dashboard/src/lib/utils/translations/da.json b/apps/dashboard/src/lib/utils/translations/da.json
index db0fcc7f..bde2ec6b 100644
--- a/apps/dashboard/src/lib/utils/translations/da.json
+++ b/apps/dashboard/src/lib/utils/translations/da.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Se som studerende"
+ "view_as_student": "Se som studerende",
+ "view_course_site": "Se kursusside"
+ },
+ "view_course_site": {
+ "unpublished_title": "Udgiv dit kursus",
+ "unpublished_description": "Du bør udgive dette kursus, før du kan se sitet",
+ "cancel": "Annuller",
+ "publish_course": "Udgiv kursus"
},
"sidebar": {
"back_to_course": "Tilbage til kurset",
@@ -2420,6 +2427,8 @@
"delete": "Slet",
"open": "Åbn",
"open_public_course": "Åbn offentligt kursus",
+ "view_course_site": "Se kursusside",
+ "publish_course": "Udgiv kursus",
"copy_course_url": "Kopiér kursus-URL"
},
"due_date": "Forfalder",
diff --git a/apps/dashboard/src/lib/utils/translations/de.json b/apps/dashboard/src/lib/utils/translations/de.json
index 741a5230..05522897 100644
--- a/apps/dashboard/src/lib/utils/translations/de.json
+++ b/apps/dashboard/src/lib/utils/translations/de.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Als Student anzeigen"
+ "view_as_student": "Als Student anzeigen",
+ "view_course_site": "Kursseite anzeigen"
+ },
+ "view_course_site": {
+ "unpublished_title": "Kurs veröffentlichen",
+ "unpublished_description": "Sie sollten diesen Kurs veröffentlichen, bevor Sie die Seite ansehen können",
+ "cancel": "Abbrechen",
+ "publish_course": "Kurs veröffentlichen"
},
"sidebar": {
"back_to_course": "Zurück zum Kurs",
@@ -2420,6 +2427,8 @@
"delete": "Löschen",
"open": "Offen",
"open_public_course": "Öffentlichen Kurs öffnen",
+ "view_course_site": "Kursseite anzeigen",
+ "publish_course": "Kurs veröffentlichen",
"copy_course_url": "Kurs-URL kopieren"
},
"due_date": "Fällig",
diff --git a/apps/dashboard/src/lib/utils/translations/en.json b/apps/dashboard/src/lib/utils/translations/en.json
index 6d545d69..2473ed00 100644
--- a/apps/dashboard/src/lib/utils/translations/en.json
+++ b/apps/dashboard/src/lib/utils/translations/en.json
@@ -1970,6 +1970,8 @@
"invite": "Invite",
"delete": "Delete",
"open_public_course": "Open public course",
+ "view_course_site": "View course site",
+ "publish_course": "Publish course",
"copy_course_url": "Copy course URL"
},
"error_message": "An error occurred.",
@@ -2147,7 +2149,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "View as student"
+ "view_as_student": "View as student",
+ "view_course_site": "View Course Site"
+ },
+ "view_course_site": {
+ "unpublished_title": "Publish your course",
+ "unpublished_description": "You should publish this course before you can see the site",
+ "cancel": "Cancel",
+ "publish_course": "Publish Course"
},
"view_as_student": {
"title": "This will redirect you to the Student LMS.",
diff --git a/apps/dashboard/src/lib/utils/translations/es.json b/apps/dashboard/src/lib/utils/translations/es.json
index 85404798..fc8e67d0 100644
--- a/apps/dashboard/src/lib/utils/translations/es.json
+++ b/apps/dashboard/src/lib/utils/translations/es.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Ver como estudiante"
+ "view_as_student": "Ver como estudiante",
+ "view_course_site": "Ver sitio del curso"
+ },
+ "view_course_site": {
+ "unpublished_title": "Publica tu curso",
+ "unpublished_description": "Debes publicar este curso antes de poder ver el sitio",
+ "cancel": "Cancelar",
+ "publish_course": "Publicar curso"
},
"sidebar": {
"back_to_course": "volver al curso",
@@ -2420,6 +2427,8 @@
"delete": "Eliminar",
"open": "Abierto",
"open_public_course": "Abrir curso público",
+ "view_course_site": "Ver sitio del curso",
+ "publish_course": "Publicar curso",
"copy_course_url": "Copiar URL del curso"
},
"due_date": "debido",
diff --git a/apps/dashboard/src/lib/utils/translations/fr.json b/apps/dashboard/src/lib/utils/translations/fr.json
index 6a8e84d0..ebf30ec4 100644
--- a/apps/dashboard/src/lib/utils/translations/fr.json
+++ b/apps/dashboard/src/lib/utils/translations/fr.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Afficher en tant qu'étudiant"
+ "view_as_student": "Afficher en tant qu'étudiant",
+ "view_course_site": "Voir le site du cours"
+ },
+ "view_course_site": {
+ "unpublished_title": "Publiez votre cours",
+ "unpublished_description": "Vous devez publier ce cours avant de pouvoir voir le site",
+ "cancel": "Annuler",
+ "publish_course": "Publier le cours"
},
"sidebar": {
"back_to_course": "Retour au cours",
@@ -2420,6 +2427,8 @@
"delete": "Supprimer",
"open": "Ouvert",
"open_public_course": "Ouvrir le cours public",
+ "view_course_site": "Voir le site du cours",
+ "publish_course": "Publier le cours",
"copy_course_url": "Copier l’URL du cours"
},
"due_date": "Due",
diff --git a/apps/dashboard/src/lib/utils/translations/hi.json b/apps/dashboard/src/lib/utils/translations/hi.json
index 32034545..e1aa0e41 100644
--- a/apps/dashboard/src/lib/utils/translations/hi.json
+++ b/apps/dashboard/src/lib/utils/translations/hi.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "विद्यार्थी के रूप में देखें"
+ "view_as_student": "विद्यार्थी के रूप में देखें",
+ "view_course_site": "कोर्स साइट देखें"
+ },
+ "view_course_site": {
+ "unpublished_title": "अपना कोर्स प्रकाशित करें",
+ "unpublished_description": "साइट देखने से पहले आपको इस कोर्स को प्रकाशित करना चाहिए",
+ "cancel": "रद्द करें",
+ "publish_course": "कोर्स प्रकाशित करें"
},
"sidebar": {
"back_to_course": "पाठ्यक्रम पर वापस जाएँ",
@@ -2420,6 +2427,8 @@
"delete": "हटाएँ",
"open": "खुला",
"open_public_course": "सार्वजनिक कोर्स खोलें",
+ "view_course_site": "कोर्स साइट देखें",
+ "publish_course": "कोर्स प्रकाशित करें",
"copy_course_url": "कोर्स URL कॉपी करें"
},
"due_date": "देय",
diff --git a/apps/dashboard/src/lib/utils/translations/pl.json b/apps/dashboard/src/lib/utils/translations/pl.json
index 793ca2b4..ebc3ca4c 100644
--- a/apps/dashboard/src/lib/utils/translations/pl.json
+++ b/apps/dashboard/src/lib/utils/translations/pl.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Wyświetl jako student"
+ "view_as_student": "Wyświetl jako student",
+ "view_course_site": "Zobacz stronę kursu"
+ },
+ "view_course_site": {
+ "unpublished_title": "Opublikuj swój kurs",
+ "unpublished_description": "Powinieneś opublikować ten kurs, zanim zobaczysz stronę",
+ "cancel": "Anuluj",
+ "publish_course": "Opublikuj kurs"
},
"sidebar": {
"back_to_course": "Powrót na kurs",
@@ -2420,6 +2427,8 @@
"delete": "Usuń",
"open": "Otwórz",
"open_public_course": "Otwórz publiczny kurs",
+ "view_course_site": "Zobacz stronę kursu",
+ "publish_course": "Opublikuj kurs",
"copy_course_url": "Kopiuj adres URL kursu"
},
"due_date": "Termin",
diff --git a/apps/dashboard/src/lib/utils/translations/pt.json b/apps/dashboard/src/lib/utils/translations/pt.json
index f49962d9..955d91c1 100644
--- a/apps/dashboard/src/lib/utils/translations/pt.json
+++ b/apps/dashboard/src/lib/utils/translations/pt.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Ver como aluno"
+ "view_as_student": "Ver como aluno",
+ "view_course_site": "Ver site do curso"
+ },
+ "view_course_site": {
+ "unpublished_title": "Publique seu curso",
+ "unpublished_description": "Você deve publicar este curso antes de poder ver o site",
+ "cancel": "Cancelar",
+ "publish_course": "Publicar curso"
},
"sidebar": {
"back_to_course": "De volta ao curso",
@@ -2420,6 +2427,8 @@
"delete": "Excluir",
"open": "Abrir",
"open_public_course": "Abrir curso público",
+ "view_course_site": "Ver site do curso",
+ "publish_course": "Publicar curso",
"copy_course_url": "Copiar URL do curso"
},
"due_date": "Devido",
diff --git a/apps/dashboard/src/lib/utils/translations/ru.json b/apps/dashboard/src/lib/utils/translations/ru.json
index 7e970810..7d4e243c 100644
--- a/apps/dashboard/src/lib/utils/translations/ru.json
+++ b/apps/dashboard/src/lib/utils/translations/ru.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Посмотреть как студент"
+ "view_as_student": "Посмотреть как студент",
+ "view_course_site": "Посмотреть сайт курса"
+ },
+ "view_course_site": {
+ "unpublished_title": "Опубликуйте курс",
+ "unpublished_description": "Перед просмотром сайта необходимо опубликовать этот курс",
+ "cancel": "Отмена",
+ "publish_course": "Опубликовать курс"
},
"sidebar": {
"back_to_course": "Вернуться к курсу",
@@ -2420,6 +2427,8 @@
"delete": "Удалить",
"open": "Открыть",
"open_public_course": "Открыть публичный курс",
+ "view_course_site": "Посмотреть сайт курса",
+ "publish_course": "Опубликовать курс",
"copy_course_url": "Копировать URL курса"
},
"due_date": "Срок погашения",
diff --git a/apps/dashboard/src/lib/utils/translations/vi.json b/apps/dashboard/src/lib/utils/translations/vi.json
index 7c2b9846..2fc5401b 100644
--- a/apps/dashboard/src/lib/utils/translations/vi.json
+++ b/apps/dashboard/src/lib/utils/translations/vi.json
@@ -2234,7 +2234,14 @@
"header": {
"preview": "Preview course",
"preview_missing_slug": "Generate and save a course slug before previewing.",
- "view_as_student": "Xem với tư cách sinh viên"
+ "view_as_student": "Xem với tư cách sinh viên",
+ "view_course_site": "Xem trang khóa học"
+ },
+ "view_course_site": {
+ "unpublished_title": "Xuất bản khóa học của bạn",
+ "unpublished_description": "Bạn nên xuất bản khóa học này trước khi có thể xem trang web",
+ "cancel": "Hủy",
+ "publish_course": "Xuất bản khóa học"
},
"sidebar": {
"back_to_course": "Quay lại khóa học",
@@ -2420,6 +2427,8 @@
"delete": "Xóa",
"open": "Mở",
"open_public_course": "Mở khóa học công khai",
+ "view_course_site": "Xem trang khóa học",
+ "publish_course": "Xuất bản khóa học",
"copy_course_url": "Sao chép URL khóa học"
},
"due_date": "Đến hạn",