feat: add sequential progression and video watch lesson completion

Gate student content by completion order, track per-video watch progress
with seek enforcement, and auto-complete lessons at the configured
threshold. Show ring progress in the lesson header, keep watch progress
on rewatch, and sync progression locks client-side so the next item
unlocks after completion.
This commit is contained in:
Rotimi Best
2026-06-11 14:48:34 +01:00
parent c8154409f5
commit 52d9bf18c3
94 changed files with 3499 additions and 564 deletions
@@ -352,7 +352,8 @@
"thumbnail_regen_started": "Восстановление миниатюр…",
"thumbnail_regen_failed": "Не удалось восстановить миниатюры.",
"transcript_saved": "Стенограмма обновлена",
"transcript_save_failed": "Не удалось сохранить расшифровку"
"transcript_save_failed": "Не удалось сохранить расшифровку",
"transcription_already_running": "Транскрипция этого видео уже выполняется."
},
"tags": {
"group_created": "Группа тегов создана.",
@@ -743,6 +744,10 @@
"drag": "Перетащите метки, чтобы изменить порядок вкладок материалов.",
"content_grouping_title": "Группировка контента",
"content_grouping_description": "Группируйте уроки и упражнения по разделам в списке контента.",
"progression_mode_title": "Прогресс студента",
"progression_mode_description": "Управляйте доступом студентов ко всему разблокированному контенту или требуйте прохождения по порядку.",
"progression_mode_free": "Свободный — разблокированный контент доступен",
"progression_mode_sequential": "Последовательный — завершите предыдущие элементы, чтобы открыть следующий",
"lesson_download": "Уроки СкачатьСкачать",
"available": "Сделайте урок доступным для скачивания в формате pdf для зарегистрированных студентов.",
"course_download": "Загрузка курса",
@@ -1022,7 +1027,14 @@
"field_template": "Шаблон",
"field_accent": "Акцент",
"field_signatories": "Подписавшиеся",
"customize_design": "Настроить дизайн"
"customize_design": "Настроить дизайн",
"signatory_one_toggle": "Показать подписанта 1",
"signatory_two_toggle": "Показать подписанта 2",
"signature_upload": "Изображение подписи",
"signature_upload_hint": "Загрузите изображение подписи (необязательно)",
"signature_dont_crop": "Не обрезать",
"signature_upload_in_progress": "Прежде чем сохранять, дождитесь завершения загрузки подписи.",
"save_failed": "Не удалось сохранить дизайн сертификата. Проверьте форму и повторите попытку."
}
},
"people": {
@@ -1758,7 +1770,11 @@
"collapse_section": "Свернуть раздел",
"expand_section": "Развернуть раздел"
},
"more": "Действия"
"more": "Действия",
"settings_completion_policy": "Правило завершения",
"settings_completion_submitted": "Отправлено — любая отправка засчитывается",
"settings_completion_passed": "Пройдено — требуется минимальный балл",
"settings_pass_threshold": "Порог прохождения (%)"
},
"new_exercise_modal": {
"heading": "Новое упражнение",
@@ -1803,6 +1819,9 @@
"button": "кнопка",
"button_done": "Готово",
"tabs": {
"settings": {
"title": "Настройки"
},
"document": {
"title": "Документы",
"add_document": "Добавить документ",
@@ -1918,7 +1937,8 @@
"kind_google_drive": "Гугл Диск",
"kind_generic": "Встроенный",
"course_fallback": "Курс",
"menu_aria": "Параметры видео"
"menu_aria": "Параметры видео",
"transcription_in_progress": "(Идет генерация стенограммы)"
},
"view_transcript": "Посмотреть транскрипцию",
"playback_error": "Не удалось загрузить это видео.",
@@ -1926,7 +1946,8 @@
"generate_1080": "Создать 1080p",
"generate_1080_in_progress": "Генерация 1080p…",
"generate_1080_ready": "доступно 1080p",
"generate_1080_unavailable": "Разрешение 1080p недоступно для этого исходного видео."
"generate_1080_unavailable": "Разрешение 1080p недоступно для этого исходного видео.",
"loading": "Загрузка видео…"
},
"slide": {
"title": "Слайд",
@@ -1985,6 +2006,38 @@
},
"content_locked_title": "Этот контент заблокирован",
"content_locked_description": "Ваш преподаватель еще не разблокировал этот контент. Завершите предыдущий контент или зайдите позже.",
"content_progression_locked_title": "Сначала завершите предыдущий материал",
"content_progression_locked_description": "Этот контент откроется после завершения предыдущего обязательного элемента курса.",
"content_progression_locked_description_lesson": "Этот урок откроется после завершения предыдущего обязательного элемента курса.",
"content_progression_locked_description_exercise": "Это упражнение откроется после завершения предыдущего обязательного элемента курса.",
"completion_policy": {
"label": "Правило завершения",
"manual_label": "Вручную",
"manual_description": "Студенты сами отмечают урок как завершённый.",
"video_watch_label": "Просмотр видео",
"video_watch_description": "Урок завершается автоматически после просмотра выбранных видео.",
"none_label": "Нет",
"none_description": "Этот урок не блокирует прохождение курса.",
"watch_threshold": "Порог просмотра (%)"
},
"settings": {
"progression": {
"heading": "Прогресс",
"intro": "Выберите, как студенты завершают этот урок и проходят курс.",
"option_guide_heading": "Что означает каждый вариант",
"watch_videos_heading": "Видео для контроля",
"watch_videos_helper": "Контроль возможен только для загруженных видео. YouTube и внешние встраивания отслеживать нельзя.",
"watch_videos_empty": "Добавьте загруженное видео на вкладке Видео для контроля просмотра.",
"watch_videos_required": "Выберите хотя бы одно видео для контроля просмотра.",
"students_only_note": "Эти правила действуют только для студентов. Преподаватель может свободно просматривать и переходить между уроками."
}
},
"watch_progress": {
"status": "Просмотрено {percent}% видеоконтента",
"videos_complete": "Просмотрено {percent}% обязательного видеоконтента ({complete} из {required} видео)",
"seek_blocked": "Нельзя перематывать вперёд в этом видео",
"complete_via_watch": "Урок завершается, когда вы достаточно просмотрите видео"
},
"saving": "Сохранение…",
"saved": "Сохранено",
"draft_recovery": "Для этого урока найден несохраненный черновик. Хотели бы вы восстановить его?",