@hed-hog/lms 0.0.350 → 0.0.351
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/certificate/certificate.controller.d.ts +2 -2
- package/dist/certificate/certificate.controller.d.ts.map +1 -1
- package/dist/certificate/certificate.controller.js +8 -6
- package/dist/certificate/certificate.controller.js.map +1 -1
- package/dist/certificate/certificate.service.d.ts +5 -2
- package/dist/certificate/certificate.service.d.ts.map +1 -1
- package/dist/certificate/certificate.service.js +70 -6
- package/dist/certificate/certificate.service.js.map +1 -1
- package/dist/course/course-structure.controller.d.ts +24 -10
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +23 -2
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +16 -8
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +61 -30
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-conversion.service.d.ts +37 -0
- package/dist/course/course-video-conversion.service.d.ts.map +1 -0
- package/dist/course/course-video-conversion.service.js +308 -0
- package/dist/course/course-video-conversion.service.js.map +1 -0
- package/dist/course/course.controller.d.ts +17 -0
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +23 -0
- package/dist/course/course.controller.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +15 -2
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +15 -0
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +103 -49
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +5 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +16 -2
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course.dto.d.ts +1 -0
- package/dist/course/dto/create-course.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course.dto.js +9 -0
- package/dist/course/dto/create-course.dto.js.map +1 -1
- package/dist/enterprise/enterprise.controller.d.ts +3 -3
- package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
- package/dist/enterprise/enterprise.controller.js +0 -1
- package/dist/enterprise/enterprise.controller.js.map +1 -1
- package/dist/enterprise/enterprise.service.d.ts +3 -3
- package/dist/evaluation/evaluation.service.d.ts.map +1 -1
- package/dist/evaluation/evaluation.service.js +9 -2
- package/dist/evaluation/evaluation.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +3 -0
- package/dist/lms.module.js.map +1 -1
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts +6 -0
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts.map +1 -0
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js +33 -0
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js.map +1 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts +6 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts.map +1 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js +33 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts +38 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.js +89 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts +26 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js +160 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.d.ts +3 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.js +26 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.d.ts +45 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.js +117 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.js.map +1 -0
- package/hedhog/data/menu.yaml +17 -0
- package/hedhog/data/route.yaml +133 -0
- package/hedhog/data/video_resolution_profile.yaml +7 -0
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +269 -324
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +124 -70
- package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +7 -4
- package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +34 -4
- package/hedhog/frontend/app/_lib/editor/types.ts.ejs +28 -3
- package/hedhog/frontend/app/achievements/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/bitcodes/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +7 -3
- package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +29 -8
- package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +14 -0
- package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +194 -9
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +15 -5
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +9 -5
- package/hedhog/frontend/app/classes/page.tsx.ejs +73 -47
- package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +19 -9
- package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +24 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +28 -16
- package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +11 -6
- package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +7 -4
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +24 -87
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +892 -411
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1004 -293
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +11 -11
- package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +62 -52
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +2 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -6
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +86 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/page.tsx.ejs +112 -89
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +8 -4
- package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +10 -4
- package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +23 -9
- package/hedhog/frontend/app/exams/page.tsx.ejs +14 -6
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +190 -17
- package/hedhog/frontend/app/layout.tsx.ejs +5 -1
- package/hedhog/frontend/app/paths/page.tsx.ejs +13 -5
- package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +10 -10
- package/hedhog/frontend/app/training/page.tsx.ejs +13 -5
- package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +607 -0
- package/hedhog/frontend/messages/en.json +250 -9
- package/hedhog/frontend/messages/pt.json +250 -9
- package/hedhog/table/course.yaml +4 -0
- package/hedhog/table/course_lesson_file.yaml +8 -0
- package/hedhog/table/course_video_resolution_profile.yaml +22 -0
- package/hedhog/table/video_resolution_profile.yaml +18 -0
- package/package.json +7 -6
- package/src/certificate/certificate.controller.ts +19 -14
- package/src/certificate/certificate.service.ts +106 -11
- package/src/course/course-structure.controller.ts +24 -2
- package/src/course/course-structure.service.ts +21 -4
- package/src/course/course-video-conversion.service.ts +415 -0
- package/src/course/course.controller.ts +18 -0
- package/src/course/course.module.ts +15 -2
- package/src/course/course.service.ts +72 -2
- package/src/course/dto/create-course-structure-lesson.dto.ts +13 -2
- package/src/course/dto/create-course.dto.ts +8 -0
- package/src/enterprise/enterprise.controller.ts +0 -1
- package/src/evaluation/evaluation.service.ts +9 -2
- package/src/index.ts +1 -0
- package/src/lms.module.ts +3 -0
- package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +16 -0
- package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +16 -0
- package/src/video-resolution-profile/video-resolution-profile.controller.ts +62 -0
- package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +128 -0
- package/src/video-resolution-profile/video-resolution-profile.module.ts +13 -0
- package/src/video-resolution-profile/video-resolution-profile.service.ts +117 -0
|
@@ -140,9 +140,9 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
140
140
|
className="flex flex-col h-full min-h-0"
|
|
141
141
|
>
|
|
142
142
|
{/* ── Header ───────────────────────────────────────────────────────── */}
|
|
143
|
-
<div className="flex items-center gap-
|
|
144
|
-
<div className="flex size-
|
|
145
|
-
<Layers className="size-
|
|
143
|
+
<div className="flex items-center gap-2 border-b bg-muted/30 px-2 py-2 shrink-0 sm:gap-3 sm:px-4 sm:py-3">
|
|
144
|
+
<div className="flex size-8 items-center justify-center rounded-md bg-muted shrink-0 sm:size-9 sm:rounded-lg">
|
|
145
|
+
<Layers className="size-3.5 text-muted-foreground sm:size-4" />
|
|
146
146
|
</div>
|
|
147
147
|
<div className="flex-1 min-w-0">
|
|
148
148
|
<div className="flex items-center gap-1.5">
|
|
@@ -204,7 +204,7 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
204
204
|
</CardTitle>
|
|
205
205
|
</CardHeader>
|
|
206
206
|
<CardContent className="px-3 pb-2 flex flex-col gap-3">
|
|
207
|
-
<div className="grid grid-cols-
|
|
207
|
+
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
|
208
208
|
<FormField
|
|
209
209
|
control={form.control}
|
|
210
210
|
name="code"
|
|
@@ -280,7 +280,7 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
280
280
|
</CardTitle>
|
|
281
281
|
</CardHeader>
|
|
282
282
|
<CardContent className="px-3 pb-2">
|
|
283
|
-
<div className="grid grid-cols-
|
|
283
|
+
<div className="grid grid-cols-1 gap-2 items-end sm:grid-cols-2">
|
|
284
284
|
<FormField
|
|
285
285
|
control={form.control}
|
|
286
286
|
name="visibility"
|
|
@@ -342,7 +342,7 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
342
342
|
|
|
343
343
|
{/* ── Aulas da sessão ───────────────────────────────────────────── */}
|
|
344
344
|
{lessons.length === 0 && (
|
|
345
|
-
<div className="flex flex-col items-center gap-2
|
|
345
|
+
<div className="flex flex-col items-center gap-2 px-2 py-4 rounded-lg border border-dashed text-center sm:py-6">
|
|
346
346
|
<Video className="size-5 text-muted-foreground/40" />
|
|
347
347
|
<p className="text-xs text-muted-foreground leading-relaxed">
|
|
348
348
|
Esta sessão ainda não tem aulas.
|
|
@@ -370,12 +370,12 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
370
370
|
{/* ── Footer ───────────────────────────────────────────────────────── */}
|
|
371
371
|
<div className="shrink-0 border-t bg-background">
|
|
372
372
|
<Separator />
|
|
373
|
-
<div className="flex items-center gap-2 px-3 py-2">
|
|
373
|
+
<div className="flex items-center gap-1.5 px-2 py-1.5 sm:gap-2 sm:px-3 sm:py-2">
|
|
374
374
|
<Button
|
|
375
375
|
type="button"
|
|
376
376
|
variant="ghost"
|
|
377
377
|
size="sm"
|
|
378
|
-
className="h-7 text-xs"
|
|
378
|
+
className="h-6 text-[11px] sm:h-7 sm:text-xs"
|
|
379
379
|
disabled={!isDirty || updateSession.isPending}
|
|
380
380
|
onClick={() => form.reset()}
|
|
381
381
|
>
|
|
@@ -387,7 +387,7 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
387
387
|
type="button"
|
|
388
388
|
variant="outline"
|
|
389
389
|
size="sm"
|
|
390
|
-
className="h-7 text-xs"
|
|
390
|
+
className="h-6 text-[11px] sm:h-7 sm:text-xs"
|
|
391
391
|
disabled={createLesson.isPending}
|
|
392
392
|
onClick={() => createLesson.mutate({ sessionId })}
|
|
393
393
|
>
|
|
@@ -401,7 +401,7 @@ export function EditorSession({ sessionId }: EditorSessionProps) {
|
|
|
401
401
|
<Button
|
|
402
402
|
type="submit"
|
|
403
403
|
size="sm"
|
|
404
|
-
className="h-7 text-xs"
|
|
404
|
+
className="h-6 text-[11px] sm:h-7 sm:text-xs"
|
|
405
405
|
disabled={!isDirty || updateSession.isPending}
|
|
406
406
|
>
|
|
407
407
|
{updateSession.isPending ? (
|
|
@@ -430,7 +430,7 @@ function StatChip({
|
|
|
430
430
|
value: string | number;
|
|
431
431
|
}) {
|
|
432
432
|
return (
|
|
433
|
-
<div className="flex items-center gap-
|
|
433
|
+
<div className="flex items-center gap-1.5 rounded-md border bg-muted/20 px-2 py-1.5 sm:gap-2 sm:px-2.5 sm:py-2">
|
|
434
434
|
<span className="text-muted-foreground shrink-0">{icon}</span>
|
|
435
435
|
<div className="min-w-0">
|
|
436
436
|
<p className="text-[0.65rem] text-muted-foreground truncate">{label}</p>
|
|
@@ -6,11 +6,11 @@ import { useTranslations } from 'next-intl';
|
|
|
6
6
|
import { Badge } from '@/components/ui/badge';
|
|
7
7
|
import { Button } from '@/components/ui/button';
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from '@/components/ui/
|
|
9
|
+
Sheet,
|
|
10
|
+
SheetHeader,
|
|
11
|
+
SheetTitle,
|
|
12
|
+
} from '@/components/ui/sheet';
|
|
13
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
14
14
|
import { Separator } from '@/components/ui/separator';
|
|
15
15
|
|
|
16
16
|
// ── Data ──────────────────────────────────────────────────────────────────────
|
|
@@ -99,56 +99,65 @@ export function ShortcutsHelp({ open, onOpenChange }: ShortcutsHelpProps) {
|
|
|
99
99
|
},
|
|
100
100
|
];
|
|
101
101
|
return (
|
|
102
|
-
<
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
103
|
+
<ResizableSheetContent
|
|
104
|
+
sheetId="lms-course-structure-shortcuts-help-sheet"
|
|
105
|
+
defaultWidth={520}
|
|
106
|
+
minWidth={400}
|
|
107
|
+
maxWidth={860}
|
|
108
|
+
side="right"
|
|
109
|
+
className="w-full sm:max-w-md p-0 gap-0"
|
|
110
|
+
>
|
|
111
|
+
<SheetHeader className="border-b px-4 py-3">
|
|
112
|
+
<SheetTitle className="flex items-center gap-2">
|
|
106
113
|
<Keyboard className="size-4" />
|
|
107
114
|
{t('title')}
|
|
108
|
-
</
|
|
109
|
-
</
|
|
115
|
+
</SheetTitle>
|
|
116
|
+
</SheetHeader>
|
|
110
117
|
|
|
111
|
-
<div className="flex
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
118
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-4 py-3">
|
|
119
|
+
<div className="flex flex-col gap-4 mt-1">
|
|
120
|
+
{shortcutGroups.map((group, gi) => (
|
|
121
|
+
<div key={gi}>
|
|
122
|
+
<p className="text-[0.65rem] font-semibold uppercase tracking-wider text-muted-foreground mb-2">
|
|
123
|
+
{group.heading}
|
|
124
|
+
</p>
|
|
125
|
+
<div className="flex flex-col gap-1.5">
|
|
126
|
+
{group.items.map(({ keys, description }, ki) => (
|
|
127
|
+
<div
|
|
128
|
+
key={ki}
|
|
129
|
+
className="flex items-center justify-between gap-3"
|
|
130
|
+
>
|
|
131
|
+
<span className="text-sm text-muted-foreground">
|
|
132
|
+
{description}
|
|
133
|
+
</span>
|
|
134
|
+
<div className="flex items-center gap-1 shrink-0">
|
|
135
|
+
{keys.map((k, i) => (
|
|
136
|
+
<Badge
|
|
137
|
+
key={i}
|
|
138
|
+
variant="outline"
|
|
139
|
+
className="h-5 px-1.5 text-[0.65rem] font-mono font-medium"
|
|
140
|
+
>
|
|
141
|
+
{k}
|
|
142
|
+
</Badge>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
136
145
|
</div>
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
))}
|
|
147
|
+
</div>
|
|
148
|
+
{gi < shortcutGroups.length - 1 && (
|
|
149
|
+
<Separator className="mt-3" />
|
|
150
|
+
)}
|
|
139
151
|
</div>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)}
|
|
143
|
-
</div>
|
|
144
|
-
))}
|
|
145
|
-
</div>
|
|
152
|
+
))}
|
|
153
|
+
</div>
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
<p className="text-[0.65rem] text-muted-foreground mt-4">
|
|
156
|
+
{t('footer')}
|
|
157
|
+
</p>
|
|
158
|
+
</div>
|
|
159
|
+
</ResizableSheetContent>
|
|
160
|
+
</Sheet>
|
|
152
161
|
);
|
|
153
162
|
}
|
|
154
163
|
|
|
@@ -161,15 +170,16 @@ export function ShortcutsHelpTrigger({ onOpen }: { onOpen: () => void }) {
|
|
|
161
170
|
<Button
|
|
162
171
|
variant="ghost"
|
|
163
172
|
size="sm"
|
|
164
|
-
className="h-8 gap-1.5 text-xs text-muted-foreground hover:text-foreground"
|
|
173
|
+
className="h-8 gap-1.5 px-2 text-xs text-muted-foreground hover:text-foreground sm:px-3"
|
|
165
174
|
onClick={onOpen}
|
|
166
175
|
title={t('triggerTitle')}
|
|
176
|
+
aria-label={t('triggerTitle')}
|
|
167
177
|
>
|
|
168
178
|
<Keyboard className="size-3.5" />
|
|
169
|
-
{t('triggerLabel')}
|
|
179
|
+
<span className="sr-only sm:not-sr-only">{t('triggerLabel')}</span>
|
|
170
180
|
<Badge
|
|
171
181
|
variant="outline"
|
|
172
|
-
className="h-4 px-1 text-[0.6rem] font-mono
|
|
182
|
+
className="ml-0.5 hidden h-4 px-1 text-[0.6rem] font-mono sm:inline-flex"
|
|
173
183
|
>
|
|
174
184
|
Ctrl+/
|
|
175
185
|
</Badge>
|
|
@@ -41,6 +41,7 @@ export interface Session {
|
|
|
41
41
|
|
|
42
42
|
export interface Resource {
|
|
43
43
|
id: string;
|
|
44
|
+
fileId?: number;
|
|
44
45
|
name: string;
|
|
45
46
|
size: string;
|
|
46
47
|
type: string;
|
|
@@ -72,6 +73,7 @@ export interface Lesson {
|
|
|
72
73
|
videoUrl?: string;
|
|
73
74
|
autoDuration?: boolean;
|
|
74
75
|
transcription?: string;
|
|
76
|
+
videoConversionJobId?: number;
|
|
75
77
|
// Questão
|
|
76
78
|
linkedExam?: string;
|
|
77
79
|
// Post
|
package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs
CHANGED
|
@@ -74,6 +74,7 @@ function normalizeVideoProvider(
|
|
|
74
74
|
function normalizeResource(raw: ApiLessonResource): Resource {
|
|
75
75
|
return {
|
|
76
76
|
id: String(raw.id),
|
|
77
|
+
fileId: raw.fileId ?? undefined,
|
|
77
78
|
name: raw.nome,
|
|
78
79
|
type: raw.tipo ?? '',
|
|
79
80
|
/**
|
|
@@ -163,6 +164,7 @@ export function normalizeLesson(raw: ApiLesson, order = 0): Lesson {
|
|
|
163
164
|
videoUrl: raw.videoUrl,
|
|
164
165
|
autoDuration: raw.duracaoAutomatica,
|
|
165
166
|
transcription: raw.transcricao,
|
|
167
|
+
videoConversionJobId: raw.videoConversionJobId,
|
|
166
168
|
/**
|
|
167
169
|
* ⚠️ TYPE MISMATCH: backend sends `exameVinculado` as number;
|
|
168
170
|
* frontend uses `linkedExam` as string. Converted with String().
|
|
@@ -265,6 +267,7 @@ export function toUpdateSessionPayload(
|
|
|
265
267
|
export function toCreateLessonPayload(
|
|
266
268
|
data: LessonFormValues & {
|
|
267
269
|
transcription?: string;
|
|
270
|
+
videoConversionJobId?: number;
|
|
268
271
|
instructorIds?: number[];
|
|
269
272
|
resources?: LessonResourceInput[];
|
|
270
273
|
}
|
|
@@ -285,6 +288,9 @@ export function toCreateLessonPayload(
|
|
|
285
288
|
duracaoAutomatica: data.autoDuration,
|
|
286
289
|
}),
|
|
287
290
|
...(data.transcription && { transcricao: data.transcription }),
|
|
291
|
+
...(data.videoConversionJobId !== undefined && {
|
|
292
|
+
videoConversionJobId: data.videoConversionJobId,
|
|
293
|
+
}),
|
|
288
294
|
// ⚠️ TYPE MISMATCH: frontend stores linkedExam as string; backend expects number.
|
|
289
295
|
...(data.linkedExam && { exameVinculado: Number(data.linkedExam) }),
|
|
290
296
|
...(data.postContent && { conteudoPost: data.postContent }),
|
|
@@ -294,9 +300,11 @@ export function toCreateLessonPayload(
|
|
|
294
300
|
recursos: data.resources.map((r) => ({
|
|
295
301
|
nome: r.name,
|
|
296
302
|
// Only include fileId when the id is a valid server-assigned integer.
|
|
297
|
-
...(Number.isInteger(
|
|
298
|
-
? { fileId:
|
|
299
|
-
:
|
|
303
|
+
...(Number.isInteger(r.fileId ?? Number.NaN) && (r.fileId ?? 0) > 0
|
|
304
|
+
? { fileId: r.fileId }
|
|
305
|
+
: Number.isInteger(Number(r.id)) && Number(r.id) > 0
|
|
306
|
+
? { fileId: Number(r.id) }
|
|
307
|
+
: {}),
|
|
300
308
|
...(r.type ? { tipo: r.type } : {}),
|
|
301
309
|
publico: r.public,
|
|
302
310
|
})),
|
|
@@ -312,6 +320,7 @@ export function toUpdateLessonPayload(
|
|
|
312
320
|
data: Partial<
|
|
313
321
|
LessonFormValues & {
|
|
314
322
|
transcription?: string;
|
|
323
|
+
videoConversionJobId?: number;
|
|
315
324
|
instructorIds?: number[];
|
|
316
325
|
resources?: LessonResourceInput[];
|
|
317
326
|
}
|
|
@@ -333,6 +342,8 @@ export function toUpdateLessonPayload(
|
|
|
333
342
|
payload.duracaoAutomatica = data.autoDuration;
|
|
334
343
|
if (data.transcription !== undefined)
|
|
335
344
|
payload.transcricao = data.transcription;
|
|
345
|
+
if (data.videoConversionJobId !== undefined)
|
|
346
|
+
payload.videoConversionJobId = data.videoConversionJobId;
|
|
336
347
|
if (data.postContent !== undefined) payload.conteudoPost = data.postContent;
|
|
337
348
|
if (data.code !== undefined) payload.codigo = data.code;
|
|
338
349
|
if (data.instructorIds !== undefined)
|
|
@@ -347,9 +358,11 @@ export function toUpdateLessonPayload(
|
|
|
347
358
|
if (data.resources !== undefined) {
|
|
348
359
|
payload.recursos = data.resources.map((r) => ({
|
|
349
360
|
nome: r.name,
|
|
350
|
-
...(Number.isInteger(
|
|
351
|
-
? { fileId:
|
|
352
|
-
:
|
|
361
|
+
...(Number.isInteger(r.fileId ?? Number.NaN) && (r.fileId ?? 0) > 0
|
|
362
|
+
? { fileId: r.fileId }
|
|
363
|
+
: Number.isInteger(Number(r.id)) && Number(r.id) > 0
|
|
364
|
+
? { fileId: Number(r.id) }
|
|
365
|
+
: {}),
|
|
353
366
|
...(r.type ? { tipo: r.type } : {}),
|
|
354
367
|
publico: r.public,
|
|
355
368
|
}));
|
package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs
CHANGED
|
@@ -48,8 +48,63 @@ type RequestFn = (input: {
|
|
|
48
48
|
method: string;
|
|
49
49
|
data?: unknown;
|
|
50
50
|
headers?: Record<string, string>;
|
|
51
|
+
onUploadProgress?: (event: { loaded: number; total?: number }) => void;
|
|
51
52
|
}) => Promise<{ data: unknown }>;
|
|
52
53
|
|
|
54
|
+
export type QueueJobStatus =
|
|
55
|
+
| 'pending'
|
|
56
|
+
| 'scheduled'
|
|
57
|
+
| 'processing'
|
|
58
|
+
| 'completed'
|
|
59
|
+
| 'failed'
|
|
60
|
+
| 'retrying'
|
|
61
|
+
| 'canceled'
|
|
62
|
+
| 'dead_letter';
|
|
63
|
+
|
|
64
|
+
export interface QueueJobAttempt {
|
|
65
|
+
id: number;
|
|
66
|
+
attempt_number: number;
|
|
67
|
+
status: 'processing' | 'completed' | 'failed' | 'canceled';
|
|
68
|
+
started_at: string;
|
|
69
|
+
finished_at?: string | null;
|
|
70
|
+
duration_ms?: number | null;
|
|
71
|
+
worker_id?: string | null;
|
|
72
|
+
error_message?: string | null;
|
|
73
|
+
created_at: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface QueueJobEvent {
|
|
77
|
+
id: number;
|
|
78
|
+
event_type:
|
|
79
|
+
| 'created'
|
|
80
|
+
| 'scheduled'
|
|
81
|
+
| 'locked'
|
|
82
|
+
| 'started'
|
|
83
|
+
| 'completed'
|
|
84
|
+
| 'failed'
|
|
85
|
+
| 'retry_scheduled'
|
|
86
|
+
| 'canceled'
|
|
87
|
+
| 'moved_to_dead_letter'
|
|
88
|
+
| 'requeued'
|
|
89
|
+
| 'unlocked';
|
|
90
|
+
message?: string | null;
|
|
91
|
+
created_at: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface QueueJobResponse {
|
|
95
|
+
id: number;
|
|
96
|
+
status: QueueJobStatus;
|
|
97
|
+
attempts: number;
|
|
98
|
+
max_attempts: number;
|
|
99
|
+
created_at: string;
|
|
100
|
+
started_at?: string | null;
|
|
101
|
+
finished_at?: string | null;
|
|
102
|
+
next_retry_at?: string | null;
|
|
103
|
+
last_error?: string | null;
|
|
104
|
+
queue_job_attempt: QueueJobAttempt[];
|
|
105
|
+
queue_job_event: QueueJobEvent[];
|
|
106
|
+
}
|
|
107
|
+
|
|
53
108
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
109
|
// URL helpers
|
|
55
110
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -434,7 +489,10 @@ export async function pasteLessons(
|
|
|
434
489
|
export async function uploadFile(
|
|
435
490
|
request: RequestFn,
|
|
436
491
|
file: File,
|
|
437
|
-
destination = 'lms/lessons'
|
|
492
|
+
destination = 'lms/lessons',
|
|
493
|
+
options?: {
|
|
494
|
+
onUploadProgress?: (event: { loaded: number; total?: number }) => void;
|
|
495
|
+
}
|
|
438
496
|
): Promise<{ id: number; filename: string }> {
|
|
439
497
|
const formData = new FormData();
|
|
440
498
|
formData.append('file', file);
|
|
@@ -445,10 +503,37 @@ export async function uploadFile(
|
|
|
445
503
|
method: 'POST',
|
|
446
504
|
data: formData,
|
|
447
505
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
506
|
+
onUploadProgress: options?.onUploadProgress,
|
|
448
507
|
});
|
|
449
508
|
return extractData<{ id: number; filename: string }>(response);
|
|
450
509
|
}
|
|
451
510
|
|
|
511
|
+
export async function enqueueLessonVideoConversion(
|
|
512
|
+
request: RequestFn,
|
|
513
|
+
courseId: string | number,
|
|
514
|
+
sessionId: string | number,
|
|
515
|
+
lessonId: string | number,
|
|
516
|
+
originalFileId: number
|
|
517
|
+
): Promise<{ queueJobId: number; status: 'queued' }> {
|
|
518
|
+
const response = await request({
|
|
519
|
+
url: `${LESSONS_PATH(courseId, sessionId)}/${lessonId}/video-conversions`,
|
|
520
|
+
method: 'POST',
|
|
521
|
+
data: { originalFileId },
|
|
522
|
+
});
|
|
523
|
+
return extractData<{ queueJobId: number; status: 'queued' }>(response);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export async function getQueueJob(
|
|
527
|
+
request: RequestFn,
|
|
528
|
+
jobId: number
|
|
529
|
+
): Promise<QueueJobResponse> {
|
|
530
|
+
const response = await request({
|
|
531
|
+
url: `/queue/jobs/${jobId}`,
|
|
532
|
+
method: 'GET',
|
|
533
|
+
});
|
|
534
|
+
return extractData<QueueJobResponse>(response);
|
|
535
|
+
}
|
|
536
|
+
|
|
452
537
|
/**
|
|
453
538
|
* DELETE /file
|
|
454
539
|
*
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
export interface ApiLessonResource {
|
|
21
21
|
id: number;
|
|
22
22
|
nome: string;
|
|
23
|
+
fileId?: number | null;
|
|
23
24
|
/** MIME category or custom type label; may be null. */
|
|
24
25
|
tipo: string | null;
|
|
25
26
|
publico: boolean;
|
|
@@ -76,6 +77,7 @@ export interface ApiLesson {
|
|
|
76
77
|
videoUrl?: string;
|
|
77
78
|
duracaoAutomatica?: boolean;
|
|
78
79
|
transcricao?: string;
|
|
80
|
+
videoConversionJobId?: number;
|
|
79
81
|
/**
|
|
80
82
|
* ID of the linked exam (when tipo === 'questao').
|
|
81
83
|
* ⚠️ TYPE MISMATCH: backend sends number; frontend stores as string.
|
|
@@ -188,6 +190,7 @@ export interface ApiCreateLessonPayload {
|
|
|
188
190
|
videoUrl?: string;
|
|
189
191
|
duracaoAutomatica?: boolean;
|
|
190
192
|
transcricao?: string;
|
|
193
|
+
videoConversionJobId?: number;
|
|
191
194
|
/** ID of the linked exam. ⚠️ TYPE MISMATCH: backend expects number; frontend stores string. */
|
|
192
195
|
exameVinculado?: number;
|
|
193
196
|
conteudoPost?: string;
|