@hed-hog/lms 0.0.364 → 0.0.365
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +1 -0
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
- package/dist/bitcode-wallet/bitcode-wallet.service.js +22 -3
- package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
- package/dist/course/course-export-scorm12-worker.service.d.ts +21 -0
- package/dist/course/course-export-scorm12-worker.service.d.ts.map +1 -0
- package/dist/course/course-export-scorm12-worker.service.js +109 -0
- package/dist/course/course-export-scorm12-worker.service.js.map +1 -0
- package/dist/course/course-export-scorm12.service.d.ts +42 -0
- package/dist/course/course-export-scorm12.service.d.ts.map +1 -0
- package/dist/course/course-export-scorm12.service.js +628 -0
- package/dist/course/course-export-scorm12.service.js.map +1 -0
- package/dist/course/course-export.service.d.ts +84 -0
- package/dist/course/course-export.service.d.ts.map +1 -0
- package/dist/course/course-export.service.js +237 -0
- package/dist/course/course-export.service.js.map +1 -0
- package/dist/course/course-structure.controller.d.ts +17 -9
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +17 -4
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +12 -4
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +98 -23
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-hls.service.d.ts +57 -0
- package/dist/course/course-video-hls.service.d.ts.map +1 -0
- package/dist/course/course-video-hls.service.js +767 -0
- package/dist/course/course-video-hls.service.js.map +1 -0
- package/dist/course/course.controller.d.ts +45 -13
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +40 -26
- package/dist/course/course.controller.js.map +1 -1
- package/dist/course/course.mcp-tools.js +1 -1
- package/dist/course/course.mcp-tools.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +11 -0
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +6 -9
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +57 -48
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.d.ts +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.js +1 -0
- package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.d.ts +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.js +1 -1
- package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -1
- package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
- package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
- package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
- package/dist/course/dto/create-course-export.dto.d.ts +14 -0
- package/dist/course/dto/create-course-export.dto.d.ts.map +1 -0
- package/dist/course/dto/create-course-export.dto.js +71 -0
- package/dist/course/dto/create-course-export.dto.js.map +1 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +2 -2
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +3 -2
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/lms-bulk-upload-automation.service.d.ts +16 -1
- package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload-automation.service.js +102 -8
- package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
- package/dist/course/lms-bulk-upload-infra.service.d.ts +1 -0
- package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload-infra.service.js +32 -8
- package/dist/course/lms-bulk-upload-infra.service.js.map +1 -1
- package/dist/course/lms-bulk-upload.controller.d.ts +30 -3
- package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.controller.js +43 -2
- package/dist/course/lms-bulk-upload.controller.js.map +1 -1
- package/dist/course/lms-bulk-upload.service.d.ts +11 -0
- package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.service.js +59 -6
- package/dist/course/lms-bulk-upload.service.js.map +1 -1
- package/dist/course/lms-setting.controller.d.ts +2 -1
- package/dist/course/lms-setting.controller.d.ts.map +1 -1
- package/dist/course/lms-setting.controller.js +4 -2
- package/dist/course/lms-setting.controller.js.map +1 -1
- package/dist/course/scorm12-schemas.d.ts +4 -0
- package/dist/course/scorm12-schemas.d.ts.map +1 -0
- package/dist/course/scorm12-schemas.js +9 -0
- package/dist/course/scorm12-schemas.js.map +1 -0
- package/dist/enterprise/training/training-student.service.d.ts +51 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +217 -4
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/evaluation/evaluation.service.d.ts +18 -0
- package/dist/evaluation/evaluation.service.d.ts.map +1 -1
- package/dist/evaluation/evaluation.service.js +125 -0
- package/dist/evaluation/evaluation.service.js.map +1 -1
- package/dist/exam/dto/create-standalone-question.dto.d.ts +12 -0
- package/dist/exam/dto/create-standalone-question.dto.d.ts.map +1 -0
- package/dist/exam/dto/create-standalone-question.dto.js +70 -0
- package/dist/exam/dto/create-standalone-question.dto.js.map +1 -0
- package/dist/exam/exam.module.d.ts.map +1 -1
- package/dist/exam/exam.module.js +2 -1
- package/dist/exam/exam.module.js.map +1 -1
- package/dist/exam/exam.service.d.ts +21 -0
- package/dist/exam/exam.service.d.ts.map +1 -1
- package/dist/exam/exam.service.js +80 -0
- package/dist/exam/exam.service.js.map +1 -1
- package/dist/exam/question.controller.d.ts +27 -0
- package/dist/exam/question.controller.d.ts.map +1 -0
- package/dist/exam/question.controller.js +53 -0
- package/dist/exam/question.controller.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +4 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +161 -25
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
- package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
- package/dist/lms-commerce-access.subscriber.d.ts +11 -0
- package/dist/lms-commerce-access.subscriber.d.ts.map +1 -0
- package/dist/lms-commerce-access.subscriber.js +74 -0
- package/dist/lms-commerce-access.subscriber.js.map +1 -0
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +6 -5
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/platforma-video.service.d.ts +39 -0
- package/dist/platforma/platforma-video.service.d.ts.map +1 -0
- package/dist/platforma/platforma-video.service.js +301 -0
- package/dist/platforma/platforma-video.service.js.map +1 -0
- package/dist/platforma/platforma.controller.d.ts +95 -1
- package/dist/platforma/platforma.controller.d.ts.map +1 -1
- package/dist/platforma/platforma.controller.js +160 -2
- package/dist/platforma/platforma.controller.js.map +1 -1
- package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts +5 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts.map +1 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.js +26 -0
- package/dist/student-xp/dto/grant-skill-card-xp.dto.js.map +1 -0
- package/dist/student-xp/student-xp.controller.d.ts +15 -0
- package/dist/student-xp/student-xp.controller.d.ts.map +1 -1
- package/dist/student-xp/student-xp.controller.js +24 -0
- package/dist/student-xp/student-xp.controller.js.map +1 -1
- package/dist/student-xp/student-xp.service.d.ts +16 -0
- package/dist/student-xp/student-xp.service.d.ts.map +1 -1
- package/dist/student-xp/student-xp.service.js +51 -1
- package/dist/student-xp/student-xp.service.js.map +1 -1
- package/hedhog/data/evaluation_topic.yaml +17 -0
- package/hedhog/data/menu.yaml +0 -17
- package/hedhog/data/queue_definition.yaml +48 -0
- package/hedhog/data/route.yaml +94 -124
- package/hedhog/data/setting_group.yaml +19 -19
- package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +337 -41
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +69 -4
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +420 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +308 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +17 -15
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -63
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +8 -3
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +31 -8
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +16 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +201 -401
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +378 -690
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +3 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +6 -10
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +49 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -3
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +0 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +106 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +28 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +0 -2
- package/hedhog/frontend/app/courses/page.tsx.ejs +45 -0
- package/hedhog/frontend/messages/en.json +26 -28
- package/hedhog/frontend/messages/pt.json +26 -28
- package/hedhog/table/course_export.yaml +62 -0
- package/package.json +13 -9
- package/src/bitcode-wallet/bitcode-wallet.service.ts +43 -4
- package/src/course/course-export-scorm12-worker.service.ts +124 -0
- package/src/course/course-export-scorm12.service.ts +668 -0
- package/src/course/course-export.service.ts +280 -0
- package/src/course/course-structure.controller.ts +14 -2
- package/src/course/course-structure.service.ts +100 -7
- package/src/course/course-video-hls.service.ts +946 -0
- package/src/course/course.controller.ts +33 -19
- package/src/course/course.mcp-tools.ts +1 -1
- package/src/course/course.module.ts +11 -0
- package/src/course/course.service.ts +73 -60
- package/src/course/dto/cleanup-course-storage.dto.ts +1 -0
- package/src/course/dto/cleanup-upload-history.dto.ts +1 -1
- package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
- package/src/course/dto/create-course-export.dto.ts +56 -0
- package/src/course/dto/create-course-structure-lesson.dto.ts +4 -3
- package/src/course/lms-bulk-upload-automation.service.ts +153 -6
- package/src/course/lms-bulk-upload-infra.service.ts +39 -6
- package/src/course/lms-bulk-upload.controller.ts +32 -2
- package/src/course/lms-bulk-upload.service.ts +70 -7
- package/src/course/lms-setting.controller.ts +4 -2
- package/src/course/scorm12-schemas.ts +9 -0
- package/src/enterprise/training/training-student.service.ts +221 -2
- package/src/evaluation/evaluation.service.ts +123 -0
- package/src/exam/dto/create-standalone-question.dto.ts +66 -0
- package/src/exam/exam.module.ts +2 -1
- package/src/exam/exam.service.ts +86 -0
- package/src/exam/question.controller.ts +28 -0
- package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +205 -31
- package/src/lms-commerce-access.subscriber.ts +88 -0
- package/src/lms.module.ts +6 -5
- package/src/platforma/platforma-video.service.ts +346 -0
- package/src/platforma/platforma.controller.ts +95 -1
- package/src/platforma/platforma.service.ts +268 -268
- package/src/student-xp/dto/grant-skill-card-xp.dto.ts +10 -0
- package/src/student-xp/student-xp.controller.ts +18 -2
- package/src/student-xp/student-xp.service.ts +84 -2
- package/hedhog/data/video_resolution_profile.yaml +0 -7
- package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +0 -607
- package/hedhog/table/course_video_resolution_profile.yaml +0 -22
- package/hedhog/table/video_resolution_profile.yaml +0 -18
- package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +0 -16
- package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +0 -16
- package/src/video-resolution-profile/video-resolution-profile.controller.ts +0 -62
- package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +0 -128
- package/src/video-resolution-profile/video-resolution-profile.module.ts +0 -13
- package/src/video-resolution-profile/video-resolution-profile.service.ts +0 -117
|
@@ -72,7 +72,7 @@ const TITLE_PATTERNS = [
|
|
|
72
72
|
'Conceitos fundamentais',
|
|
73
73
|
'Configuracao do ambiente',
|
|
74
74
|
'Pratica guiada passo a passo',
|
|
75
|
-
'
|
|
75
|
+
'Pratica: implementacao',
|
|
76
76
|
'Quiz de revisao',
|
|
77
77
|
'Caso de uso real',
|
|
78
78
|
'Implementacao completa',
|
|
@@ -107,7 +107,6 @@ const LESSON_TYPES: LessonType[] = [
|
|
|
107
107
|
'video',
|
|
108
108
|
'video',
|
|
109
109
|
'questao',
|
|
110
|
-
'exercicio',
|
|
111
110
|
];
|
|
112
111
|
const PROVIDERS: VideoProvider[] = [
|
|
113
112
|
'youtube',
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
TooltipTrigger,
|
|
8
8
|
} from '@/components/ui/tooltip';
|
|
9
9
|
import { cn } from '@/lib/utils';
|
|
10
|
+
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
10
11
|
import {
|
|
11
|
-
ClipboardList,
|
|
12
12
|
Clock3,
|
|
13
13
|
Eye,
|
|
14
14
|
EyeOff,
|
|
@@ -24,19 +24,18 @@ import {
|
|
|
24
24
|
} from 'lucide-react';
|
|
25
25
|
import { useTranslations } from 'next-intl';
|
|
26
26
|
import { useEffect, useRef } from 'react';
|
|
27
|
-
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
28
|
-
import { useUpdateLessonMutation } from '../_data/use-course-structure-mutations';
|
|
29
27
|
import {
|
|
30
28
|
getQueueJob,
|
|
31
29
|
type QueueJobStatus,
|
|
32
30
|
} from '../_data/services/course-structure.service';
|
|
31
|
+
import { useUpdateLessonMutation } from '../_data/use-course-structure-mutations';
|
|
33
32
|
import { HighlightedText } from './highlighted-text';
|
|
34
33
|
import { useStructureStore } from './store';
|
|
35
|
-
import type { Lesson, LessonStatus, LessonType } from './types';
|
|
36
34
|
import {
|
|
37
35
|
getLessonAttachedResourceCount,
|
|
38
36
|
getLessonUploadedVideoCount,
|
|
39
37
|
} from './tree-helpers';
|
|
38
|
+
import type { Lesson, LessonStatus, LessonType } from './types';
|
|
40
39
|
import { useTreeDisplaySettings } from './use-tree-display-settings';
|
|
41
40
|
|
|
42
41
|
// ── Type configs ──────────────────────────────────────────────────────────────
|
|
@@ -48,11 +47,6 @@ const LESSON_TYPE_CONFIG: Record<
|
|
|
48
47
|
video: { icon: Video, color: 'text-violet-500', label: 'Vídeo' },
|
|
49
48
|
post: { icon: FileText, color: 'text-emerald-500', label: 'Post' },
|
|
50
49
|
questao: { icon: HelpCircle, color: 'text-amber-500', label: 'Quiz' },
|
|
51
|
-
exercicio: {
|
|
52
|
-
icon: ClipboardList,
|
|
53
|
-
color: 'text-rose-500',
|
|
54
|
-
label: 'Exercício',
|
|
55
|
-
},
|
|
56
50
|
};
|
|
57
51
|
|
|
58
52
|
const STATUS_CONFIG: Record<LessonStatus, { label: string; dot: string }> = {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Types — LMS Course Structure TreeView
|
|
3
3
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
4
|
|
|
5
|
-
export type LessonType = 'video' | 'questao' | 'post'
|
|
5
|
+
export type LessonType = 'video' | 'questao' | 'post';
|
|
6
6
|
export type VideoProvider = 'youtube' | 'vimeo' | 'file_storage';
|
|
7
7
|
export type ItemType = 'course' | 'session' | 'lesson';
|
|
8
8
|
|
package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs
CHANGED
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* ⚠️ KNOWN MISMATCHES (documented with inline comments):
|
|
16
16
|
* 1. exameVinculado (number) ↔ linkedExam (string) — converted with String()/Number()
|
|
17
|
-
* 2.
|
|
18
|
-
* 3.
|
|
19
|
-
* 4. Resource.size / url — not returned by API; left as empty string / undefined.
|
|
17
|
+
* 2. Session.order — not returned by API; inferred from sorted array position.
|
|
18
|
+
* 3. Resource.size / url — not returned by API; left as empty string / undefined.
|
|
20
19
|
*/
|
|
21
20
|
|
|
22
21
|
import type {
|
|
@@ -55,7 +54,7 @@ import type {
|
|
|
55
54
|
* Map backend lesson type to the frontend LessonType union.
|
|
56
55
|
*
|
|
57
56
|
* ⚠️ ADAPTER POINT:
|
|
58
|
-
*
|
|
57
|
+
* Keep this helper updated if backend introduces new lesson type aliases.
|
|
59
58
|
*/
|
|
60
59
|
function normalizeLessonType(tipo: ApiLesson['tipo']): LessonType {
|
|
61
60
|
return tipo as LessonType;
|
|
@@ -129,7 +128,7 @@ export function normalizeCourse(raw: ApiCourse): Course {
|
|
|
129
128
|
description: raw.descricao,
|
|
130
129
|
slug: raw.slug,
|
|
131
130
|
offeringType: raw.offeringType,
|
|
132
|
-
published:
|
|
131
|
+
published: raw.status === 'published',
|
|
133
132
|
};
|
|
134
133
|
}
|
|
135
134
|
|
|
@@ -301,9 +300,7 @@ export function toCreateLessonPayload(
|
|
|
301
300
|
): ApiCreateLessonPayload {
|
|
302
301
|
return {
|
|
303
302
|
titulo: data.title,
|
|
304
|
-
|
|
305
|
-
// backend accepts it. Cast is safe as long as LessonType ⊆ ApiLesson['tipo'].
|
|
306
|
-
tipo: data.type as ApiCreateLessonPayload['tipo'],
|
|
303
|
+
tipo: data.type,
|
|
307
304
|
duracao: data.duration,
|
|
308
305
|
published: data.published,
|
|
309
306
|
statusProducao: data.status,
|
|
@@ -361,8 +358,7 @@ export function toUpdateLessonPayload(
|
|
|
361
358
|
): ApiUpdateLessonPayload {
|
|
362
359
|
const payload: ApiUpdateLessonPayload = {};
|
|
363
360
|
if (data.title !== undefined) payload.titulo = data.title;
|
|
364
|
-
if (data.type !== undefined)
|
|
365
|
-
payload.tipo = data.type as ApiCreateLessonPayload['tipo'];
|
|
361
|
+
if (data.type !== undefined) payload.tipo = data.type;
|
|
366
362
|
if (data.duration !== undefined) payload.duracao = data.duration;
|
|
367
363
|
if (data.published !== undefined) payload.published = data.published;
|
|
368
364
|
if (data.status !== undefined) payload.statusProducao = data.status;
|
package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs
CHANGED
|
@@ -54,6 +54,7 @@ type RequestFn = (input: {
|
|
|
54
54
|
data?: unknown;
|
|
55
55
|
headers?: Record<string, string>;
|
|
56
56
|
onUploadProgress?: (event: { loaded: number; total?: number }) => void;
|
|
57
|
+
showErrors?: boolean;
|
|
57
58
|
}) => Promise<{ data: unknown }>;
|
|
58
59
|
|
|
59
60
|
export type QueueJobStatus =
|
|
@@ -584,6 +585,7 @@ export async function getQueueJob(
|
|
|
584
585
|
const response = await request({
|
|
585
586
|
url: `/queue/jobs/${jobId}`,
|
|
586
587
|
method: 'GET',
|
|
588
|
+
showErrors: false,
|
|
587
589
|
});
|
|
588
590
|
return extractData<QueueJobResponse>(response);
|
|
589
591
|
}
|
|
@@ -604,3 +606,50 @@ export async function deleteFile(
|
|
|
604
606
|
data: { ids: [fileId] },
|
|
605
607
|
});
|
|
606
608
|
}
|
|
609
|
+
|
|
610
|
+
// ── Questions ─────────────────────────────────────────────────────────────────
|
|
611
|
+
|
|
612
|
+
export interface ApiQuestion {
|
|
613
|
+
id: number;
|
|
614
|
+
questionType: string;
|
|
615
|
+
statement: string;
|
|
616
|
+
points: number;
|
|
617
|
+
subjectId: number | null;
|
|
618
|
+
subjectName: string | null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export interface ApiQuestionListResponse {
|
|
622
|
+
total: number;
|
|
623
|
+
page: number;
|
|
624
|
+
limit: number;
|
|
625
|
+
data: ApiQuestion[];
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export interface CreateQuestionPayload {
|
|
629
|
+
statement: string;
|
|
630
|
+
questionType?: string;
|
|
631
|
+
points?: number;
|
|
632
|
+
subjectId?: number;
|
|
633
|
+
explanation?: string;
|
|
634
|
+
alternatives?: { text: string; isCorrect: boolean }[];
|
|
635
|
+
fillBlankAnswers?: { answer: string; alternatives?: string[] }[];
|
|
636
|
+
matchingPairs?: { id: string; leftText: string; rightText: string }[];
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
export async function listQuestions(
|
|
640
|
+
request: RequestFn,
|
|
641
|
+
search?: string
|
|
642
|
+
): Promise<ApiQuestionListResponse> {
|
|
643
|
+
const params = new URLSearchParams({ limit: '100' });
|
|
644
|
+
if (search?.trim()) params.set('search', search.trim());
|
|
645
|
+
const response = await request({ url: `/lms/questions?${params}`, method: 'GET' });
|
|
646
|
+
return response.data as ApiQuestionListResponse;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export async function createQuestion(
|
|
650
|
+
request: RequestFn,
|
|
651
|
+
dto: CreateQuestionPayload
|
|
652
|
+
): Promise<ApiQuestion> {
|
|
653
|
+
const response = await request({ url: '/lms/questions', method: 'POST', data: dto });
|
|
654
|
+
return response.data as ApiQuestion;
|
|
655
|
+
}
|
|
@@ -65,7 +65,7 @@ export interface ApiLessonInstructor {
|
|
|
65
65
|
* The backend maps DB lesson types to UI types in mapDbTypeToUiType():
|
|
66
66
|
* DB 'video' → UI 'video'
|
|
67
67
|
* DB 'text' → UI 'post'
|
|
68
|
-
* DB 'quiz' → UI 'questao'
|
|
68
|
+
* DB 'quiz' → UI 'questao' (based on content.sourceType)
|
|
69
69
|
*
|
|
70
70
|
* ⚠️ BACKEND NOTE — missing fields:
|
|
71
71
|
* `order` and `visibility` are NOT included in the API response.
|
|
@@ -80,7 +80,7 @@ export interface ApiLesson {
|
|
|
80
80
|
descricaoPrivada: string;
|
|
81
81
|
locale_id?: number | null;
|
|
82
82
|
/** UI-level lesson type resolved by the backend service. */
|
|
83
|
-
tipo: 'video' | 'questao' | 'post'
|
|
83
|
+
tipo: 'video' | 'questao' | 'post';
|
|
84
84
|
/** Duration in minutes. */
|
|
85
85
|
duracao: number;
|
|
86
86
|
published?: boolean;
|
|
@@ -140,6 +140,7 @@ export interface ApiCourse {
|
|
|
140
140
|
titulo: string;
|
|
141
141
|
descricao: string;
|
|
142
142
|
offeringType?: 'scheduled' | 'on_demand' | 'blended';
|
|
143
|
+
status?: 'draft' | 'published' | 'archived';
|
|
143
144
|
recursos?: ApiCourseResource[];
|
|
144
145
|
}
|
|
145
146
|
|
|
@@ -207,7 +208,7 @@ export interface ApiUpdateSessionPayload {
|
|
|
207
208
|
*/
|
|
208
209
|
export interface ApiCreateLessonPayload {
|
|
209
210
|
titulo: string;
|
|
210
|
-
tipo: 'video' | 'questao' | 'post'
|
|
211
|
+
tipo: 'video' | 'questao' | 'post';
|
|
211
212
|
/** Duration in minutes. */
|
|
212
213
|
duracao: number;
|
|
213
214
|
published?: boolean;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
4
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
5
|
+
|
|
6
|
+
export type ScormVisualSettings = {
|
|
7
|
+
primaryColor?: string;
|
|
8
|
+
fontFamily?: string;
|
|
9
|
+
fontSize?: string;
|
|
10
|
+
sidebarWidth?: number;
|
|
11
|
+
sidebarPosition?: string;
|
|
12
|
+
progressStyle?: string;
|
|
13
|
+
sidebarTheme?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CourseExportRecord = {
|
|
17
|
+
id: number;
|
|
18
|
+
format: string;
|
|
19
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
20
|
+
settings: Record<string, unknown> | null;
|
|
21
|
+
duration_seconds: number | null;
|
|
22
|
+
error_message: string | null;
|
|
23
|
+
created_at: string;
|
|
24
|
+
finished_at: string | null;
|
|
25
|
+
file_id: number | null;
|
|
26
|
+
notification_id: number | null;
|
|
27
|
+
file: {
|
|
28
|
+
id: number;
|
|
29
|
+
path: string;
|
|
30
|
+
filename: string;
|
|
31
|
+
size: number | null;
|
|
32
|
+
} | null;
|
|
33
|
+
user: {
|
|
34
|
+
id: number;
|
|
35
|
+
name: string;
|
|
36
|
+
photo_id: number | null;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function courseExportsQueryKey(courseId: string) {
|
|
41
|
+
return ['course-exports', courseId] as const;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function useCourseExportsQuery(courseId: string | null) {
|
|
45
|
+
const { request } = useApp();
|
|
46
|
+
|
|
47
|
+
return useQuery<CourseExportRecord[]>({
|
|
48
|
+
queryKey: courseExportsQueryKey(courseId ?? ''),
|
|
49
|
+
enabled: Boolean(courseId),
|
|
50
|
+
queryFn: async () => {
|
|
51
|
+
const res = await request<CourseExportRecord[]>({
|
|
52
|
+
url: `/lms/courses/${courseId}/exports`,
|
|
53
|
+
method: 'GET',
|
|
54
|
+
});
|
|
55
|
+
return res.data ?? [];
|
|
56
|
+
},
|
|
57
|
+
initialData: [],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function useCreateCourseExportMutation(courseId: string | null) {
|
|
62
|
+
const { request } = useApp();
|
|
63
|
+
const queryClient = useQueryClient();
|
|
64
|
+
|
|
65
|
+
return useMutation({
|
|
66
|
+
mutationFn: async (payload: {
|
|
67
|
+
format: 'scorm_1_2';
|
|
68
|
+
visualSettings?: ScormVisualSettings;
|
|
69
|
+
}) => {
|
|
70
|
+
const res = await request<{
|
|
71
|
+
exportId: number;
|
|
72
|
+
queueJobId: number;
|
|
73
|
+
notificationId: number;
|
|
74
|
+
}>({
|
|
75
|
+
url: `/lms/courses/${courseId}/exports`,
|
|
76
|
+
method: 'POST',
|
|
77
|
+
data: payload,
|
|
78
|
+
});
|
|
79
|
+
return res.data;
|
|
80
|
+
},
|
|
81
|
+
onSuccess: () => {
|
|
82
|
+
queryClient.invalidateQueries({
|
|
83
|
+
queryKey: courseExportsQueryKey(courseId ?? ''),
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function useDeleteCourseExportMutation(courseId: string | null) {
|
|
90
|
+
const { request } = useApp();
|
|
91
|
+
const queryClient = useQueryClient();
|
|
92
|
+
|
|
93
|
+
return useMutation({
|
|
94
|
+
mutationFn: async (exportId: number) => {
|
|
95
|
+
await request({
|
|
96
|
+
url: `/lms/courses/${courseId}/exports/${exportId}`,
|
|
97
|
+
method: 'DELETE',
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
onSuccess: () => {
|
|
101
|
+
queryClient.invalidateQueries({
|
|
102
|
+
queryKey: courseExportsQueryKey(courseId ?? ''),
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* onError: restore cache snapshot + Zustand rollback
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
23
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
24
24
|
import { useTranslations } from 'next-intl';
|
|
25
25
|
import { toast } from 'sonner';
|
|
26
26
|
|
|
@@ -54,6 +54,9 @@ import {
|
|
|
54
54
|
updateCourse as apiUpdateCourse,
|
|
55
55
|
updateLesson as apiUpdateLesson,
|
|
56
56
|
updateSession as apiUpdateSession,
|
|
57
|
+
listQuestions as apiListQuestions,
|
|
58
|
+
createQuestion as apiCreateQuestion,
|
|
59
|
+
type CreateQuestionPayload,
|
|
57
60
|
} from './services/course-structure.service';
|
|
58
61
|
import type { CourseStructureCacheData } from './use-course-structure-query';
|
|
59
62
|
import { courseStructureQueryKey } from './use-course-structure-query';
|
|
@@ -1104,3 +1107,27 @@ export function useUpdateResourceTypeMutation() {
|
|
|
1104
1107
|
},
|
|
1105
1108
|
});
|
|
1106
1109
|
}
|
|
1110
|
+
|
|
1111
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1112
|
+
// useQuestionsQuery / useCreateQuestionMutation
|
|
1113
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1114
|
+
|
|
1115
|
+
export function useQuestionsQuery(search?: string) {
|
|
1116
|
+
const { request } = useApp();
|
|
1117
|
+
return useQuery({
|
|
1118
|
+
queryKey: ['lms-questions', search ?? ''],
|
|
1119
|
+
queryFn: () => apiListQuestions(request, search),
|
|
1120
|
+
staleTime: 30_000,
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
export function useCreateQuestionMutation() {
|
|
1125
|
+
const { request } = useApp();
|
|
1126
|
+
const queryClient = useQueryClient();
|
|
1127
|
+
return useMutation({
|
|
1128
|
+
mutationFn: (dto: CreateQuestionPayload) => apiCreateQuestion(request, dto),
|
|
1129
|
+
onSuccess: () => {
|
|
1130
|
+
void queryClient.invalidateQueries({ queryKey: ['lms-questions'] });
|
|
1131
|
+
},
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
4
4
|
|
|
5
5
|
export interface LmsFeatureFlags {
|
|
6
|
-
videoConversionEnabled: boolean;
|
|
7
6
|
imageExtractionEnabled: boolean;
|
|
8
7
|
transcriptionEnabled: boolean;
|
|
9
8
|
youtubeEnabled: boolean;
|
|
@@ -11,7 +10,6 @@ export interface LmsFeatureFlags {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
const DEFAULT_FLAGS: LmsFeatureFlags = {
|
|
14
|
-
videoConversionEnabled: true,
|
|
15
13
|
imageExtractionEnabled: true,
|
|
16
14
|
transcriptionEnabled: true,
|
|
17
15
|
youtubeEnabled: true,
|
|
@@ -95,6 +95,7 @@ interface Curso {
|
|
|
95
95
|
criadoEm: string;
|
|
96
96
|
logoFileId?: number | null;
|
|
97
97
|
operationsProjectId?: number | null;
|
|
98
|
+
processando?: boolean;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
type ApiCourse = {
|
|
@@ -122,6 +123,7 @@ type ApiCourse = {
|
|
|
122
123
|
createdAt: string;
|
|
123
124
|
logoFileId?: number | null;
|
|
124
125
|
operationsProjectId?: number | null;
|
|
126
|
+
hasRunningJobs?: boolean;
|
|
125
127
|
};
|
|
126
128
|
|
|
127
129
|
type ApiCourseList = {
|
|
@@ -238,6 +240,7 @@ function mapApiCourse(course: ApiCourse): Curso {
|
|
|
238
240
|
criadoEm: course.createdAt ?? '',
|
|
239
241
|
logoFileId: course.logoFileId ?? null,
|
|
240
242
|
operationsProjectId: course.operationsProjectId ?? null,
|
|
243
|
+
processando: course.hasRunningJobs ?? false,
|
|
241
244
|
};
|
|
242
245
|
}
|
|
243
246
|
|
|
@@ -1405,6 +1408,27 @@ export default function CursosPage() {
|
|
|
1405
1408
|
</Badge>
|
|
1406
1409
|
</motion.div>
|
|
1407
1410
|
)}
|
|
1411
|
+
{curso.processando &&
|
|
1412
|
+
!deletingCourseIds.has(curso.id) && (
|
|
1413
|
+
<motion.div
|
|
1414
|
+
initial={{ opacity: 0, scale: 0.8 }}
|
|
1415
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
1416
|
+
exit={{ opacity: 0, scale: 0.8 }}
|
|
1417
|
+
transition={{
|
|
1418
|
+
type: 'spring',
|
|
1419
|
+
stiffness: 300,
|
|
1420
|
+
damping: 30,
|
|
1421
|
+
}}
|
|
1422
|
+
>
|
|
1423
|
+
<Badge
|
|
1424
|
+
variant="secondary"
|
|
1425
|
+
className="gap-1 rounded-full px-2 py-0.5 text-[10px]"
|
|
1426
|
+
>
|
|
1427
|
+
<Loader2 className="size-2.5 animate-spin" />
|
|
1428
|
+
Processando...
|
|
1429
|
+
</Badge>
|
|
1430
|
+
</motion.div>
|
|
1431
|
+
)}
|
|
1408
1432
|
</div>
|
|
1409
1433
|
</div>
|
|
1410
1434
|
</div>
|
|
@@ -1554,6 +1578,27 @@ export default function CursosPage() {
|
|
|
1554
1578
|
</Badge>
|
|
1555
1579
|
</motion.div>
|
|
1556
1580
|
)}
|
|
1581
|
+
{curso.processando &&
|
|
1582
|
+
!deletingCourseIds.has(curso.id) && (
|
|
1583
|
+
<motion.div
|
|
1584
|
+
initial={{ opacity: 0, scale: 0.8 }}
|
|
1585
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
1586
|
+
exit={{ opacity: 0, scale: 0.8 }}
|
|
1587
|
+
transition={{
|
|
1588
|
+
type: 'spring',
|
|
1589
|
+
stiffness: 300,
|
|
1590
|
+
damping: 30,
|
|
1591
|
+
}}
|
|
1592
|
+
>
|
|
1593
|
+
<Badge
|
|
1594
|
+
variant="secondary"
|
|
1595
|
+
className="gap-1 text-[11px]"
|
|
1596
|
+
>
|
|
1597
|
+
<Loader2 className="size-2.5 animate-spin" />
|
|
1598
|
+
Processando...
|
|
1599
|
+
</Badge>
|
|
1600
|
+
</motion.div>
|
|
1601
|
+
)}
|
|
1557
1602
|
{curso.destaque && (
|
|
1558
1603
|
<span className="inline-flex items-center gap-1 rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-[11px] font-medium text-amber-700">
|
|
1559
1604
|
<Star className="size-3 fill-amber-400 text-amber-400" />
|
|
@@ -579,6 +579,8 @@
|
|
|
579
579
|
"statusProgress": "{completed}/{total} required items completed",
|
|
580
580
|
"ready": "Ready to publish",
|
|
581
581
|
"notReady": "Not ready yet",
|
|
582
|
+
"publishButton": "Publish course",
|
|
583
|
+
"alreadyPublished": "Course already published",
|
|
582
584
|
"items": {
|
|
583
585
|
"title": "Commercial title filled",
|
|
584
586
|
"description": "Public description filled",
|
|
@@ -1197,36 +1199,32 @@
|
|
|
1197
1199
|
},
|
|
1198
1200
|
"addResource": "Add resource",
|
|
1199
1201
|
"noResources": "No resources added",
|
|
1200
|
-
"
|
|
1201
|
-
"
|
|
1202
|
-
"
|
|
1203
|
-
"
|
|
1204
|
-
"loadingVideoProfiles": "Loading course video profiles...",
|
|
1205
|
-
"videoProfilesLoadError": "Could not load the course video profiles.",
|
|
1206
|
-
"retryLoadVideoProfiles": "Try loading again",
|
|
1207
|
-
"noVideoProfilesConfigured": "Configure video profiles in the course Resolutions tab before attaching File Storage videos.",
|
|
1202
|
+
"hlsStatusTitle": "HLS Stream",
|
|
1203
|
+
"hlsStatusReady": "Stream available for students.",
|
|
1204
|
+
"hlsStatusProcessing": "Processing video...",
|
|
1205
|
+
"hlsStatusPending": "No video processed yet.",
|
|
1208
1206
|
"originalVideoTitle": "Original high-resolution video",
|
|
1209
|
-
"originalVideoHint": "Upload an original video to automatically generate the
|
|
1210
|
-
"originalVideoPurpose": "This original file is only used to generate the
|
|
1211
|
-
"fileStorageVideoHint": "Use the original video below to automatically generate the
|
|
1212
|
-
"uploadOriginalForConversion": "Upload
|
|
1207
|
+
"originalVideoHint": "Upload an original video to automatically generate the HLS stream for this course.",
|
|
1208
|
+
"originalVideoPurpose": "This original file is only used to generate the adaptive streaming for this course.",
|
|
1209
|
+
"fileStorageVideoHint": "Use the original video below to automatically generate the HLS stream for this course.",
|
|
1210
|
+
"uploadOriginalForConversion": "Upload video for processing",
|
|
1213
1211
|
"replaceOriginalForConversion": "Replace original video",
|
|
1214
1212
|
"retryConversionWithSavedOriginal": "Start a new job with the saved original",
|
|
1215
|
-
"videoUploadBlockedWhileProcessing": "Original uploads stay blocked while the current
|
|
1213
|
+
"videoUploadBlockedWhileProcessing": "Original uploads stay blocked while the current processing job is still running.",
|
|
1216
1214
|
"videoJobStateLoading": "Waiting for the latest job status before allowing new actions.",
|
|
1217
1215
|
"videoProviderSavePending": "Wait for the provider to be saved before uploading the original video.",
|
|
1218
1216
|
"videoUploadMaxSizeError": "The video exceeds the {size} limit.",
|
|
1219
|
-
"videoConversionQueued": "Video
|
|
1220
|
-
"videoConversionFailed": "Could not queue the video
|
|
1217
|
+
"videoConversionQueued": "Video processing queued as job #{id}.",
|
|
1218
|
+
"videoConversionFailed": "Could not queue the video for processing.",
|
|
1221
1219
|
"videoConversionRetryFailed": "Could not start a new job with the saved original video.",
|
|
1222
|
-
"videoConversionJob": "
|
|
1223
|
-
"videoJobFeedbackTitle": "
|
|
1224
|
-
"videoJobToggleDetails": "Toggle
|
|
1225
|
-
"videoJobCollapsedSummary": "
|
|
1226
|
-
"videoJobLoading": "Loading
|
|
1227
|
-
"videoJobPendingLoad": "Preparing
|
|
1228
|
-
"awaitingConversion": "Awaiting
|
|
1229
|
-
"videoJobLoadError": "Could not load the
|
|
1220
|
+
"videoConversionJob": "Processing queued as job #{id}",
|
|
1221
|
+
"videoJobFeedbackTitle": "Processing status",
|
|
1222
|
+
"videoJobToggleDetails": "Toggle processing details",
|
|
1223
|
+
"videoJobCollapsedSummary": "Processing completed successfully. HLS streaming is now available for students.",
|
|
1224
|
+
"videoJobLoading": "Loading processing progress...",
|
|
1225
|
+
"videoJobPendingLoad": "Preparing processing job tracking...",
|
|
1226
|
+
"awaitingConversion": "Awaiting processing...",
|
|
1227
|
+
"videoJobLoadError": "Could not load the processing progress.",
|
|
1230
1228
|
"retryLoadVideoJob": "Try again",
|
|
1231
1229
|
"videoJobIdLabel": "Job",
|
|
1232
1230
|
"videoJobAttemptsLabel": "Attempts",
|
|
@@ -1242,7 +1240,6 @@
|
|
|
1242
1240
|
"videoJobRecentEvents": "Recent events",
|
|
1243
1241
|
"videoJobTranscriptionEvents": "Recent transcription events",
|
|
1244
1242
|
"videoJobNoEvents": "No events recorded yet.",
|
|
1245
|
-
"videoProfilesLockedWhileProcessing": "Profile video uploads stay locked while the original conversion job is active.",
|
|
1246
1243
|
"videoJobStatuses": {
|
|
1247
1244
|
"pending": "Queued",
|
|
1248
1245
|
"scheduled": "Scheduled",
|
|
@@ -1274,8 +1271,9 @@
|
|
|
1274
1271
|
},
|
|
1275
1272
|
"videoJobProgress": {
|
|
1276
1273
|
"download_original": "Downloading original video...",
|
|
1277
|
-
"probe_duration": "Reading video duration...",
|
|
1278
|
-
"
|
|
1274
|
+
"probe_duration": "Reading video dimensions and duration...",
|
|
1275
|
+
"hls_encode": "Generating HLS stream...",
|
|
1276
|
+
"hls_upload": "Uploading HLS segments to storage...",
|
|
1279
1277
|
"extract_frames": "Extracting video frames...",
|
|
1280
1278
|
"extract_frames_done": "Frames extracted: {count}.",
|
|
1281
1279
|
"extract_audio": "Extracting video audio...",
|
|
@@ -1342,8 +1340,7 @@
|
|
|
1342
1340
|
"types": {
|
|
1343
1341
|
"video": "Video",
|
|
1344
1342
|
"post": "Post",
|
|
1345
|
-
"questao": "Question"
|
|
1346
|
-
"exercicio": "Exercise"
|
|
1343
|
+
"questao": "Question"
|
|
1347
1344
|
},
|
|
1348
1345
|
"statuses": {
|
|
1349
1346
|
"preparada": "Prepared",
|
|
@@ -1382,6 +1379,7 @@
|
|
|
1382
1379
|
"videoUploadFailed": "Failed to upload {count} video file(s).",
|
|
1383
1380
|
"newQuestion": "New question",
|
|
1384
1381
|
"editQuestion": "Edit question",
|
|
1382
|
+
"loadingQuestions": "Loading questions...",
|
|
1385
1383
|
"selectQuestion": "Select question...",
|
|
1386
1384
|
"searchQuestion": "Search question...",
|
|
1387
1385
|
"noQuestionsFound": "No questions found",
|