diff --git a/apps/dashboard/src/lib/features/course/components/lesson/video/video-card-dropdown.svelte b/apps/dashboard/src/lib/features/course/components/lesson/video/video-card-dropdown.svelte index adc4dbcb..e308e973 100644 --- a/apps/dashboard/src/lib/features/course/components/lesson/video/video-card-dropdown.svelte +++ b/apps/dashboard/src/lib/features/course/components/lesson/video/video-card-dropdown.svelte @@ -4,10 +4,13 @@ import { IconButton } from '@cio/ui/custom/icon-button'; import { mediaApi } from '$features/media/api'; import { JobPoller, jobsApi, type MediaJobEnvelope } from '$features/jobs'; + import { sidePanel } from '$features/side-panel'; import { snackbar } from '$features/ui/snackbar/store'; import { t } from '$lib/utils/functions/translations'; import { onDestroy } from 'svelte'; import type { LessonVideo } from './video-card-utils'; + import { lessonVideoBus } from './lesson-video-bus.svelte'; + import { TRANSCRIPT_PANEL_ID } from './transcript-panel-definition'; interface Props { video: LessonVideo; @@ -19,6 +22,8 @@ let { video, onRemove, menuPlacement = 'inline' }: Props = $props(); let isTranscribing = $state(false); + let hasTranscript = $state(false); + let hasCheckedTranscript = $state(false); let activePoller: JobPoller | null = null; const assetId = $derived((video as LessonVideo & { assetId?: string }).assetId ?? null); @@ -29,6 +34,14 @@ activePoller?.stop(); }); + async function ensureTranscriptChecked() { + if (hasCheckedTranscript || !assetId) return; + + hasCheckedTranscript = true; + const data = await mediaApi.getAssetTranscript(assetId); + hasTranscript = !!data?.segments?.length; + } + async function startTranscriptionPoll(afterAssetId: string) { activePoller?.stop(); activePoller = null; @@ -44,6 +57,7 @@ if (latest.job.status === 'completed') { isTranscribing = false; + hasTranscript = true; snackbar.success('snackbar.media_manager.transcription_completed'); return; @@ -62,6 +76,7 @@ activePoller?.stop(); activePoller = null; isTranscribing = false; + hasTranscript = true; snackbar.success('snackbar.media_manager.transcription_completed'); } @@ -85,6 +100,22 @@ await startTranscriptionPoll(assetId); } + async function handleViewTranscript() { + if (!assetId) return; + + lessonVideoBus.assetId = assetId; + lessonVideoBus.transcriptLoading = true; + sidePanel.open(TRANSCRIPT_PANEL_ID); + + try { + const data = await mediaApi.getAssetTranscript(assetId); + lessonVideoBus.transcript = data; + hasTranscript = !!data?.segments?.length; + } finally { + lessonVideoBus.transcriptLoading = false; + } + } + const generateLabel = $derived( isTranscribing ? t.get('course.navItem.lessons.materials.tabs.video.generate_transcript_in_progress') @@ -92,7 +123,13 @@ ); - + { + if (open && canGenerateTranscript) { + void ensureTranscriptChecked(); + } + }} +> {generateLabel} + {#if hasTranscript} + { + void handleViewTranscript(); + }} + > + {$t('course.navItem.lessons.materials.tabs.video.view_transcript')} + + {/if} {/if} {$t('course.navItem.lessons.materials.tabs.video.remove_video')} diff --git a/apps/dashboard/src/lib/utils/translations/da.json b/apps/dashboard/src/lib/utils/translations/da.json index 6bb6241b..802f8d26 100644 --- a/apps/dashboard/src/lib/utils/translations/da.json +++ b/apps/dashboard/src/lib/utils/translations/da.json @@ -1872,7 +1872,8 @@ "kind_generic": "Indlejret", "course_fallback": "Kursus", "menu_aria": "Video muligheder" - } + }, + "view_transcript": "Se transskription" }, "slide": { "title": "Slide", @@ -2163,9 +2164,7 @@ "depth": "Hvor dybt skal kurset gå?", "topic": "Emne eller niche", "expertise_level": "Mål ekspertiseniveau", - "documentation_url": { - "hint": "Vi læser dette websted for at basere kurset på dit rigtige produkt." - } + "documentation_url": "Dokumentations-URL (valgfrit)" }, "expert_on_x": { "title": "Bliv ekspert på X", diff --git a/apps/dashboard/src/lib/utils/translations/de.json b/apps/dashboard/src/lib/utils/translations/de.json index 188a00ec..25f30c4c 100644 --- a/apps/dashboard/src/lib/utils/translations/de.json +++ b/apps/dashboard/src/lib/utils/translations/de.json @@ -1872,7 +1872,8 @@ "kind_generic": "Eingebettet", "course_fallback": "Kurs", "menu_aria": "Videooptionen" - } + }, + "view_transcript": "Transkription ansehen" }, "slide": { "title": "Rutsche", @@ -2163,9 +2164,7 @@ "depth": "Wie tief soll der Kurs gehen?", "topic": "Thema oder Nische", "expertise_level": "Zielkompetenzniveau", - "documentation_url": { - "hint": "Wir werden diese Website lesen, um den Kurs in Ihrem realen Produkt zu verankern." - } + "documentation_url": "Dokumentations-URL (optional)" }, "expert_on_x": { "title": "Werden Sie Experte für X", diff --git a/apps/dashboard/src/lib/utils/translations/en.json b/apps/dashboard/src/lib/utils/translations/en.json index 5188725c..8f6e79b2 100644 --- a/apps/dashboard/src/lib/utils/translations/en.json +++ b/apps/dashboard/src/lib/utils/translations/en.json @@ -2276,6 +2276,7 @@ }, "generate_transcript": "Generate transcription", "generate_transcript_in_progress": "Transcribing…", + "view_transcript": "View transcription", "simple_card": { "kind_youtube": "YouTube", "kind_upload": "Upload", diff --git a/apps/dashboard/src/lib/utils/translations/es.json b/apps/dashboard/src/lib/utils/translations/es.json index 07de3b24..542b91a0 100644 --- a/apps/dashboard/src/lib/utils/translations/es.json +++ b/apps/dashboard/src/lib/utils/translations/es.json @@ -1872,7 +1872,8 @@ "kind_generic": "Incrustado", "course_fallback": "Curso", "menu_aria": "Opciones de vídeo" - } + }, + "view_transcript": "Ver transcripción" }, "slide": { "title": "diapositiva", @@ -2163,9 +2164,7 @@ "depth": "¿Qué profundidad debe tener el curso?", "topic": "Tema o nicho", "expertise_level": "Nivel de experiencia objetivo", - "documentation_url": { - "hint": "Leeremos este sitio para basar el curso en su producto real." - } + "documentation_url": "URL de documentación (opcional)" }, "expert_on_x": { "title": "Conviértete en un experto en X", diff --git a/apps/dashboard/src/lib/utils/translations/fr.json b/apps/dashboard/src/lib/utils/translations/fr.json index ec4c5c6f..b703f5ea 100644 --- a/apps/dashboard/src/lib/utils/translations/fr.json +++ b/apps/dashboard/src/lib/utils/translations/fr.json @@ -1872,7 +1872,8 @@ "kind_generic": "Intégré", "course_fallback": "Cours", "menu_aria": "Options vidéo" - } + }, + "view_transcript": "Voir la transcription" }, "slide": { "title": "Diapositive", @@ -2163,9 +2164,7 @@ "depth": "Jusqu’où doit aller le cours ?", "topic": "Sujet ou niche", "expertise_level": "Niveau d’expertise cible", - "documentation_url": { - "hint": "Nous lirons ce site pour ancrer le cours dans votre produit réel." - } + "documentation_url": "URL de la documentation (facultatif)" }, "expert_on_x": { "title": "Devenez un expert sur X", diff --git a/apps/dashboard/src/lib/utils/translations/hi.json b/apps/dashboard/src/lib/utils/translations/hi.json index f7b367d0..67ad7702 100644 --- a/apps/dashboard/src/lib/utils/translations/hi.json +++ b/apps/dashboard/src/lib/utils/translations/hi.json @@ -1872,7 +1872,8 @@ "kind_generic": "एंबेडेड", "course_fallback": "पाठ्यक्रम", "menu_aria": "वीडियो विकल्प" - } + }, + "view_transcript": "प्रतिलेखन देखें" }, "slide": { "title": "फिसल पट्टी", @@ -2163,9 +2164,7 @@ "depth": "पाठ्यक्रम कितना गहरा होना चाहिए?", "topic": "विषय या आला", "expertise_level": "लक्ष्य विशेषज्ञता स्तर", - "documentation_url": { - "hint": "हम आपके वास्तविक उत्पाद में पाठ्यक्रम को आधार बनाने के लिए इस साइट को पढ़ेंगे।" - } + "documentation_url": "दस्तावेज़ीकरण URL (वैकल्पिक)" }, "expert_on_x": { "title": "एक्स पर विशेषज्ञ बनें", diff --git a/apps/dashboard/src/lib/utils/translations/pl.json b/apps/dashboard/src/lib/utils/translations/pl.json index 7333d8f4..6006cea9 100644 --- a/apps/dashboard/src/lib/utils/translations/pl.json +++ b/apps/dashboard/src/lib/utils/translations/pl.json @@ -1872,7 +1872,8 @@ "kind_generic": "Wbudowany", "course_fallback": "Kurs", "menu_aria": "Opcje wideo" - } + }, + "view_transcript": "Zobacz transkrypcję" }, "slide": { "title": "Przesuń", @@ -2163,9 +2164,7 @@ "depth": "Jak głęboko powinien sięgać kurs?", "topic": "Temat lub nisza", "expertise_level": "Docelowy poziom wiedzy", - "documentation_url": { - "hint": "Przeczytamy tę witrynę, aby oprzeć kurs na Twoim prawdziwym produkcie." - } + "documentation_url": "Adres URL dokumentacji (opcjonalnie)" }, "expert_on_x": { "title": "Zostań ekspertem w dziedzinie X", diff --git a/apps/dashboard/src/lib/utils/translations/pt.json b/apps/dashboard/src/lib/utils/translations/pt.json index a0a56121..b8557889 100644 --- a/apps/dashboard/src/lib/utils/translations/pt.json +++ b/apps/dashboard/src/lib/utils/translations/pt.json @@ -1872,7 +1872,8 @@ "kind_generic": "Incorporado", "course_fallback": "Curso", "menu_aria": "Opções de vídeo" - } + }, + "view_transcript": "Ver transcrição" }, "slide": { "title": "Deslizar", @@ -2163,9 +2164,7 @@ "depth": "Quão profundo o curso deve ir?", "topic": "Tópico ou nicho", "expertise_level": "Nível de especialização alvo", - "documentation_url": { - "hint": "Leremos este site para fundamentar o curso em seu produto real." - } + "documentation_url": "URL da documentação (opcional)" }, "expert_on_x": { "title": "Torne-se um especialista em X", diff --git a/apps/dashboard/src/lib/utils/translations/ru.json b/apps/dashboard/src/lib/utils/translations/ru.json index a669bbd4..dd59a976 100644 --- a/apps/dashboard/src/lib/utils/translations/ru.json +++ b/apps/dashboard/src/lib/utils/translations/ru.json @@ -1872,7 +1872,8 @@ "kind_generic": "Встроенный", "course_fallback": "Курс", "menu_aria": "Параметры видео" - } + }, + "view_transcript": "Посмотреть транскрипцию" }, "slide": { "title": "Слайд", @@ -2163,9 +2164,7 @@ "depth": "Насколько глубоким должен быть курс?", "topic": "Тема или ниша", "expertise_level": "Целевой уровень экспертизы", - "documentation_url": { - "hint": "Мы прочитаем этот сайт, чтобы закрепить курс на вашем реальном продукте." - } + "documentation_url": "URL-адрес документации (необязательно)" }, "expert_on_x": { "title": "Станьте экспертом по X", diff --git a/apps/dashboard/src/lib/utils/translations/vi.json b/apps/dashboard/src/lib/utils/translations/vi.json index b8e06cbc..7eb8d3c1 100644 --- a/apps/dashboard/src/lib/utils/translations/vi.json +++ b/apps/dashboard/src/lib/utils/translations/vi.json @@ -1872,7 +1872,8 @@ "kind_generic": "Đã nhúng", "course_fallback": "Khóa học", "menu_aria": "Tùy chọn video" - } + }, + "view_transcript": "Xem bản chép lời" }, "slide": { "title": "Trượt", @@ -2163,9 +2164,7 @@ "depth": "Khóa học nên đi sâu đến mức nào?", "topic": "Chủ đề hoặc niche", "expertise_level": "Mục tiêu trình độ chuyên môn", - "documentation_url": { - "hint": "Chúng tôi sẽ đọc trang web này để xây dựng khóa học về sản phẩm thực của bạn." - } + "documentation_url": "URL tài liệu (tùy chọn)" }, "expert_on_x": { "title": "Trở thành chuyên gia về X",