From 931ccb1a0918da1e0987ed0985eb5a646284ef10 Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Sun, 30 Jun 2024 04:47:15 +0100 Subject: [PATCH] feat: lesson note version history (#374) * chore: install diff package * chore: add id to render component * feat: implement language lesson version history * chore: fix darkmode colors and use tailwind css * chore: add screensize conditional * refactor: list updating after data fetch * change mode to view to prevent duplicate data being saved * chore: remove raw text * chore: db migration * feat: properly format timestamp * feat: add snackbar for success * chore: group by minutes, use icons instead of texts for buttons * docs: generate translations * refactor --------- Co-authored-by: Iniubong Obonguko --- apps/dashboard/package.json | 1 + .../LanguageLessonVersionHistory.svelte | 268 ++++++++++++++++++ .../components/HTMLRender/HTMLRender.svelte | 3 +- .../src/lib/utils/services/courses/index.ts | 10 + .../src/lib/utils/translations/de.json | 11 +- .../src/lib/utils/translations/en.json | 9 +- .../src/lib/utils/translations/es.json | 11 +- .../src/lib/utils/translations/fr.json | 11 +- .../src/lib/utils/translations/hi.json | 11 +- .../src/lib/utils/translations/pt.json | 11 +- .../src/lib/utils/translations/ru.json | 11 +- .../src/lib/utils/translations/vi.json | 11 +- .../lessons/[...lessonParams]/+page.svelte | 53 +++- pnpm-lock.yaml | 8 + .../20240618210526_lesson_versions.sql | 7 + 15 files changed, 407 insertions(+), 29 deletions(-) create mode 100644 apps/dashboard/src/lib/components/Course/components/Lesson/LanguageLessonVersionHistory.svelte create mode 100644 supabase/migrations/20240618210526_lesson_versions.sql diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index b4e3dde1..0813723a 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -79,6 +79,7 @@ "babel-jest": "^29.7.0", "carbon-components-svelte": "^0.79.0", "carbon-icons-svelte": "^12.1.0", + "diff": "^5.2.0", "dotenv": "^16.3.1", "eslint-config-custom": "workspace:*", "jessy": "^3.1.1", diff --git a/apps/dashboard/src/lib/components/Course/components/Lesson/LanguageLessonVersionHistory.svelte b/apps/dashboard/src/lib/components/Course/components/Lesson/LanguageLessonVersionHistory.svelte new file mode 100644 index 00000000..b4ca04d0 --- /dev/null +++ b/apps/dashboard/src/lib/components/Course/components/Lesson/LanguageLessonVersionHistory.svelte @@ -0,0 +1,268 @@ + + + + + diff --git a/apps/dashboard/src/lib/components/HTMLRender/HTMLRender.svelte b/apps/dashboard/src/lib/components/HTMLRender/HTMLRender.svelte index cec37e70..8142bbc7 100644 --- a/apps/dashboard/src/lib/components/HTMLRender/HTMLRender.svelte +++ b/apps/dashboard/src/lib/components/HTMLRender/HTMLRender.svelte @@ -1,9 +1,10 @@ -
+
{#if $$slots.content} {:else} diff --git a/apps/dashboard/src/lib/utils/services/courses/index.ts b/apps/dashboard/src/lib/utils/services/courses/index.ts index 0da620d3..023cd68a 100644 --- a/apps/dashboard/src/lib/utils/services/courses/index.ts +++ b/apps/dashboard/src/lib/utils/services/courses/index.ts @@ -185,6 +185,16 @@ export function fetchLesson(lessonId: Lesson['id']) { .single(); } +export function fetchLesssonLanguageHistory(lessonId: string, locale: string, endRange: number) { + return supabase + .from('lesson_versions') + .select('*') + .range(0, endRange) + .eq('lesson_id', lessonId) + .eq('locale', locale) + .order('timestamp', { ascending: false }); +} + export function createLesson(lesson: any) { return supabase.from('lesson').insert(lesson).select(); } diff --git a/apps/dashboard/src/lib/utils/translations/de.json b/apps/dashboard/src/lib/utils/translations/de.json index 0abf9c2e..48bd5745 100644 --- a/apps/dashboard/src/lib/utils/translations/de.json +++ b/apps/dashboard/src/lib/utils/translations/de.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "Als abgeschlossen markiert", - "complete_download": "Download abgeschlossen" + "complete_download": "Download abgeschlossen", + "version_restored": "Version erfolgreich wiederhergestellt" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "Funktion ist deaktiviert", "next": "Nächste", "prev": "Vorher", + "version_history": { + "title": "Versionsgeschichte", + "fetch_more_versions": "Weitere Versionen abrufen", + "current_version": "Aktuelle Version", + "restore_version": "Stellen Sie diese Version wieder her" + }, "drag": "Ziehen Sie eine Karte, um sie neu anzuordnen", "no_lesson": "Noch kein Unterricht", "share_your_knowledge": "Teilen Sie Ihr Wissen mit der Welt, indem Sie ansprechende Lektionen erstellen. Klicken Sie zunächst auf die Schaltfläche „Hinzufügen“." @@ -1363,4 +1370,4 @@ "hyphen_rule": "Der Name der Organisation darf nicht mit einem Bindestrich beginnen oder enden" } } -} \ 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 6b95d845..b67bfe68 100644 --- a/apps/dashboard/src/lib/utils/translations/en.json +++ b/apps/dashboard/src/lib/utils/translations/en.json @@ -747,6 +747,12 @@ "option_label": "Option Label", "cancel": "Cancel", "poll_question": "Poll Question" + }, + "version_history": { + "title": "Version history", + "fetch_more_versions": "Fetch more versions", + "current_version": "Current version", + "restore_version": "Restore this version" } }, @@ -1243,7 +1249,8 @@ "lessons": { "success": { "complete_marked": "Marked as complete", - "complete_download": "Download Complete" + "complete_download": "Download Complete", + "version_restored": "Version restored sucessfully" }, "error": { "try_later": "Something's not right - Please try later" diff --git a/apps/dashboard/src/lib/utils/translations/es.json b/apps/dashboard/src/lib/utils/translations/es.json index 84cffdf8..e72e2bbd 100644 --- a/apps/dashboard/src/lib/utils/translations/es.json +++ b/apps/dashboard/src/lib/utils/translations/es.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "Marcado como completo", - "complete_download": "Descarga completa" + "complete_download": "Descarga completa", + "version_restored": "Versión restaurada exitosamente" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "La característica está deshabilitada", "next": "Próximo", "prev": "Anterior", + "version_history": { + "title": "Historial de versiones", + "fetch_more_versions": "Obtener más versiones", + "current_version": "Versión actual", + "restore_version": "Restaurar esta versión" + }, "drag": "arrastra una tarjeta para reordenar", "no_lesson": "Aún no hay lecciones", "share_your_knowledge": "Comparta sus conocimientos con el mundo creando lecciones interesantes. Comience haciendo clic en el botón Agregar." @@ -1363,4 +1370,4 @@ "hyphen_rule": "El nombre de la organización no puede comenzar ni terminar con un guión." } } -} \ 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 948f4db3..a4d30b10 100644 --- a/apps/dashboard/src/lib/utils/translations/fr.json +++ b/apps/dashboard/src/lib/utils/translations/fr.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "Marqué comme terminé", - "complete_download": "Téléchargement complet" + "complete_download": "Téléchargement complet", + "version_restored": "Version restaurée avec succès" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "La fonctionnalité est désactivée", "next": "Suivant", "prev": "Précédent", + "version_history": { + "title": "Historique des versions", + "fetch_more_versions": "Récupérer plus de versions", + "current_version": "Version actuelle", + "restore_version": "Restaurer cette version" + }, "drag": "faites glisser une carte pour réorganiser", "no_lesson": "Pas encore de cours", "share_your_knowledge": "Partagez vos connaissances avec le monde en créant des leçons engageantes. Commencez par cliquer sur le bouton Ajouter." @@ -1363,4 +1370,4 @@ "hyphen_rule": "Le nom de l'organisation ne peut pas commencer ou se terminer par un trait d'union" } } -} \ 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 b9f1763c..dff1756a 100644 --- a/apps/dashboard/src/lib/utils/translations/hi.json +++ b/apps/dashboard/src/lib/utils/translations/hi.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "पूर्ण के रूप में चिह्नित", - "complete_download": "डाउनलोड पूर्ण" + "complete_download": "डाउनलोड पूर्ण", + "version_restored": "संस्करण सफलतापूर्वक पुनर्स्थापित किया गया" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "सुविधा अक्षम है", "next": "अगला", "prev": "पिछला", + "version_history": { + "title": "संस्करण इतिहास", + "fetch_more_versions": "अधिक संस्करण प्राप्त करें", + "current_version": "वर्तमान संस्करण", + "restore_version": "इस संस्करण को पुनर्स्थापित करें" + }, "drag": "पुनः व्यवस्थित करने के लिए कार्ड खींचें", "no_lesson": "अभी तक कोई सबक नहीं", "share_your_knowledge": "आकर्षक पाठ बनाकर अपना ज्ञान दुनिया के साथ साझा करें। ऐड बटन पर क्लिक करके प्रारंभ करें।" @@ -1363,4 +1370,4 @@ "hyphen_rule": "संगठन का नाम हाइफ़न से प्रारंभ या समाप्त नहीं हो सकता" } } -} \ 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 be98d686..6eb5fb1a 100644 --- a/apps/dashboard/src/lib/utils/translations/pt.json +++ b/apps/dashboard/src/lib/utils/translations/pt.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "Marcado como concluído", - "complete_download": "Download completo" + "complete_download": "Download completo", + "version_restored": "Versão restaurada com sucesso" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "O recurso está desativado", "next": "Próximo", "prev": "Anterior", + "version_history": { + "title": "Histórico de versões", + "fetch_more_versions": "Buscar mais versões", + "current_version": "Versão Atual", + "restore_version": "Restaurar esta versão" + }, "drag": "arraste um cartão para reordenar", "no_lesson": "Ainda não há aulas", "share_your_knowledge": "Compartilhe seu conhecimento com o mundo criando aulas envolventes. Comece clicando no botão Adicionar." @@ -1363,4 +1370,4 @@ "hyphen_rule": "O nome da organização não pode começar ou terminar com um hífen" } } -} \ 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 098cc533..ff0f6022 100644 --- a/apps/dashboard/src/lib/utils/translations/ru.json +++ b/apps/dashboard/src/lib/utils/translations/ru.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "Помечено как завершенное", - "complete_download": "Загрузка завершена" + "complete_download": "Загрузка завершена", + "version_restored": "Версия успешно восстановлена" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "Функция отключена", "next": "Следующий", "prev": "Предыдущий", + "version_history": { + "title": "История версий", + "fetch_more_versions": "Получить больше версий", + "current_version": "Текущая версия", + "restore_version": "Восстановить эту версию" + }, "drag": "перетащите карточку, чтобы изменить порядок", "no_lesson": "Уроков пока нет", "share_your_knowledge": "Поделитесь своими знаниями со всем миром, создавая увлекательные уроки. Начните с нажатия кнопки «Добавить»." @@ -1363,4 +1370,4 @@ "hyphen_rule": "Название организации не может начинаться или заканчиваться дефисом." } } -} \ 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 0f02c11d..a01997a0 100644 --- a/apps/dashboard/src/lib/utils/translations/vi.json +++ b/apps/dashboard/src/lib/utils/translations/vi.json @@ -47,7 +47,8 @@ }, "success": { "complete_marked": "Được đánh dấu là hoàn thành", - "complete_download": "Hoan thanh tải" + "complete_download": "Hoan thanh tải", + "version_restored": "Đã khôi phục phiên bản thành công" } }, "attendance": { @@ -750,6 +751,12 @@ "disabled": "Tính năng bị vô hiệu hóa", "next": "Kế tiếp", "prev": "Trước đó", + "version_history": { + "title": "Lịch sử phiên bản", + "fetch_more_versions": "Tìm nạp thêm phiên bản", + "current_version": "Phiên bản hiện tại", + "restore_version": "Khôi phục phiên bản này" + }, "drag": "kéo thẻ để sắp xếp lại", "no_lesson": "Chưa có bài học nào", "share_your_knowledge": "Chia sẻ kiến ​​thức của bạn với thế giới bằng cách tạo ra những bài học hấp dẫn. Bắt đầu bằng cách nhấp vào nút Thêm." @@ -1363,4 +1370,4 @@ "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" } } -} \ No newline at end of file +} diff --git a/apps/dashboard/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte b/apps/dashboard/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte index d54eee9c..a1239d7c 100644 --- a/apps/dashboard/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte +++ b/apps/dashboard/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte @@ -19,9 +19,11 @@ import PageBody from '$lib/components/PageBody/index.svelte'; import Materials from '$lib/components/Course/components/Lesson/Materials/index.svelte'; import Exercises from '$lib/components/Course/components/Lesson/Exercises/index.svelte'; + import LanguageLessonVersionHistory from '$lib/components/Course/components/Lesson/LanguageLessonVersionHistory.svelte'; import MODES from '$lib/utils/constants/mode'; import { course } from '$lib/components/Course/store'; import Download from 'carbon-icons-svelte/lib/Download.svelte'; + import ResultOld from 'carbon-icons-svelte/lib/ResultOld.svelte'; import OverflowMenuVertical from 'carbon-icons-svelte/lib/OverflowMenuVertical.svelte'; import { apps } from '$lib/components/Apps/store'; import APPS_CONSTANTS from '$lib/components/Apps/constants'; @@ -39,7 +41,7 @@ import { getIsLessonComplete } from '$lib/components/Course/components/Lesson/functions'; import { t } from '$lib/utils/functions/translations'; import { LANGUAGES } from '$lib/utils/constants/translation'; - import { ChevronLeft, ChevronRight } from 'carbon-icons-svelte'; + import { ChevronLeft, ChevronRight, Edit, Save } from 'carbon-icons-svelte'; export let data; @@ -50,6 +52,7 @@ let isLoading = false; let isSaving = false; let isLessonComplete = false; + let isVersionDrawerOpen = false; function getLessonOrder(id: string) { const index = $lessons.findIndex((lesson) => lesson.id === id); @@ -268,6 +271,15 @@ goto(path); }; + const refetchDataAfterVersionRestore = () => { + isVersionDrawerOpen = false; + if (data.courseId && browser) { + mode = MODES.view; + fetchReqData(data.lessonId, data.isMaterialsTabActive); + } + snackbar.success('snackbar.lessons.success.version_restored'); + }; + $: path = $page.url?.pathname?.replace(/\/exercises[\/ 0-9 a-z -]*/, ''); $: if (data.courseId && browser) { @@ -303,12 +315,16 @@ ]} > -
+
{#if data.isMaterialsTabActive} -
- -
+ + {#if mode === MODES.edit && window.innerWidth >= 1024} + (isVersionDrawerOpen = true)}> + + + {/if} +
@@ -321,19 +337,19 @@ : 'hidden' } lg:flex items-center`} > - { $apps.dropdown = false; toggleMode(); }} - isDisabled={isSaving} + disabled={isSaving} > - {mode === MODES.edit - ? $t('course.navItem.lessons.done') - : $t('course.navItem.lessons.edit')} - + {#if mode === MODES.edit} + + {:else} + + {/if} + {#if $course.metadata.lessonDownload && !!PUBLIC_SERVER_URL} {/if}
+ + {/if}
@@ -437,6 +455,15 @@
+ + + {#if isVersionDrawerOpen && window.innerWidth >= 1024} + (isVersionDrawerOpen = false)} + on:restore={refetchDataAfterVersionRestore} + /> + {/if}