diff --git a/apps/dashboard/src/lib/features/course/components/lesson/content-page-menu.svelte b/apps/dashboard/src/lib/features/course/components/lesson/content-page-menu.svelte new file mode 100644 index 00000000..0b8ed273 --- /dev/null +++ b/apps/dashboard/src/lib/features/course/components/lesson/content-page-menu.svelte @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + {$t('course.navItem.lessons.content_menu.unlock_all')} + + + + + + {contentGroupingEnabled + ? $t('course.navItem.lessons.content_menu.disable_grouping') + : $t('course.navItem.lessons.content_menu.enable_grouping')} + + + + + diff --git a/apps/dashboard/src/lib/features/course/pages/settings.svelte b/apps/dashboard/src/lib/features/course/pages/settings.svelte index e30f1366..dc375a33 100644 --- a/apps/dashboard/src/lib/features/course/pages/settings.svelte +++ b/apps/dashboard/src/lib/features/course/pages/settings.svelte @@ -25,7 +25,6 @@ import LockOpenIcon from '@lucide/svelte/icons/lock-open'; import type { TCourseType } from '@cio/db/types'; import type { Course } from '../utils/types'; - import { ContentType } from '@cio/utils/constants/content'; import { t } from '$lib/utils/functions/translations'; import { isObject } from '$lib/utils/functions/isObject'; import { snackbar } from '$features/ui/snackbar/store'; @@ -33,6 +32,7 @@ import { DEFAULT_COMPLIANCE_SETTINGS } from '../utils/compliance-utils'; import { DeleteModal } from '$features/ui'; import { contentApi, courseApi } from '$features/course/api'; + import { collectLockedContentItems } from '$features/course/utils/content-lock-utils'; import { tagApi } from '$features/tag/api'; import { uploadImage } from '$lib/utils/services/upload'; import { copyToClipboard } from '$lib/utils/functions/formatYoutubeVideo'; @@ -120,22 +120,6 @@ let isUnlockingAll = $state(false); - type LockedContentItem = { id: string; type: ContentType.Lesson | ContentType.Exercise }; - - function collectLockedContentItems(course: Course | null): LockedContentItem[] { - const content = course?.content; - if (!content) return []; - - const items = content.grouped ? content.sections.flatMap((section) => section.items) : content.items; - - return items - .filter((item) => (item.isUnlocked ?? true) === false) - .map((item) => ({ - id: item.id, - type: item.type === ContentType.Exercise ? ContentType.Exercise : ContentType.Lesson - })); - } - const lockedContentItems = $derived(collectLockedContentItems(courseApi.course)); const isLiveClassCourse = $derived(courseApi.course?.type === 'LIVE_CLASS'); const showLockedContentNotice = $derived($settings.isPublished && lockedContentItems.length > 0); diff --git a/apps/dashboard/src/lib/features/course/utils/content-lock-utils.ts b/apps/dashboard/src/lib/features/course/utils/content-lock-utils.ts index 7cd7a492..471ff478 100644 --- a/apps/dashboard/src/lib/features/course/utils/content-lock-utils.ts +++ b/apps/dashboard/src/lib/features/course/utils/content-lock-utils.ts @@ -1,5 +1,22 @@ import { ContentType } from '@cio/utils/constants/content'; import type { StudentContentLockReason } from '$features/ai-assistant/utils/content-ask-ai-bar'; +import type { Course } from './types'; +import { getCourseContent } from './content'; + +export type LockedContentItem = { id: string; type: ContentType.Lesson | ContentType.Exercise }; + +export function collectLockedContentItems(course: Course | null): LockedContentItem[] { + const content = getCourseContent(course); + + const items = content.grouped ? content.sections.flatMap((section) => section.items) : content.items; + + return items + .filter((item) => (item.isUnlocked ?? true) === false) + .map((item) => ({ + id: item.id, + type: item.type === ContentType.Exercise ? ContentType.Exercise : ContentType.Lesson + })); +} export function getStudentContentLockTitleKey(reason: StudentContentLockReason): string { if (reason === 'progression_locked') { diff --git a/apps/dashboard/src/lib/utils/translations/da.json b/apps/dashboard/src/lib/utils/translations/da.json index a3dcfffa..60c1a744 100644 --- a/apps/dashboard/src/lib/utils/translations/da.json +++ b/apps/dashboard/src/lib/utils/translations/da.json @@ -289,7 +289,10 @@ "successful_integration": "Integration lykkedes", "successful_deletion": "Integration slettet", "update_successful": "Opdateringen lykkedes", - "unlocked_all_content": "Alle kursusartikler er låst op" + "unlocked_all_content": "Alle kursusartikler er låst op", + "all_content_already_unlocked": "Alt indhold er allerede låst op", + "content_grouping_enabled": "Indholdsgruppering aktiveret", + "content_grouping_disabled": "Indholdsgruppering deaktiveret" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Opret lektion", "add_content_section_title_placeholder": "f.eks. Modul 1: Introduktion", "add_content_lesson_title_placeholder": "f.eks. Kom godt i gang med variabler", + "content_menu": { + "label": "Indholdsindstillinger", + "unlock_all": "Lås alt indhold op", + "enable_grouping": "Aktivér indholdsgruppering", + "disable_grouping": "Deaktivér indholdsgruppering" + }, "join_lesson": "Deltag i lektionen", "no_link": "Intet link", "no_tutor": "Ingen underviser tilføjet", diff --git a/apps/dashboard/src/lib/utils/translations/de.json b/apps/dashboard/src/lib/utils/translations/de.json index 8200beec..8506926f 100644 --- a/apps/dashboard/src/lib/utils/translations/de.json +++ b/apps/dashboard/src/lib/utils/translations/de.json @@ -289,7 +289,10 @@ "successful_integration": "Integration erfolgreich", "successful_deletion": "Integration gelöscht", "update_successful": "Update erfolgreich", - "unlocked_all_content": "Alle Kursgegenstände freigeschaltet" + "unlocked_all_content": "Alle Kursgegenstände freigeschaltet", + "all_content_already_unlocked": "Alle Inhalte sind bereits freigeschaltet", + "content_grouping_enabled": "Inhaltsgruppierung aktiviert", + "content_grouping_disabled": "Inhaltsgruppierung deaktiviert" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Lektion erstellen", "add_content_section_title_placeholder": "z. B. Modul 1: Einführung", "add_content_lesson_title_placeholder": "z. B. Erste Schritte mit Variablen", + "content_menu": { + "label": "Inhaltsoptionen", + "unlock_all": "Alle Inhalte freischalten", + "enable_grouping": "Inhaltsgruppierung aktivieren", + "disable_grouping": "Inhaltsgruppierung deaktivieren" + }, "join_lesson": "Nehmen Sie an der Lektion teil", "no_link": "Kein Link", "no_tutor": "Kein Tutor hinzugefügt", diff --git a/apps/dashboard/src/lib/utils/translations/en.json b/apps/dashboard/src/lib/utils/translations/en.json index 0458ae76..cf5a9482 100644 --- a/apps/dashboard/src/lib/utils/translations/en.json +++ b/apps/dashboard/src/lib/utils/translations/en.json @@ -2236,6 +2236,12 @@ }, "add_content_section_title_placeholder": "e.g., Module 1: Introduction", "add_content_lesson_title_placeholder": "e.g., Getting Started with Variables", + "content_menu": { + "label": "Content options", + "unlock_all": "Unlock all content", + "enable_grouping": "Enable content grouping", + "disable_grouping": "Disable content grouping" + }, "join_lesson": "Join lesson", "no_link": "No link", "no_tutor": "No tutor added", @@ -4173,6 +4179,9 @@ "course_settings": { "success": { "unlocked_all_content": "All course items unlocked", + "all_content_already_unlocked": "All content is already unlocked", + "content_grouping_enabled": "Content grouping enabled", + "content_grouping_disabled": "Content grouping disabled", "download": "Download Complete", "saved": "Saved successfully", "successful_integration": "Integration successful", diff --git a/apps/dashboard/src/lib/utils/translations/es.json b/apps/dashboard/src/lib/utils/translations/es.json index 42903d61..02eeb8ac 100644 --- a/apps/dashboard/src/lib/utils/translations/es.json +++ b/apps/dashboard/src/lib/utils/translations/es.json @@ -289,7 +289,10 @@ "successful_integration": "Integración exitosa", "successful_deletion": "Integración eliminada", "update_successful": "Actualización exitosa", - "unlocked_all_content": "Todos los elementos del curso desbloqueados" + "unlocked_all_content": "Todos los elementos del curso desbloqueados", + "all_content_already_unlocked": "Todo el contenido ya está desbloqueado", + "content_grouping_enabled": "Agrupación de contenidos activada", + "content_grouping_disabled": "Agrupación de contenidos desactivada" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Crear lección", "add_content_section_title_placeholder": "por ejemplo, Módulo 1: Introducción", "add_content_lesson_title_placeholder": "por ejemplo, Introducción a las variables", + "content_menu": { + "label": "Opciones de contenido", + "unlock_all": "Desbloquear todo el contenido", + "enable_grouping": "Activar agrupación de contenidos", + "disable_grouping": "Desactivar agrupación de contenidos" + }, "join_lesson": "Unirse a la lección", "no_link": "Sin enlace", "no_tutor": "No se agregó ningún tutor", diff --git a/apps/dashboard/src/lib/utils/translations/fr.json b/apps/dashboard/src/lib/utils/translations/fr.json index 39eadfca..0212c651 100644 --- a/apps/dashboard/src/lib/utils/translations/fr.json +++ b/apps/dashboard/src/lib/utils/translations/fr.json @@ -289,7 +289,10 @@ "successful_integration": "Intégration réussie", "successful_deletion": "Intégration supprimée", "update_successful": "Mise à jour réussie", - "unlocked_all_content": "Tous les éléments de cours débloqués" + "unlocked_all_content": "Tous les éléments de cours débloqués", + "all_content_already_unlocked": "Tout le contenu est déjà déverrouillé", + "content_grouping_enabled": "Regroupement de contenu activé", + "content_grouping_disabled": "Regroupement de contenu désactivé" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Créer une leçon", "add_content_section_title_placeholder": "par exemple, Module 1 : Introduction", "add_content_lesson_title_placeholder": "par exemple, Premiers pas avec les variables", + "content_menu": { + "label": "Options de contenu", + "unlock_all": "Déverrouiller tout le contenu", + "enable_grouping": "Activer le regroupement de contenu", + "disable_grouping": "Désactiver le regroupement de contenu" + }, "join_lesson": "Rejoindre la leçon", "no_link": "Pas de lien", "no_tutor": "Aucun tuteur ajouté", diff --git a/apps/dashboard/src/lib/utils/translations/hi.json b/apps/dashboard/src/lib/utils/translations/hi.json index 2e4ed722..18ccfb0b 100644 --- a/apps/dashboard/src/lib/utils/translations/hi.json +++ b/apps/dashboard/src/lib/utils/translations/hi.json @@ -289,7 +289,10 @@ "successful_integration": "एकीकरण सफल", "successful_deletion": "एकीकरण हटा दिया गया", "update_successful": "अद्यतन सफल", - "unlocked_all_content": "सभी पाठ्यक्रम आइटम अनलॉक हो गए" + "unlocked_all_content": "सभी पाठ्यक्रम आइटम अनलॉक हो गए", + "all_content_already_unlocked": "सभी सामग्री पहले से अनलॉक है", + "content_grouping_enabled": "सामग्री समूहीकरण सक्षम", + "content_grouping_disabled": "सामग्री समूहीकरण अक्षम" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "पाठ बनाएँ", "add_content_section_title_placeholder": "उदाहरण के लिए, मॉड्यूल 1: परिचय", "add_content_lesson_title_placeholder": "उदाहरण के लिए, वेरिएबल्स के साथ शुरुआत करना", + "content_menu": { + "label": "सामग्री विकल्प", + "unlock_all": "सभी सामग्री अनलॉक करें", + "enable_grouping": "सामग्री समूहीकरण सक्षम करें", + "disable_grouping": "सामग्री समूहीकरण अक्षम करें" + }, "join_lesson": "पाठ से जुड़ें", "no_link": "कोई लिंक नहीं", "no_tutor": "कोई शिक्षक नहीं जोड़ा गया", diff --git a/apps/dashboard/src/lib/utils/translations/pl.json b/apps/dashboard/src/lib/utils/translations/pl.json index a4c7ca99..def446c3 100644 --- a/apps/dashboard/src/lib/utils/translations/pl.json +++ b/apps/dashboard/src/lib/utils/translations/pl.json @@ -289,7 +289,10 @@ "successful_integration": "Integracja udana", "successful_deletion": "Integracja usunięta", "update_successful": "Aktualizacja pomyślna", - "unlocked_all_content": "Wszystkie elementy kursu odblokowane" + "unlocked_all_content": "Wszystkie elementy kursu odblokowane", + "all_content_already_unlocked": "Cała treść jest już odblokowana", + "content_grouping_enabled": "Grupowanie treści włączone", + "content_grouping_disabled": "Grupowanie treści wyłączone" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Utwórz lekcję", "add_content_section_title_placeholder": "np. Moduł 1: Wprowadzenie", "add_content_lesson_title_placeholder": "np. Pierwsze kroki ze zmiennymi", + "content_menu": { + "label": "Opcje treści", + "unlock_all": "Odblokuj całą treść", + "enable_grouping": "Włącz grupowanie treści", + "disable_grouping": "Wyłącz grupowanie treści" + }, "join_lesson": "Dołącz do lekcji", "no_link": "Brak linku", "no_tutor": "Nie dodano żadnego nauczyciela", diff --git a/apps/dashboard/src/lib/utils/translations/pt.json b/apps/dashboard/src/lib/utils/translations/pt.json index 9563399b..d6abaaad 100644 --- a/apps/dashboard/src/lib/utils/translations/pt.json +++ b/apps/dashboard/src/lib/utils/translations/pt.json @@ -289,7 +289,10 @@ "successful_integration": "Integração bem-sucedida", "successful_deletion": "Integração excluída", "update_successful": "Atualização bem-sucedida", - "unlocked_all_content": "Todos os itens do curso desbloqueados" + "unlocked_all_content": "Todos os itens do curso desbloqueados", + "all_content_already_unlocked": "Todo o conteúdo já está desbloqueado", + "content_grouping_enabled": "Agrupamento de conteúdo ativado", + "content_grouping_disabled": "Agrupamento de conteúdo desativado" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Criar aula", "add_content_section_title_placeholder": "por exemplo, Módulo 1: Introdução", "add_content_lesson_title_placeholder": "por exemplo, Introdução às variáveis", + "content_menu": { + "label": "Opções de conteúdo", + "unlock_all": "Desbloquear todo o conteúdo", + "enable_grouping": "Ativar agrupamento de conteúdo", + "disable_grouping": "Desativar agrupamento de conteúdo" + }, "join_lesson": "Participar da aula", "no_link": "Sem link", "no_tutor": "Nenhum tutor adicionado", diff --git a/apps/dashboard/src/lib/utils/translations/ru.json b/apps/dashboard/src/lib/utils/translations/ru.json index d6c17f26..215db764 100644 --- a/apps/dashboard/src/lib/utils/translations/ru.json +++ b/apps/dashboard/src/lib/utils/translations/ru.json @@ -289,7 +289,10 @@ "successful_integration": "Интеграция прошла успешно", "successful_deletion": "Интеграция удалена", "update_successful": "Обновление успешно выполнено", - "unlocked_all_content": "Все предметы курса разблокированы" + "unlocked_all_content": "Все предметы курса разблокированы", + "all_content_already_unlocked": "Весь контент уже разблокирован", + "content_grouping_enabled": "Группировка контента включена", + "content_grouping_disabled": "Группировка контента отключена" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Создать урок", "add_content_section_title_placeholder": "например, Модуль 1: Введение", "add_content_lesson_title_placeholder": "например, Начало работы с переменными", + "content_menu": { + "label": "Параметры контента", + "unlock_all": "Разблокировать весь контент", + "enable_grouping": "Включить группировку контента", + "disable_grouping": "Отключить группировку контента" + }, "join_lesson": "Присоединиться к уроку", "no_link": "Нет ссылки", "no_tutor": "Репетитор не добавлен", diff --git a/apps/dashboard/src/lib/utils/translations/vi.json b/apps/dashboard/src/lib/utils/translations/vi.json index b9d2fdbc..6a4370ed 100644 --- a/apps/dashboard/src/lib/utils/translations/vi.json +++ b/apps/dashboard/src/lib/utils/translations/vi.json @@ -289,7 +289,10 @@ "successful_integration": "Tích hợp thành công", "successful_deletion": "Đã xóa tích hợp", "update_successful": "Cập nhật thành công", - "unlocked_all_content": "Tất cả các mục khóa học đã được mở khóa" + "unlocked_all_content": "Tất cả các mục khóa học đã được mở khóa", + "all_content_already_unlocked": "Tất cả nội dung đã được mở khóa", + "content_grouping_enabled": "Đã bật nhóm nội dung", + "content_grouping_disabled": "Đã tắt nhóm nội dung" } }, "people": { @@ -1320,6 +1323,12 @@ "add_content_create_lesson": "Tạo bài học", "add_content_section_title_placeholder": "ví dụ: Mô-đun 1: Giới thiệu", "add_content_lesson_title_placeholder": "ví dụ: Bắt đầu với các biến", + "content_menu": { + "label": "Tùy chọn nội dung", + "unlock_all": "Mở khóa tất cả nội dung", + "enable_grouping": "Bật nhóm nội dung", + "disable_grouping": "Tắt nhóm nội dung" + }, "join_lesson": "Tham gia bài học", "no_link": "Không có liên kết", "no_tutor": "Không có gia sư nào được thêm vào", diff --git a/apps/dashboard/src/routes/(app)/courses/[id]/lessons/+page.svelte b/apps/dashboard/src/routes/(app)/courses/[id]/lessons/+page.svelte index c7693d27..1e3089d0 100644 --- a/apps/dashboard/src/routes/(app)/courses/[id]/lessons/+page.svelte +++ b/apps/dashboard/src/routes/(app)/courses/[id]/lessons/+page.svelte @@ -1,5 +1,6 @@