@hed-hog/lms 0.0.355 → 0.0.358
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/course/course-audio-transcription.service.d.ts.map +1 -1
- package/dist/course/course-audio-transcription.service.js +15 -7
- package/dist/course/course-audio-transcription.service.js.map +1 -1
- package/dist/course/course-operations-integration.service.d.ts +31 -0
- package/dist/course/course-operations-integration.service.d.ts.map +1 -1
- package/dist/course/course-operations-integration.service.js +286 -22
- package/dist/course/course-operations-integration.service.js.map +1 -1
- package/dist/course/course-operations.controller.d.ts +10 -0
- package/dist/course/course-operations.controller.d.ts.map +1 -0
- package/dist/course/course-operations.controller.js +67 -0
- package/dist/course/course-operations.controller.js.map +1 -0
- package/dist/course/course-structure.controller.d.ts +15 -1
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.service.d.ts +25 -1
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +160 -24
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +4 -1
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +4 -2
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +61 -2
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +3 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +15 -0
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course-structure-session.dto.d.ts +1 -0
- package/dist/course/dto/create-course-structure-session.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-session.dto.js +5 -0
- package/dist/course/dto/create-course-structure-session.dto.js.map +1 -1
- package/dist/course/dto/update-course-operations-config.dto.d.ts +6 -0
- package/dist/course/dto/update-course-operations-config.dto.d.ts.map +1 -0
- package/dist/course/dto/update-course-operations-config.dto.js +33 -0
- package/dist/course/dto/update-course-operations-config.dto.js.map +1 -0
- package/dist/course/lms-operations-task.subscriber.d.ts +13 -0
- package/dist/course/lms-operations-task.subscriber.d.ts.map +1 -0
- package/dist/course/lms-operations-task.subscriber.js +57 -0
- package/dist/course/lms-operations-task.subscriber.js.map +1 -0
- package/dist/enterprise/enterprise.service.js +1 -1
- package/dist/enterprise/enterprise.service.js.map +1 -1
- package/dist/enterprise/training/training-student.controller.d.ts +0 -95
- package/dist/enterprise/training/training-student.controller.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.controller.js +1 -34
- package/dist/enterprise/training/training-student.controller.js.map +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +63 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +320 -4
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/instructor/instructor.service.d.ts.map +1 -1
- package/dist/instructor/instructor.service.js +12 -3
- package/dist/instructor/instructor.service.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +2 -0
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/platforma.controller.d.ts +287 -0
- package/dist/platforma/platforma.controller.d.ts.map +1 -0
- package/dist/platforma/platforma.controller.js +147 -0
- package/dist/platforma/platforma.controller.js.map +1 -0
- package/hedhog/data/route.yaml +102 -9
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +10 -13
- package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +26 -4
- package/hedhog/frontend/app/_lib/editor/types.ts.ejs +6 -0
- package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +447 -31
- package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +59 -47
- package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +201 -35
- package/hedhog/frontend/app/certificates/models/TemplateEditorPage.tsx.ejs +36 -5
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +17 -6
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +382 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +45 -8
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +177 -67
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +25 -60
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +11 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +22 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +79 -64
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +31 -14
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +4 -4
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +39 -27
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +24 -2
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +41 -6
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-query.ts.ejs +2 -2
- package/hedhog/frontend/app/courses/page.tsx.ejs +80 -103
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +1 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +18 -6
- package/hedhog/frontend/app/exams/[id]/page.tsx.ejs +5 -4
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +16 -10
- package/hedhog/frontend/app/paths/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/training/page.tsx.ejs +1 -1
- package/hedhog/frontend/messages/en.json +7 -2
- package/hedhog/frontend/messages/pt.json +7 -2
- package/hedhog/table/course_lesson.yaml +2 -2
- package/hedhog/table/course_module.yaml +3 -0
- package/package.json +8 -8
- package/src/course/course-audio-transcription.service.ts +21 -8
- package/src/course/course-operations-integration.service.ts +460 -22
- package/src/course/course-operations.controller.ts +45 -0
- package/src/course/course-structure.service.ts +209 -4
- package/src/course/course.module.ts +4 -1
- package/src/course/course.service.ts +67 -1
- package/src/course/dto/create-course-structure-lesson.dto.ts +17 -0
- package/src/course/dto/create-course-structure-session.dto.ts +13 -1
- package/src/course/dto/update-course-operations-config.dto.ts +16 -0
- package/src/course/lms-operations-task.subscriber.ts +44 -0
- package/src/enterprise/enterprise.service.ts +1 -1
- package/src/enterprise/training/training-student.controller.ts +3 -27
- package/src/enterprise/training/training-student.service.ts +350 -2
- package/src/instructor/instructor.service.ts +12 -3
- package/src/lms.module.ts +2 -0
- package/src/platforma/platforma.controller.ts +92 -0
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
Film,
|
|
17
17
|
HelpCircle,
|
|
18
18
|
Loader2,
|
|
19
|
-
Lock,
|
|
20
19
|
Paperclip,
|
|
21
20
|
ScrollText,
|
|
22
21
|
Video,
|
|
@@ -32,7 +31,7 @@ import {
|
|
|
32
31
|
} from '../_data/services/course-structure.service';
|
|
33
32
|
import { HighlightedText } from './highlighted-text';
|
|
34
33
|
import { useStructureStore } from './store';
|
|
35
|
-
import type { Lesson, LessonStatus, LessonType
|
|
34
|
+
import type { Lesson, LessonStatus, LessonType } from './types';
|
|
36
35
|
import {
|
|
37
36
|
getLessonAttachedResourceCount,
|
|
38
37
|
getLessonUploadedVideoCount,
|
|
@@ -63,15 +62,6 @@ const STATUS_CONFIG: Record<LessonStatus, { label: string; dot: string }> = {
|
|
|
63
62
|
publicada: { label: 'Publicada', dot: 'bg-emerald-500' },
|
|
64
63
|
};
|
|
65
64
|
|
|
66
|
-
const VISIBILITY_CONFIG: Record<
|
|
67
|
-
Visibility,
|
|
68
|
-
{ icon: LucideIcon; color: string; label: string }
|
|
69
|
-
> = {
|
|
70
|
-
publico: { icon: Eye, color: 'text-emerald-500', label: 'Público' },
|
|
71
|
-
privado: { icon: EyeOff, color: 'text-muted-foreground', label: 'Privado' },
|
|
72
|
-
restrito: { icon: Lock, color: 'text-amber-500', label: 'Restrito' },
|
|
73
|
-
};
|
|
74
|
-
|
|
75
65
|
// ── Props ─────────────────────────────────────────────────────────────────────
|
|
76
66
|
|
|
77
67
|
interface TreeRowLessonProps {
|
|
@@ -98,9 +88,7 @@ export function TreeRowLesson({
|
|
|
98
88
|
const { request } = useApp();
|
|
99
89
|
|
|
100
90
|
const statusCfg = data.status ? STATUS_CONFIG[data.status] : null;
|
|
101
|
-
const
|
|
102
|
-
? VISIBILITY_CONFIG[data.visibility]
|
|
103
|
-
: null;
|
|
91
|
+
const isPublished = data.published === true;
|
|
104
92
|
|
|
105
93
|
const {
|
|
106
94
|
showStatusDot,
|
|
@@ -171,7 +159,6 @@ export function TreeRowLesson({
|
|
|
171
159
|
publicDescription: data.publicDescription,
|
|
172
160
|
privateDescription: data.privateDescription,
|
|
173
161
|
status: data.status,
|
|
174
|
-
visibility: data.visibility,
|
|
175
162
|
videoProvider: data.videoProvider,
|
|
176
163
|
videoUrl: data.videoUrl,
|
|
177
164
|
autoDuration: data.autoDuration,
|
|
@@ -215,24 +202,32 @@ export function TreeRowLesson({
|
|
|
215
202
|
data.type !== 'video' ? (
|
|
216
203
|
<Icon className={cn('size-3.5 shrink-0', cfg.color)} aria-hidden />
|
|
217
204
|
) : videoJobVisualState === 'waiting' ? (
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
205
|
+
<Tooltip>
|
|
206
|
+
<TooltipTrigger asChild>
|
|
207
|
+
<span
|
|
208
|
+
aria-label="Conversão aguardando processamento"
|
|
209
|
+
className="inline-flex items-center"
|
|
210
|
+
>
|
|
211
|
+
<Clock3 className="size-3.5 shrink-0 text-amber-500" aria-hidden />
|
|
212
|
+
</span>
|
|
213
|
+
</TooltipTrigger>
|
|
214
|
+
<TooltipContent>Conversão aguardando processamento</TooltipContent>
|
|
215
|
+
</Tooltip>
|
|
225
216
|
) : videoJobVisualState === 'processing' ? (
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
217
|
+
<Tooltip>
|
|
218
|
+
<TooltipTrigger asChild>
|
|
219
|
+
<span
|
|
220
|
+
aria-label="Conversão em processamento"
|
|
221
|
+
className="inline-flex items-center"
|
|
222
|
+
>
|
|
223
|
+
<Loader2
|
|
224
|
+
className="size-3.5 shrink-0 animate-spin text-blue-500"
|
|
225
|
+
aria-hidden
|
|
226
|
+
/>
|
|
227
|
+
</span>
|
|
228
|
+
</TooltipTrigger>
|
|
229
|
+
<TooltipContent>Conversão em processamento</TooltipContent>
|
|
230
|
+
</Tooltip>
|
|
236
231
|
) : (
|
|
237
232
|
<Video className={cn('size-3.5 shrink-0', cfg.color)} aria-hidden />
|
|
238
233
|
);
|
|
@@ -246,7 +241,7 @@ export function TreeRowLesson({
|
|
|
246
241
|
}}
|
|
247
242
|
role="treeitem"
|
|
248
243
|
aria-selected={isActive}
|
|
249
|
-
|
|
244
|
+
aria-label={`${cfg.label} · ${data.duration}min`}
|
|
250
245
|
className={cn(
|
|
251
246
|
'flex items-center gap-1.5 pl-7 pr-2 rounded-md cursor-pointer select-none text-sm h-full group',
|
|
252
247
|
'transition-colors duration-100',
|
|
@@ -290,39 +285,55 @@ export function TreeRowLesson({
|
|
|
290
285
|
{durationLabel}
|
|
291
286
|
</span>
|
|
292
287
|
{/* Visibility icon */}
|
|
293
|
-
{showVisibility &&
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const VisIcon = visibilityCfg.icon;
|
|
297
|
-
return (
|
|
288
|
+
{showVisibility && (
|
|
289
|
+
<Tooltip>
|
|
290
|
+
<TooltipTrigger asChild>
|
|
298
291
|
<span
|
|
299
|
-
|
|
300
|
-
aria-label={t('visibility', { value: visibilityCfg.label })}
|
|
292
|
+
aria-label={isPublished ? t('published') : t('draft')}
|
|
301
293
|
className="inline-flex items-center"
|
|
302
294
|
>
|
|
303
|
-
|
|
304
|
-
className=
|
|
305
|
-
|
|
295
|
+
{isPublished ? (
|
|
296
|
+
<Eye className="size-3 shrink-0 text-emerald-600 dark:text-emerald-400" />
|
|
297
|
+
) : (
|
|
298
|
+
<EyeOff className="size-3 shrink-0 text-muted-foreground" />
|
|
299
|
+
)}
|
|
306
300
|
</span>
|
|
307
|
-
|
|
308
|
-
|
|
301
|
+
</TooltipTrigger>
|
|
302
|
+
<TooltipContent>
|
|
303
|
+
{isPublished ? t('published') : t('draft')}
|
|
304
|
+
</TooltipContent>
|
|
305
|
+
</Tooltip>
|
|
306
|
+
)}
|
|
309
307
|
{/* Status dot */}
|
|
310
308
|
{showStatusDot && statusCfg && (
|
|
311
|
-
<
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
309
|
+
<Tooltip>
|
|
310
|
+
<TooltipTrigger asChild>
|
|
311
|
+
<span
|
|
312
|
+
className={cn(
|
|
313
|
+
'size-1.5 rounded-full shrink-0',
|
|
314
|
+
statusCfg.dot
|
|
315
|
+
)}
|
|
316
|
+
aria-label={t('status', { value: statusCfg.label })}
|
|
317
|
+
/>
|
|
318
|
+
</TooltipTrigger>
|
|
319
|
+
<TooltipContent>
|
|
320
|
+
{t('status', { value: statusCfg.label })}
|
|
321
|
+
</TooltipContent>
|
|
322
|
+
</Tooltip>
|
|
316
323
|
)}
|
|
317
324
|
{/* Video linked indicator */}
|
|
318
325
|
{showVideoIndicator && data.type === 'video' && data.videoUrl && (
|
|
319
|
-
<
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
+
<Tooltip>
|
|
327
|
+
<TooltipTrigger asChild>
|
|
328
|
+
<span
|
|
329
|
+
aria-label={t('videoLinked')}
|
|
330
|
+
className="inline-flex items-center"
|
|
331
|
+
>
|
|
332
|
+
<Film className="size-3 shrink-0 text-violet-500" />
|
|
333
|
+
</span>
|
|
334
|
+
</TooltipTrigger>
|
|
335
|
+
<TooltipContent>{t('videoLinked')}</TooltipContent>
|
|
336
|
+
</Tooltip>
|
|
326
337
|
)}
|
|
327
338
|
{/* Uploaded videos indicator */}
|
|
328
339
|
{showVideoIndicator && uploadedVideoCount > 0 && (
|
|
@@ -364,13 +375,17 @@ export function TreeRowLesson({
|
|
|
364
375
|
{showTranscriptionIndicator &&
|
|
365
376
|
data.type === 'video' &&
|
|
366
377
|
data.transcription && (
|
|
367
|
-
<
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
378
|
+
<Tooltip>
|
|
379
|
+
<TooltipTrigger asChild>
|
|
380
|
+
<span
|
|
381
|
+
aria-label={t('hasTranscription')}
|
|
382
|
+
className="inline-flex items-center"
|
|
383
|
+
>
|
|
384
|
+
<ScrollText className="size-3 shrink-0 text-emerald-500" />
|
|
385
|
+
</span>
|
|
386
|
+
</TooltipTrigger>
|
|
387
|
+
<TooltipContent>{t('hasTranscription')}</TooltipContent>
|
|
388
|
+
</Tooltip>
|
|
374
389
|
)}
|
|
375
390
|
</div>
|
|
376
391
|
)}
|
|
@@ -10,6 +10,8 @@ import { cn } from '@/lib/utils';
|
|
|
10
10
|
import {
|
|
11
11
|
ChevronDown,
|
|
12
12
|
ChevronRight,
|
|
13
|
+
Eye,
|
|
14
|
+
EyeOff,
|
|
13
15
|
Layers,
|
|
14
16
|
Paperclip,
|
|
15
17
|
Video,
|
|
@@ -54,7 +56,7 @@ export function TreeRowSession({
|
|
|
54
56
|
const cancelRename = useStructureStore((s) => s.cancelRename);
|
|
55
57
|
const startRename = useStructureStore((s) => s.startRename);
|
|
56
58
|
const renameItem = useStructureStore((s) => s.renameItem);
|
|
57
|
-
const { showCode } = useTreeDisplaySettings();
|
|
59
|
+
const { showCode, showVisibility } = useTreeDisplaySettings();
|
|
58
60
|
|
|
59
61
|
const updateSession = useUpdateSessionMutation();
|
|
60
62
|
|
|
@@ -79,8 +81,7 @@ export function TreeRowSession({
|
|
|
79
81
|
duration: data.duration,
|
|
80
82
|
code: data.code,
|
|
81
83
|
description: '',
|
|
82
|
-
|
|
83
|
-
published: data.published ?? false,
|
|
84
|
+
published: data.published,
|
|
84
85
|
},
|
|
85
86
|
});
|
|
86
87
|
}
|
|
@@ -96,6 +97,7 @@ export function TreeRowSession({
|
|
|
96
97
|
|
|
97
98
|
const durationH = Math.floor((data.duration ?? 0) / 60);
|
|
98
99
|
const durationM = (data.duration ?? 0) % 60;
|
|
100
|
+
const isPublished = data.published === true;
|
|
99
101
|
const durationLabel =
|
|
100
102
|
durationH > 0
|
|
101
103
|
? `${durationH}h${durationM > 0 ? ` ${durationM}m` : ''}`
|
|
@@ -138,7 +140,12 @@ export function TreeRowSession({
|
|
|
138
140
|
|
|
139
141
|
{/* Session icon */}
|
|
140
142
|
<Layers
|
|
141
|
-
className=
|
|
143
|
+
className={cn(
|
|
144
|
+
'size-3.5 shrink-0 transition-colors',
|
|
145
|
+
isPublished
|
|
146
|
+
? 'text-emerald-600 dark:text-emerald-400'
|
|
147
|
+
: 'text-muted-foreground/70'
|
|
148
|
+
)}
|
|
142
149
|
aria-hidden
|
|
143
150
|
/>
|
|
144
151
|
|
|
@@ -162,6 +169,26 @@ export function TreeRowSession({
|
|
|
162
169
|
{/* Meta badges (hidden during rename) */}
|
|
163
170
|
{!isRenaming && (
|
|
164
171
|
<div className="flex items-center gap-1 shrink-0">
|
|
172
|
+
{showVisibility && (
|
|
173
|
+
<Tooltip>
|
|
174
|
+
<TooltipTrigger asChild>
|
|
175
|
+
<span
|
|
176
|
+
className="inline-flex items-center"
|
|
177
|
+
aria-label={isPublished ? t('published') : t('draft')}
|
|
178
|
+
>
|
|
179
|
+
{isPublished ? (
|
|
180
|
+
<Eye className="size-3 shrink-0 text-emerald-600 dark:text-emerald-400" />
|
|
181
|
+
) : (
|
|
182
|
+
<EyeOff className="size-3 shrink-0 text-muted-foreground" />
|
|
183
|
+
)}
|
|
184
|
+
</span>
|
|
185
|
+
</TooltipTrigger>
|
|
186
|
+
<TooltipContent>
|
|
187
|
+
{isPublished ? t('published') : t('draft')}
|
|
188
|
+
</TooltipContent>
|
|
189
|
+
</Tooltip>
|
|
190
|
+
)}
|
|
191
|
+
|
|
165
192
|
{showCode && (
|
|
166
193
|
<span className="inline-flex items-center rounded border border-border/60 bg-muted/60 px-1 text-[0.6rem] font-mono text-muted-foreground leading-4">
|
|
167
194
|
{data.code}
|
|
@@ -207,16 +234,6 @@ export function TreeRowSession({
|
|
|
207
234
|
</TooltipContent>
|
|
208
235
|
</Tooltip>
|
|
209
236
|
)}
|
|
210
|
-
{/* Published status dot */}
|
|
211
|
-
<span
|
|
212
|
-
className={cn(
|
|
213
|
-
'size-1.5 rounded-full shrink-0',
|
|
214
|
-
data.published === false
|
|
215
|
-
? 'bg-muted-foreground/30'
|
|
216
|
-
: 'bg-emerald-500'
|
|
217
|
-
)}
|
|
218
|
-
title={data.published === false ? t('draft') : t('published')}
|
|
219
|
-
/>
|
|
220
237
|
</div>
|
|
221
238
|
)}
|
|
222
239
|
</div>
|
|
@@ -35,8 +35,7 @@ export interface Session {
|
|
|
35
35
|
description?: string;
|
|
36
36
|
duration: number;
|
|
37
37
|
order: number;
|
|
38
|
-
|
|
39
|
-
published?: boolean;
|
|
38
|
+
published: boolean;
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
export interface Resource {
|
|
@@ -63,6 +62,7 @@ export interface LessonInstructor {
|
|
|
63
62
|
id: string;
|
|
64
63
|
role?: 'lead' | 'assistant';
|
|
65
64
|
name?: string;
|
|
65
|
+
avatarId?: number | null;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export interface TranscriptionSegment {
|
|
@@ -95,6 +95,7 @@ export interface Lesson {
|
|
|
95
95
|
order: number;
|
|
96
96
|
status?: LessonStatus;
|
|
97
97
|
visibility?: Visibility;
|
|
98
|
+
published?: boolean;
|
|
98
99
|
// Video
|
|
99
100
|
videoProvider?: VideoProvider;
|
|
100
101
|
videoUrl?: string;
|
|
@@ -127,7 +128,6 @@ export type SessionFormValues = {
|
|
|
127
128
|
title: string;
|
|
128
129
|
description: string;
|
|
129
130
|
duration: number;
|
|
130
|
-
visibility: Visibility;
|
|
131
131
|
published: boolean;
|
|
132
132
|
};
|
|
133
133
|
|
|
@@ -139,7 +139,7 @@ export type LessonFormValues = {
|
|
|
139
139
|
type: LessonType;
|
|
140
140
|
duration: number;
|
|
141
141
|
status: LessonStatus;
|
|
142
|
-
|
|
142
|
+
published: boolean;
|
|
143
143
|
videoProvider?: VideoProvider;
|
|
144
144
|
videoUrl?: string;
|
|
145
145
|
autoDuration?: boolean;
|
package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs
CHANGED
|
@@ -16,9 +16,7 @@
|
|
|
16
16
|
* 1. exameVinculado (number) ↔ linkedExam (string) — converted with String()/Number()
|
|
17
17
|
* 2. 'exercicio' tipo — exists in backend and is now part of frontend LessonType.
|
|
18
18
|
* 3. Session.order — not returned by API; inferred from sorted array position.
|
|
19
|
-
* 4.
|
|
20
|
-
* 5. Lesson.status / visibility — not in DB schema; left undefined.
|
|
21
|
-
* 6. Resource.size / url — not returned by API; left as empty string / undefined.
|
|
19
|
+
* 4. Resource.size / url — not returned by API; left as empty string / undefined.
|
|
22
20
|
*/
|
|
23
21
|
|
|
24
22
|
import type {
|
|
@@ -125,7 +123,7 @@ function normalizeFrame(raw: ApiLessonFrame): VideoFrame {
|
|
|
125
123
|
export function normalizeCourse(raw: ApiCourse): Course {
|
|
126
124
|
return {
|
|
127
125
|
id: raw.id,
|
|
128
|
-
code: raw.slug
|
|
126
|
+
code: raw.codigo ?? raw.code ?? raw.slug ?? raw.id,
|
|
129
127
|
name: raw.name ?? raw.slug,
|
|
130
128
|
title: raw.titulo,
|
|
131
129
|
description: raw.descricao,
|
|
@@ -154,12 +152,7 @@ export function normalizeSession(raw: ApiSession, index: number): Session {
|
|
|
154
152
|
* TODO[BACKEND]: Include `order` in the ApiSession response.
|
|
155
153
|
*/
|
|
156
154
|
order: index + 1,
|
|
157
|
-
|
|
158
|
-
* ⚠️ BACKEND NOTE: visibility and published are not in the backend schema.
|
|
159
|
-
* Defaulted here. If these fields are added to the DB, read them from `raw`.
|
|
160
|
-
*/
|
|
161
|
-
visibility: 'publico',
|
|
162
|
-
published: false,
|
|
155
|
+
published: raw.published ?? false,
|
|
163
156
|
};
|
|
164
157
|
}
|
|
165
158
|
|
|
@@ -171,7 +164,11 @@ export function normalizeSession(raw: ApiSession, index: number): Session {
|
|
|
171
164
|
*/
|
|
172
165
|
export function normalizeLesson(raw: ApiLesson, order = 0): Lesson {
|
|
173
166
|
const instructors: LessonInstructor[] = (raw.instrutores ?? []).map(
|
|
174
|
-
(inst) => ({
|
|
167
|
+
(inst) => ({
|
|
168
|
+
id: String(inst.id),
|
|
169
|
+
name: inst.name ?? inst.nome,
|
|
170
|
+
avatarId: inst.avatarId ?? null,
|
|
171
|
+
})
|
|
175
172
|
);
|
|
176
173
|
|
|
177
174
|
return {
|
|
@@ -200,12 +197,9 @@ export function normalizeLesson(raw: ApiLesson, order = 0): Lesson {
|
|
|
200
197
|
resources: (raw.recursos ?? []).map(normalizeResource),
|
|
201
198
|
frames: (raw.frames ?? []).map(normalizeFrame),
|
|
202
199
|
instructors,
|
|
203
|
-
|
|
204
|
-
* ⚠️ BACKEND NOTE: status and visibility are not in the DB schema.
|
|
205
|
-
* Left undefined. If added to the schema, read them from `raw`.
|
|
206
|
-
*/
|
|
207
|
-
status: undefined,
|
|
200
|
+
status: raw.statusProducao,
|
|
208
201
|
visibility: undefined,
|
|
202
|
+
published: raw.published ?? false,
|
|
209
203
|
};
|
|
210
204
|
}
|
|
211
205
|
|
|
@@ -220,7 +214,7 @@ export function normalizeStructureResponse(raw: ApiGetStructureResponse): {
|
|
|
220
214
|
course: Course | null;
|
|
221
215
|
sessions: Session[];
|
|
222
216
|
lessons: Lesson[];
|
|
223
|
-
instructors: { id: number; name: string }[];
|
|
217
|
+
instructors: { id: number; name: string; avatarId?: number | null }[];
|
|
224
218
|
} {
|
|
225
219
|
const sessions = raw.sessoes.map((s, i) => normalizeSession(s, i));
|
|
226
220
|
|
|
@@ -237,7 +231,11 @@ export function normalizeStructureResponse(raw: ApiGetStructureResponse): {
|
|
|
237
231
|
course: raw.curso ? normalizeCourse(raw.curso) : null,
|
|
238
232
|
sessions,
|
|
239
233
|
lessons,
|
|
240
|
-
instructors: raw.instructors ?? []
|
|
234
|
+
instructors: (raw.instructors ?? []).map((instructor) => ({
|
|
235
|
+
id: Number(instructor.id),
|
|
236
|
+
name: instructor.name ?? instructor.nome ?? '',
|
|
237
|
+
avatarId: instructor.avatarId ?? null,
|
|
238
|
+
})),
|
|
241
239
|
};
|
|
242
240
|
}
|
|
243
241
|
|
|
@@ -249,17 +247,17 @@ export function normalizeStructureResponse(raw: ApiGetStructureResponse): {
|
|
|
249
247
|
* Convert SessionFormValues → ApiCreateSessionPayload.
|
|
250
248
|
* Used for POST /lms/courses/:id/structure/sessions.
|
|
251
249
|
*
|
|
252
|
-
* ⚠️ ADAPTER POINT:
|
|
253
|
-
* `visibility` and `published` from SessionFormValues are intentionally
|
|
254
|
-
* omitted — the backend schema does not have these fields yet.
|
|
255
|
-
* TODO[BACKEND]: Add visibility/published to course_module if required.
|
|
256
250
|
*/
|
|
257
251
|
export function toCreateSessionPayload(
|
|
258
|
-
data: Pick<
|
|
252
|
+
data: Pick<
|
|
253
|
+
SessionFormValues,
|
|
254
|
+
'title' | 'duration' | 'description' | 'code' | 'published'
|
|
255
|
+
>
|
|
259
256
|
): ApiCreateSessionPayload {
|
|
260
257
|
return {
|
|
261
258
|
titulo: data.title,
|
|
262
259
|
duracao: data.duration,
|
|
260
|
+
published: data.published,
|
|
263
261
|
...(data.description?.trim() && { descricao: data.description }),
|
|
264
262
|
...(data.code?.trim() && { codigo: data.code }),
|
|
265
263
|
};
|
|
@@ -271,7 +269,10 @@ export function toCreateSessionPayload(
|
|
|
271
269
|
*/
|
|
272
270
|
export function toUpdateSessionPayload(
|
|
273
271
|
data: Partial<
|
|
274
|
-
Pick<
|
|
272
|
+
Pick<
|
|
273
|
+
SessionFormValues,
|
|
274
|
+
'title' | 'duration' | 'description' | 'code' | 'published'
|
|
275
|
+
>
|
|
275
276
|
>
|
|
276
277
|
): ApiUpdateSessionPayload {
|
|
277
278
|
const payload: ApiUpdateSessionPayload = {};
|
|
@@ -279,6 +280,7 @@ export function toUpdateSessionPayload(
|
|
|
279
280
|
if (data.duration !== undefined) payload.duracao = data.duration;
|
|
280
281
|
if (data.description !== undefined) payload.descricao = data.description;
|
|
281
282
|
if (data.code !== undefined) payload.codigo = data.code;
|
|
283
|
+
if (data.published !== undefined) payload.published = data.published;
|
|
282
284
|
return payload;
|
|
283
285
|
}
|
|
284
286
|
|
|
@@ -286,9 +288,7 @@ export function toUpdateSessionPayload(
|
|
|
286
288
|
* Convert LessonFormValues → ApiCreateLessonPayload.
|
|
287
289
|
* Used for POST /lms/courses/:id/structure/sessions/:sessionId/lessons.
|
|
288
290
|
*
|
|
289
|
-
*
|
|
290
|
-
* `status` and `visibility` from LessonFormValues are intentionally omitted
|
|
291
|
-
* (not in the DB schema). `linkedExam` (string) is converted to number.
|
|
291
|
+
* `linkedExam` (string) is converted to number.
|
|
292
292
|
*/
|
|
293
293
|
export function toCreateLessonPayload(
|
|
294
294
|
data: LessonFormValues & {
|
|
@@ -296,6 +296,7 @@ export function toCreateLessonPayload(
|
|
|
296
296
|
videoConversionJobId?: number;
|
|
297
297
|
instructorIds?: number[];
|
|
298
298
|
resources?: LessonResourceInput[];
|
|
299
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
299
300
|
}
|
|
300
301
|
): ApiCreateLessonPayload {
|
|
301
302
|
return {
|
|
@@ -304,6 +305,11 @@ export function toCreateLessonPayload(
|
|
|
304
305
|
// backend accepts it. Cast is safe as long as LessonType ⊆ ApiLesson['tipo'].
|
|
305
306
|
tipo: data.type as ApiCreateLessonPayload['tipo'],
|
|
306
307
|
duracao: data.duration,
|
|
308
|
+
published: data.published,
|
|
309
|
+
statusProducao: data.status,
|
|
310
|
+
...(data.confirmarPublicacaoComStatus !== undefined && {
|
|
311
|
+
confirmarPublicacaoComStatus: data.confirmarPublicacaoComStatus,
|
|
312
|
+
}),
|
|
307
313
|
...(data.publicDescription && { descricaoPublica: data.publicDescription }),
|
|
308
314
|
...(data.privateDescription && {
|
|
309
315
|
descricaoPrivada: data.privateDescription,
|
|
@@ -349,6 +355,7 @@ export function toUpdateLessonPayload(
|
|
|
349
355
|
videoConversionJobId?: number;
|
|
350
356
|
instructorIds?: number[];
|
|
351
357
|
resources?: LessonResourceInput[];
|
|
358
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
352
359
|
}
|
|
353
360
|
>
|
|
354
361
|
): ApiUpdateLessonPayload {
|
|
@@ -357,6 +364,11 @@ export function toUpdateLessonPayload(
|
|
|
357
364
|
if (data.type !== undefined)
|
|
358
365
|
payload.tipo = data.type as ApiCreateLessonPayload['tipo'];
|
|
359
366
|
if (data.duration !== undefined) payload.duracao = data.duration;
|
|
367
|
+
if (data.published !== undefined) payload.published = data.published;
|
|
368
|
+
if (data.status !== undefined) payload.statusProducao = data.status;
|
|
369
|
+
if (data.confirmarPublicacaoComStatus !== undefined) {
|
|
370
|
+
payload.confirmarPublicacaoComStatus = data.confirmarPublicacaoComStatus;
|
|
371
|
+
}
|
|
360
372
|
if (data.publicDescription !== undefined)
|
|
361
373
|
payload.descricaoPublica = data.publicDescription;
|
|
362
374
|
if (data.privateDescription !== undefined)
|
|
@@ -52,8 +52,10 @@ export interface ApiCourseResource {
|
|
|
52
52
|
|
|
53
53
|
/** Instructor linked to a lesson or available in the course pool. */
|
|
54
54
|
export interface ApiLessonInstructor {
|
|
55
|
-
id: number;
|
|
56
|
-
name
|
|
55
|
+
id: number | string;
|
|
56
|
+
name?: string;
|
|
57
|
+
nome?: string;
|
|
58
|
+
avatarId?: number | null;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
/**
|
|
@@ -81,6 +83,13 @@ export interface ApiLesson {
|
|
|
81
83
|
tipo: 'video' | 'questao' | 'post' | 'exercicio';
|
|
82
84
|
/** Duration in minutes. */
|
|
83
85
|
duracao: number;
|
|
86
|
+
published?: boolean;
|
|
87
|
+
statusProducao?:
|
|
88
|
+
| 'preparada'
|
|
89
|
+
| 'gravada'
|
|
90
|
+
| 'editada'
|
|
91
|
+
| 'finalizada'
|
|
92
|
+
| 'publicada';
|
|
84
93
|
/** ID of the parent session (course_module). */
|
|
85
94
|
sessaoId: string;
|
|
86
95
|
videoProvedor?: 'youtube' | 'vimeo' | 'file_storage' | 'bunny' | 'custom';
|
|
@@ -115,6 +124,7 @@ export interface ApiSession {
|
|
|
115
124
|
titulo: string;
|
|
116
125
|
/** Duration in minutes. */
|
|
117
126
|
duracao: number;
|
|
127
|
+
published?: boolean;
|
|
118
128
|
collapsed: boolean;
|
|
119
129
|
}
|
|
120
130
|
|
|
@@ -124,6 +134,8 @@ export interface ApiSession {
|
|
|
124
134
|
export interface ApiCourse {
|
|
125
135
|
id: string;
|
|
126
136
|
name: string;
|
|
137
|
+
codigo?: string | null;
|
|
138
|
+
code?: string | null;
|
|
127
139
|
slug: string;
|
|
128
140
|
titulo: string;
|
|
129
141
|
descricao: string;
|
|
@@ -159,6 +171,7 @@ export interface ApiCreateSessionPayload {
|
|
|
159
171
|
duracao: number;
|
|
160
172
|
descricao?: string;
|
|
161
173
|
codigo?: string;
|
|
174
|
+
published?: boolean;
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
/**
|
|
@@ -185,6 +198,7 @@ export interface ApiUpdateSessionPayload {
|
|
|
185
198
|
duracao?: number;
|
|
186
199
|
descricao?: string;
|
|
187
200
|
codigo?: string;
|
|
201
|
+
published?: boolean;
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
/**
|
|
@@ -196,6 +210,14 @@ export interface ApiCreateLessonPayload {
|
|
|
196
210
|
tipo: 'video' | 'questao' | 'post' | 'exercicio';
|
|
197
211
|
/** Duration in minutes. */
|
|
198
212
|
duracao: number;
|
|
213
|
+
published?: boolean;
|
|
214
|
+
statusProducao?:
|
|
215
|
+
| 'preparada'
|
|
216
|
+
| 'gravada'
|
|
217
|
+
| 'editada'
|
|
218
|
+
| 'finalizada'
|
|
219
|
+
| 'publicada';
|
|
220
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
199
221
|
descricaoPublica?: string;
|
|
200
222
|
descricaoPrivada?: string;
|
|
201
223
|
videoProvedor?: 'youtube' | 'vimeo' | 'file_storage' | 'bunny' | 'custom';
|
package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs
CHANGED
|
@@ -153,6 +153,7 @@ export function useCreateSessionMutation() {
|
|
|
153
153
|
duration: 0,
|
|
154
154
|
code: `S${String(nextIndex).padStart(2, '0')}`,
|
|
155
155
|
description: '',
|
|
156
|
+
published: false,
|
|
156
157
|
});
|
|
157
158
|
return apiCreateSession(request, courseId, payload);
|
|
158
159
|
},
|
|
@@ -220,6 +221,8 @@ export function useUpdateSessionMutation() {
|
|
|
220
221
|
title: formValues.title ?? s.title,
|
|
221
222
|
code: formValues.code ?? s.code,
|
|
222
223
|
duration: formValues.duration ?? s.duration,
|
|
224
|
+
description: formValues.description ?? s.description,
|
|
225
|
+
published: formValues.published ?? s.published,
|
|
223
226
|
}
|
|
224
227
|
: s
|
|
225
228
|
),
|
|
@@ -277,7 +280,7 @@ export function useCreateLessonMutation() {
|
|
|
277
280
|
publicDescription: '',
|
|
278
281
|
privateDescription: '',
|
|
279
282
|
status: 'preparada',
|
|
280
|
-
|
|
283
|
+
published: false,
|
|
281
284
|
});
|
|
282
285
|
return apiCreateLesson(request, courseId, sessionId, payload);
|
|
283
286
|
},
|
|
@@ -311,6 +314,7 @@ interface UpdateLessonVars {
|
|
|
311
314
|
videoConversionJobId?: number;
|
|
312
315
|
resources?: Resource[];
|
|
313
316
|
instructorIds?: number[];
|
|
317
|
+
confirmarPublicacaoComStatus?: boolean;
|
|
314
318
|
}
|
|
315
319
|
>;
|
|
316
320
|
}
|
|
@@ -336,8 +340,27 @@ export function useUpdateLessonMutation() {
|
|
|
336
340
|
lessonId,
|
|
337
341
|
toUpdateLessonPayload(formValues)
|
|
338
342
|
),
|
|
339
|
-
onSuccess: (
|
|
340
|
-
|
|
343
|
+
onSuccess: (apiLesson, { lessonId, formValues }) => {
|
|
344
|
+
const normalizedLesson = normalizeLesson(apiLesson);
|
|
345
|
+
|
|
346
|
+
updateLessonInStore(String(lessonId), {
|
|
347
|
+
...formValues,
|
|
348
|
+
code: normalizedLesson.code,
|
|
349
|
+
title: normalizedLesson.title,
|
|
350
|
+
publicDescription: normalizedLesson.publicDescription,
|
|
351
|
+
privateDescription: normalizedLesson.privateDescription,
|
|
352
|
+
duration: normalizedLesson.duration,
|
|
353
|
+
type: normalizedLesson.type,
|
|
354
|
+
status: normalizedLesson.status,
|
|
355
|
+
published: normalizedLesson.published,
|
|
356
|
+
videoProvider: normalizedLesson.videoProvider,
|
|
357
|
+
videoUrl: normalizedLesson.videoUrl,
|
|
358
|
+
linkedExam: normalizedLesson.linkedExam,
|
|
359
|
+
postContent: normalizedLesson.postContent,
|
|
360
|
+
transcription: normalizedLesson.transcription,
|
|
361
|
+
videoConversionJobId: normalizedLesson.videoConversionJobId,
|
|
362
|
+
resources: normalizedLesson.resources,
|
|
363
|
+
});
|
|
341
364
|
// Manual cache update for in-place field changes.
|
|
342
365
|
queryClient.setQueryData<CourseStructureCacheData>(
|
|
343
366
|
courseStructureQueryKey(courseId),
|
|
@@ -349,9 +372,21 @@ export function useUpdateLessonMutation() {
|
|
|
349
372
|
l.id === String(lessonId)
|
|
350
373
|
? {
|
|
351
374
|
...l,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
375
|
+
code: normalizedLesson.code,
|
|
376
|
+
title: normalizedLesson.title,
|
|
377
|
+
publicDescription: normalizedLesson.publicDescription,
|
|
378
|
+
privateDescription: normalizedLesson.privateDescription,
|
|
379
|
+
duration: normalizedLesson.duration,
|
|
380
|
+
type: normalizedLesson.type,
|
|
381
|
+
status: normalizedLesson.status,
|
|
382
|
+
published: normalizedLesson.published,
|
|
383
|
+
videoProvider: normalizedLesson.videoProvider,
|
|
384
|
+
videoUrl: normalizedLesson.videoUrl,
|
|
385
|
+
linkedExam: normalizedLesson.linkedExam,
|
|
386
|
+
postContent: normalizedLesson.postContent,
|
|
387
|
+
transcription: normalizedLesson.transcription,
|
|
388
|
+
videoConversionJobId: normalizedLesson.videoConversionJobId,
|
|
389
|
+
resources: normalizedLesson.resources,
|
|
355
390
|
}
|
|
356
391
|
: l
|
|
357
392
|
),
|