@hed-hog/lms 0.0.365 → 0.0.370
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 +1 -1
- package/dist/certificate/certificate.controller.d.ts.map +1 -1
- package/dist/certificate/certificate.controller.js +4 -2
- package/dist/certificate/certificate.controller.js.map +1 -1
- package/dist/certificate/certificate.service.d.ts +50 -0
- package/dist/certificate/certificate.service.d.ts.map +1 -1
- package/dist/certificate/certificate.service.js +73 -0
- package/dist/certificate/certificate.service.js.map +1 -1
- package/dist/class-group/class-group.controller.d.ts +1 -0
- package/dist/class-group/class-group.controller.d.ts.map +1 -1
- package/dist/class-group/class-group.service.d.ts +1 -0
- package/dist/class-group/class-group.service.d.ts.map +1 -1
- package/dist/course/course-ai-usage.service.d.ts +58 -0
- package/dist/course/course-ai-usage.service.d.ts.map +1 -0
- package/dist/course/course-ai-usage.service.js +176 -0
- package/dist/course/course-ai-usage.service.js.map +1 -0
- package/dist/course/course-audio-transcription.service.d.ts +65 -1
- package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
- package/dist/course/course-audio-transcription.service.js +381 -29
- package/dist/course/course-audio-transcription.service.js.map +1 -1
- package/dist/course/course-export-scorm12.service.d.ts +3 -0
- package/dist/course/course-export-scorm12.service.d.ts.map +1 -1
- package/dist/course/course-export-scorm12.service.js +141 -6
- package/dist/course/course-export-scorm12.service.js.map +1 -1
- package/dist/course/course-export.service.d.ts.map +1 -1
- package/dist/course/course-export.service.js +2 -1
- package/dist/course/course-export.service.js.map +1 -1
- package/dist/course/course-lesson.controller.d.ts +25 -3
- package/dist/course/course-lesson.controller.d.ts.map +1 -1
- package/dist/course/course-lesson.controller.js +71 -8
- package/dist/course/course-lesson.controller.js.map +1 -1
- package/dist/course/course-structure.controller.d.ts +30 -7
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +37 -4
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +37 -5
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +165 -20
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-transcription-translation.service.d.ts +31 -0
- package/dist/course/course-transcription-translation.service.d.ts.map +1 -0
- package/dist/course/course-transcription-translation.service.js +227 -0
- package/dist/course/course-transcription-translation.service.js.map +1 -0
- package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
- package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
- package/dist/course/course-video-agent-pipeline.service.js +398 -0
- package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
- package/dist/course/course-video-hls.service.d.ts +14 -0
- package/dist/course/course-video-hls.service.d.ts.map +1 -1
- package/dist/course/course-video-hls.service.js +25 -8
- package/dist/course/course-video-hls.service.js.map +1 -1
- package/dist/course/course.controller.d.ts +2 -0
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +9 -0
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +2 -0
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +36 -2
- package/dist/course/course.service.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 +1 -0
- package/dist/course/dto/create-course-export.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-export.dto.js +6 -0
- package/dist/course/dto/create-course-export.dto.js.map +1 -1
- package/dist/course/ffmpeg.util.d.ts +10 -0
- package/dist/course/ffmpeg.util.d.ts.map +1 -0
- package/dist/course/ffmpeg.util.js +79 -0
- package/dist/course/ffmpeg.util.js.map +1 -0
- package/dist/course/lms-bulk-upload-automation.service.d.ts +3 -1
- package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload-automation.service.js +33 -16
- package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
- package/dist/course/lms-bulk-upload.controller.d.ts +3 -0
- package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.service.d.ts +3 -0
- package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.service.js +48 -29
- package/dist/course/lms-bulk-upload.service.js.map +1 -1
- package/dist/course/subtitle.util.d.ts +46 -0
- package/dist/course/subtitle.util.d.ts.map +1 -0
- package/dist/course/subtitle.util.js +206 -0
- package/dist/course/subtitle.util.js.map +1 -0
- package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
- package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
- package/dist/enterprise/training/training-admin.service.d.ts +2 -0
- package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +27 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +197 -10
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +3 -1
- 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 +19 -5
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
- package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -1
- package/dist/lesson-xp-map/lesson-xp-map.module.js +2 -1
- package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -1
- package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +14 -0
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
- package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
- package/dist/platforma/dto/heartbeat.dto.js +50 -0
- package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
- package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
- package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
- package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
- package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
- package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
- package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
- package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
- package/dist/platforma/platforma-heartbeat.service.js +50 -0
- package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
- package/dist/platforma/platforma-performance.service.d.ts +121 -0
- package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
- package/dist/platforma/platforma-performance.service.js +500 -0
- package/dist/platforma/platforma-performance.service.js.map +1 -0
- package/dist/platforma/platforma-search.service.d.ts +21 -0
- package/dist/platforma/platforma-search.service.d.ts.map +1 -0
- package/dist/platforma/platforma-search.service.js +64 -0
- package/dist/platforma/platforma-search.service.js.map +1 -0
- package/dist/platforma/platforma-video.service.d.ts +8 -0
- package/dist/platforma/platforma-video.service.d.ts.map +1 -1
- package/dist/platforma/platforma-video.service.js +45 -2
- package/dist/platforma/platforma-video.service.js.map +1 -1
- package/dist/platforma/platforma.controller.d.ts +213 -1
- package/dist/platforma/platforma.controller.d.ts.map +1 -1
- package/dist/platforma/platforma.controller.js +159 -2
- package/dist/platforma/platforma.controller.js.map +1 -1
- package/dist/realtime/lms-realtime.controller.d.ts +2 -0
- package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
- package/dist/realtime/lms-realtime.controller.js +31 -0
- package/dist/realtime/lms-realtime.controller.js.map +1 -1
- package/dist/realtime/lms-realtime.service.d.ts +1 -1
- package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
- package/dist/realtime/lms-realtime.service.js.map +1 -1
- package/dist/training/dto/create-training.dto.d.ts +9 -0
- package/dist/training/dto/create-training.dto.d.ts.map +1 -1
- package/dist/training/dto/create-training.dto.js +45 -1
- package/dist/training/dto/create-training.dto.js.map +1 -1
- package/dist/training/training.controller.d.ts +144 -0
- package/dist/training/training.controller.d.ts.map +1 -1
- package/dist/training/training.service.d.ts +149 -0
- package/dist/training/training.service.d.ts.map +1 -1
- package/dist/training/training.service.js +332 -167
- package/dist/training/training.service.js.map +1 -1
- package/hedhog/data/image_type.yaml +10 -0
- package/hedhog/data/route.yaml +251 -0
- package/hedhog/data/setting_group.yaml +97 -0
- package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +139 -27
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
- package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
- package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
- package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +69 -57
- package/hedhog/frontend/app/courses/[id]/_components/CourseIssuedCertificatesCard.tsx.ejs +168 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-ai-costs-tab.tsx.ejs +191 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +81 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +12 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +141 -30
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +69 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +267 -19
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +114 -86
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +239 -31
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +344 -59
- package/hedhog/frontend/app/courses/[id]/structure/_components/lesson-video-preview.tsx.ejs +200 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -7
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-ai-costs.ts.ejs +40 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +25 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +148 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +157 -8
- package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +1 -22
- package/hedhog/frontend/app/courses/page.tsx.ejs +66 -13
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
- package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
- package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
- package/hedhog/frontend/app/paths/page.tsx.ejs +650 -168
- package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
- package/hedhog/frontend/messages/en.json +41 -12
- package/hedhog/frontend/messages/pt.json +44 -13
- package/hedhog/query/triggers.sql +33 -0
- package/hedhog/table/course_ai_usage.yaml +46 -0
- package/hedhog/table/course_enrollment.yaml +3 -0
- package/hedhog/table/course_lesson.yaml +3 -0
- package/hedhog/table/course_lesson_answer.yaml +37 -0
- package/hedhog/table/course_lesson_transcription_segment.yaml +8 -0
- package/hedhog/table/learning_path.yaml +6 -0
- package/hedhog/table/learning_path_module.yaml +22 -0
- package/hedhog/table/learning_path_step.yaml +9 -6
- package/hedhog/table/lesson_view_event.yaml +66 -0
- package/package.json +8 -7
- package/src/certificate/certificate.controller.ts +2 -0
- package/src/certificate/certificate.service.ts +99 -0
- package/src/course/course-ai-usage.service.ts +221 -0
- package/src/course/course-audio-transcription.service.ts +471 -43
- package/src/course/course-export-scorm12.service.ts +149 -5
- package/src/course/course-export.service.ts +1 -0
- package/src/course/course-lesson.controller.ts +59 -6
- package/src/course/course-structure.controller.ts +19 -1
- package/src/course/course-structure.service.ts +184 -10
- package/src/course/course-transcription-translation.service.ts +293 -0
- package/src/course/course-video-agent-pipeline.service.ts +471 -0
- package/src/course/course-video-hls.service.ts +30 -10
- package/src/course/course.module.ts +9 -0
- package/src/course/course.service.ts +46 -1
- package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
- package/src/course/dto/create-course-export.dto.ts +6 -0
- package/src/course/ffmpeg.util.ts +65 -0
- package/src/course/lms-bulk-upload-automation.service.ts +33 -8
- package/src/course/lms-bulk-upload.service.ts +20 -1
- package/src/course/subtitle.util.ts +220 -0
- package/src/enterprise/training/training-student.service.ts +224 -4
- package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +14 -0
- package/src/lesson-xp-map/lesson-xp-map.module.ts +2 -1
- package/src/lms.module.ts +14 -0
- package/src/platforma/dto/heartbeat.dto.ts +30 -0
- package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
- package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
- package/src/platforma/platforma-heartbeat.service.ts +33 -0
- package/src/platforma/platforma-performance.service.ts +606 -0
- package/src/platforma/platforma-search.service.ts +48 -0
- package/src/platforma/platforma-video.service.ts +59 -3
- package/src/platforma/platforma.controller.ts +130 -0
- package/src/realtime/lms-realtime.controller.ts +27 -1
- package/src/realtime/lms-realtime.service.ts +2 -1
- package/src/training/dto/create-training.dto.ts +36 -0
- package/src/training/training.service.ts +360 -163
|
@@ -83,6 +83,7 @@ type UploadStatus =
|
|
|
83
83
|
| 'queued'
|
|
84
84
|
| 'uploading'
|
|
85
85
|
| 'cancelling'
|
|
86
|
+
| 'processing'
|
|
86
87
|
| 'received'
|
|
87
88
|
| 'done'
|
|
88
89
|
| 'error'
|
|
@@ -116,6 +117,9 @@ type UploadItemRow = {
|
|
|
116
117
|
matchedSessionTitle: string | null;
|
|
117
118
|
matchedLessonId: number | null;
|
|
118
119
|
matchedLessonTitle: string | null;
|
|
120
|
+
jobStatus: string | null;
|
|
121
|
+
jobAttempts: number | null;
|
|
122
|
+
jobMaxAttempts: number | null;
|
|
119
123
|
};
|
|
120
124
|
|
|
121
125
|
type UploadListResponse = {
|
|
@@ -271,6 +275,15 @@ function formatDate(value: string | null) {
|
|
|
271
275
|
|
|
272
276
|
function getStatusMeta(status: UploadStatus) {
|
|
273
277
|
switch (status) {
|
|
278
|
+
case 'processing':
|
|
279
|
+
return {
|
|
280
|
+
label: 'Aula vinculada',
|
|
281
|
+
variant: 'secondary' as const,
|
|
282
|
+
icon: Loader2,
|
|
283
|
+
iconClass: 'text-violet-500 animate-spin',
|
|
284
|
+
animated: true,
|
|
285
|
+
progressClass: 'bg-violet-500',
|
|
286
|
+
};
|
|
274
287
|
case 'received':
|
|
275
288
|
return {
|
|
276
289
|
label: 'Concluido',
|
|
@@ -394,6 +407,7 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
394
407
|
} | null>(null);
|
|
395
408
|
const [manualLinkStructureLoading, setManualLinkStructureLoading] = useState(false);
|
|
396
409
|
const [isLinkingLesson, setIsLinkingLesson] = useState(false);
|
|
410
|
+
const [manualLinkIsReassign, setManualLinkIsReassign] = useState(false);
|
|
397
411
|
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
398
412
|
storageKey: 'pagination:global:pageSize',
|
|
399
413
|
defaultValue: PAGE_SIZE_OPTIONS[1],
|
|
@@ -446,7 +460,7 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
446
460
|
},
|
|
447
461
|
});
|
|
448
462
|
|
|
449
|
-
const ACTIVE_STATUSES: UploadStatus[] = ['queued', 'uploading', 'cancelling', 'done'];
|
|
463
|
+
const ACTIVE_STATUSES: UploadStatus[] = ['queued', 'uploading', 'cancelling', 'processing', 'done'];
|
|
450
464
|
|
|
451
465
|
const {
|
|
452
466
|
data,
|
|
@@ -485,7 +499,7 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
485
499
|
enabled: manualLinkOpen,
|
|
486
500
|
queryFn: async () => {
|
|
487
501
|
const response = await request<{ data: Array<{ id: number; title: string; code: string | null }> }>({
|
|
488
|
-
url: '/
|
|
502
|
+
url: '/lms/courses?pageSize=500',
|
|
489
503
|
method: 'GET',
|
|
490
504
|
});
|
|
491
505
|
return response.data;
|
|
@@ -498,6 +512,7 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
498
512
|
() => ({
|
|
499
513
|
sending: rows.filter((row) => row.status === 'uploading').length,
|
|
500
514
|
done: rows.filter((row) => row.status === 'received').length,
|
|
515
|
+
pending: rows.filter((row) => row.status === 'done').length,
|
|
501
516
|
error: rows.filter((row) => row.status === 'error').length,
|
|
502
517
|
}),
|
|
503
518
|
[rows]
|
|
@@ -523,6 +538,15 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
523
538
|
iconContainerClassName: 'bg-green-500/10 text-green-700',
|
|
524
539
|
layout: 'compact' as const,
|
|
525
540
|
},
|
|
541
|
+
{
|
|
542
|
+
key: 'pending',
|
|
543
|
+
title: 'Pendentes',
|
|
544
|
+
value: isLoading ? '-' : stats.pending,
|
|
545
|
+
icon: Clock,
|
|
546
|
+
accentClassName: 'from-blue-500/20 via-blue-500/10 to-transparent',
|
|
547
|
+
iconContainerClassName: 'bg-blue-500/10 text-blue-700',
|
|
548
|
+
layout: 'compact' as const,
|
|
549
|
+
},
|
|
526
550
|
{
|
|
527
551
|
key: 'error',
|
|
528
552
|
title: 'Erros',
|
|
@@ -666,15 +690,29 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
666
690
|
}
|
|
667
691
|
};
|
|
668
692
|
|
|
669
|
-
const openManualLink = async (
|
|
693
|
+
const openManualLink = async (
|
|
694
|
+
itemId: number,
|
|
695
|
+
prefill?: {
|
|
696
|
+
courseId: number | null;
|
|
697
|
+
sessionId: number | null;
|
|
698
|
+
lessonId: number | null;
|
|
699
|
+
}
|
|
700
|
+
) => {
|
|
670
701
|
setManualLinkItemId(itemId);
|
|
671
|
-
setManualLinkCourseId(null);
|
|
672
|
-
setManualLinkSessionId(null);
|
|
673
|
-
setManualLinkLessonId(null);
|
|
702
|
+
setManualLinkCourseId(prefill?.courseId ?? null);
|
|
703
|
+
setManualLinkSessionId(prefill?.sessionId ?? null);
|
|
704
|
+
setManualLinkLessonId(prefill?.lessonId ?? null);
|
|
674
705
|
setManualLinkStructure(null);
|
|
675
706
|
setManualLinkVideoUrl(null);
|
|
707
|
+
setManualLinkIsReassign(Boolean(prefill?.courseId && prefill?.lessonId));
|
|
676
708
|
setManualLinkOpen(true);
|
|
677
709
|
setManualLinkVideoLoading(true);
|
|
710
|
+
if (prefill?.courseId) {
|
|
711
|
+
void loadCourseStructure(prefill.courseId, {
|
|
712
|
+
sessionId: prefill.sessionId,
|
|
713
|
+
lessonId: prefill.lessonId,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
678
716
|
try {
|
|
679
717
|
const res = await request<{ url: string }>({
|
|
680
718
|
url: `/lms/bulk-upload/item/${itemId}/video-url`,
|
|
@@ -688,16 +726,35 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
688
726
|
}
|
|
689
727
|
};
|
|
690
728
|
|
|
691
|
-
const loadCourseStructure = async (
|
|
729
|
+
const loadCourseStructure = async (
|
|
730
|
+
courseId: number,
|
|
731
|
+
keep?: { sessionId: number | null; lessonId: number | null }
|
|
732
|
+
) => {
|
|
692
733
|
setManualLinkStructureLoading(true);
|
|
693
|
-
|
|
694
|
-
|
|
734
|
+
if (!keep) {
|
|
735
|
+
setManualLinkSessionId(null);
|
|
736
|
+
setManualLinkLessonId(null);
|
|
737
|
+
}
|
|
695
738
|
try {
|
|
696
|
-
const res = await request<{
|
|
697
|
-
|
|
739
|
+
const res = await request<{
|
|
740
|
+
sessoes: Array<{ id: string; titulo: string }>;
|
|
741
|
+
aulas: Array<{ id: string; titulo: string; sessaoId: string }>;
|
|
742
|
+
}>({
|
|
743
|
+
url: `/lms/courses/${courseId}/structure`,
|
|
698
744
|
method: 'GET',
|
|
699
745
|
});
|
|
700
|
-
|
|
746
|
+
const sessions = (res.data.sessoes ?? []).map((sessao) => ({
|
|
747
|
+
id: Number(sessao.id),
|
|
748
|
+
title: sessao.titulo,
|
|
749
|
+
lessons: (res.data.aulas ?? [])
|
|
750
|
+
.filter((aula) => aula.sessaoId === sessao.id)
|
|
751
|
+
.map((aula) => ({ id: Number(aula.id), title: aula.titulo })),
|
|
752
|
+
}));
|
|
753
|
+
setManualLinkStructure({ sessions });
|
|
754
|
+
if (keep) {
|
|
755
|
+
setManualLinkSessionId(keep.sessionId);
|
|
756
|
+
setManualLinkLessonId(keep.lessonId);
|
|
757
|
+
}
|
|
701
758
|
} catch {
|
|
702
759
|
setManualLinkStructure(null);
|
|
703
760
|
} finally {
|
|
@@ -1384,7 +1441,7 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1384
1441
|
onSaved={handleProfileSaved}
|
|
1385
1442
|
/>
|
|
1386
1443
|
|
|
1387
|
-
<KpiCardsGrid items={kpiItems} columns={
|
|
1444
|
+
<KpiCardsGrid items={kpiItems} columns={4} />
|
|
1388
1445
|
|
|
1389
1446
|
<div className="flex items-center justify-between gap-3">
|
|
1390
1447
|
<SearchBar
|
|
@@ -1465,9 +1522,21 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1465
1522
|
<FileTypeIcon filename={row.fileName} size={20} />
|
|
1466
1523
|
</span>
|
|
1467
1524
|
<div className="min-w-0 space-y-0.5">
|
|
1468
|
-
<
|
|
1469
|
-
|
|
1470
|
-
|
|
1525
|
+
<TooltipProvider>
|
|
1526
|
+
<Tooltip>
|
|
1527
|
+
<TooltipTrigger asChild>
|
|
1528
|
+
<p className="truncate text-sm font-medium cursor-default">
|
|
1529
|
+
{row.fileName}
|
|
1530
|
+
</p>
|
|
1531
|
+
</TooltipTrigger>
|
|
1532
|
+
<TooltipContent
|
|
1533
|
+
side="top"
|
|
1534
|
+
className="max-w-md break-all"
|
|
1535
|
+
>
|
|
1536
|
+
{row.fileName}
|
|
1537
|
+
</TooltipContent>
|
|
1538
|
+
</Tooltip>
|
|
1539
|
+
</TooltipProvider>
|
|
1471
1540
|
<p className="truncate text-xs text-muted-foreground">
|
|
1472
1541
|
Sessao #{row.sessionId} · {row.appName}
|
|
1473
1542
|
</p>
|
|
@@ -1512,12 +1581,13 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1512
1581
|
|
|
1513
1582
|
<TableCell className="max-w-[22rem]">
|
|
1514
1583
|
{row.matchedCourseId && row.matchedLessonId ? (
|
|
1584
|
+
<div className="flex items-center gap-1">
|
|
1515
1585
|
<button
|
|
1516
1586
|
type="button"
|
|
1517
|
-
className="flex w-
|
|
1587
|
+
className="flex min-w-0 flex-1 items-center gap-2 text-left"
|
|
1518
1588
|
onClick={() =>
|
|
1519
1589
|
window.open(
|
|
1520
|
-
`/lms/courses/${row.matchedCourseId}`,
|
|
1590
|
+
`/lms/courses/${row.matchedCourseId}?sel=${row.matchedLessonId}&type=lesson&tab=videos`,
|
|
1521
1591
|
'_blank',
|
|
1522
1592
|
'noopener,noreferrer'
|
|
1523
1593
|
)
|
|
@@ -1553,7 +1623,33 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1553
1623
|
</p>
|
|
1554
1624
|
</div>
|
|
1555
1625
|
</button>
|
|
1556
|
-
|
|
1626
|
+
<TooltipProvider>
|
|
1627
|
+
<Tooltip>
|
|
1628
|
+
<TooltipTrigger asChild>
|
|
1629
|
+
<Button
|
|
1630
|
+
variant="ghost"
|
|
1631
|
+
size="icon"
|
|
1632
|
+
className="h-7 w-7 shrink-0 text-muted-foreground"
|
|
1633
|
+
onClick={(event) => {
|
|
1634
|
+
event.stopPropagation();
|
|
1635
|
+
openManualLink(row.id, {
|
|
1636
|
+
courseId: row.matchedCourseId,
|
|
1637
|
+
sessionId: row.matchedSessionId,
|
|
1638
|
+
lessonId: row.matchedLessonId,
|
|
1639
|
+
});
|
|
1640
|
+
}}
|
|
1641
|
+
aria-label="Alterar aula vinculada"
|
|
1642
|
+
>
|
|
1643
|
+
<Pencil className="h-3.5 w-3.5" />
|
|
1644
|
+
</Button>
|
|
1645
|
+
</TooltipTrigger>
|
|
1646
|
+
<TooltipContent>
|
|
1647
|
+
Alterar aula vinculada
|
|
1648
|
+
</TooltipContent>
|
|
1649
|
+
</Tooltip>
|
|
1650
|
+
</TooltipProvider>
|
|
1651
|
+
</div>
|
|
1652
|
+
) : (row.status === 'lesson_not_found' || row.status === 'done') ? (
|
|
1557
1653
|
<Button
|
|
1558
1654
|
variant="outline"
|
|
1559
1655
|
size="sm"
|
|
@@ -1569,11 +1665,21 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1569
1665
|
</TableCell>
|
|
1570
1666
|
|
|
1571
1667
|
<TableCell>
|
|
1572
|
-
<div className="flex
|
|
1573
|
-
<
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1668
|
+
<div className="flex flex-col gap-1">
|
|
1669
|
+
<div className="flex items-center gap-1.5">
|
|
1670
|
+
<StatusIcon
|
|
1671
|
+
className={cn('h-3.5 w-3.5 shrink-0', meta.iconClass)}
|
|
1672
|
+
/>
|
|
1673
|
+
<Badge variant={meta.variant}>{meta.label}</Badge>
|
|
1674
|
+
</div>
|
|
1675
|
+
{row.status === 'processing' && row.jobStatus ? (
|
|
1676
|
+
<p className="text-[11px] text-muted-foreground">
|
|
1677
|
+
Job: {row.jobStatus}
|
|
1678
|
+
{row.jobAttempts != null && row.jobMaxAttempts != null
|
|
1679
|
+
? ` (${row.jobAttempts}/${row.jobMaxAttempts})`
|
|
1680
|
+
: ''}
|
|
1681
|
+
</p>
|
|
1682
|
+
) : null}
|
|
1577
1683
|
</div>
|
|
1578
1684
|
</TableCell>
|
|
1579
1685
|
|
|
@@ -1638,10 +1744,14 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1638
1744
|
<SheetHeader className="border-b px-6 py-4">
|
|
1639
1745
|
<SheetTitle className="flex items-center gap-2">
|
|
1640
1746
|
<Link2 className="h-4 w-4" />
|
|
1641
|
-
|
|
1747
|
+
{manualLinkIsReassign
|
|
1748
|
+
? 'Alterar aula vinculada'
|
|
1749
|
+
: 'Vincular aula manualmente'}
|
|
1642
1750
|
</SheetTitle>
|
|
1643
1751
|
<SheetDescription>
|
|
1644
|
-
|
|
1752
|
+
{manualLinkIsReassign
|
|
1753
|
+
? 'Selecione o novo curso, módulo e aula para revincular este vídeo e reiniciar o processamento.'
|
|
1754
|
+
: 'Selecione o curso, módulo e aula para associar este vídeo e iniciar o processamento.'}
|
|
1645
1755
|
</SheetDescription>
|
|
1646
1756
|
</SheetHeader>
|
|
1647
1757
|
|
|
@@ -1739,7 +1849,9 @@ export default function LmsBulkUploadSessionsPage() {
|
|
|
1739
1849
|
) : (
|
|
1740
1850
|
<Link2 className="h-4 w-4" />
|
|
1741
1851
|
)}
|
|
1742
|
-
|
|
1852
|
+
{manualLinkIsReassign
|
|
1853
|
+
? 'Confirmar novo vínculo e reprocessar'
|
|
1854
|
+
: 'Confirmar vínculo e iniciar processamento'}
|
|
1743
1855
|
</Button>
|
|
1744
1856
|
</div>
|
|
1745
1857
|
</ResizableSheetContent>
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
PageHeader,
|
|
8
8
|
PaginationFooter,
|
|
9
9
|
SearchBar,
|
|
10
|
+
ViewModeToggle,
|
|
10
11
|
} from '@/components/entity-list';
|
|
11
12
|
import { Badge } from '@/components/ui/badge';
|
|
12
13
|
import { Button } from '@/components/ui/button';
|
|
@@ -38,8 +39,17 @@ import {
|
|
|
38
39
|
SheetTitle,
|
|
39
40
|
} from '@/components/ui/sheet';
|
|
40
41
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
42
|
+
import {
|
|
43
|
+
Table,
|
|
44
|
+
TableBody,
|
|
45
|
+
TableCell,
|
|
46
|
+
TableHead,
|
|
47
|
+
TableHeader,
|
|
48
|
+
TableRow,
|
|
49
|
+
} from '@/components/ui/table';
|
|
41
50
|
import { Textarea } from '@/components/ui/textarea';
|
|
42
51
|
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
52
|
+
import { usePersistedViewMode } from '@/hooks/use-persisted-view-mode';
|
|
43
53
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
44
54
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
45
55
|
import {
|
|
@@ -96,7 +106,8 @@ type UpdateCertificateTemplatePayload = {
|
|
|
96
106
|
status: TemplateStatus;
|
|
97
107
|
};
|
|
98
108
|
|
|
99
|
-
const
|
|
109
|
+
const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
|
|
110
|
+
type ViewMode = 'cards' | 'list';
|
|
100
111
|
|
|
101
112
|
const createTemplateSchema = (t: (key: string) => string) =>
|
|
102
113
|
z.object({
|
|
@@ -140,10 +151,49 @@ export default function ModelsPage() {
|
|
|
140
151
|
const [searchQuery, setSearchQuery] = useState('');
|
|
141
152
|
const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');
|
|
142
153
|
const [currentPage, setCurrentPage] = useState(1);
|
|
154
|
+
const [viewMode, setViewMode] = usePersistedViewMode<ViewMode>({
|
|
155
|
+
storageKey: 'lms:certificate-models:view-mode',
|
|
156
|
+
defaultValue: 'cards',
|
|
157
|
+
allowedValues: ['cards', 'list'],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const { data: generalSettings } = useQuery<{
|
|
161
|
+
data: Array<{ slug: string; value: string }>;
|
|
162
|
+
}>({
|
|
163
|
+
queryKey: ['setting-group-general'],
|
|
164
|
+
queryFn: async () => {
|
|
165
|
+
const response = await request<{
|
|
166
|
+
data: Array<{ slug: string; value: string }>;
|
|
167
|
+
}>({
|
|
168
|
+
url: '/setting/group/general',
|
|
169
|
+
method: 'GET',
|
|
170
|
+
});
|
|
171
|
+
return response.data;
|
|
172
|
+
},
|
|
173
|
+
staleTime: 5 * 60 * 1000,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const pageSizeOptions = useMemo(() => {
|
|
177
|
+
const setting = generalSettings?.data?.find(
|
|
178
|
+
(s) => s.slug === 'pagination-page-sizes'
|
|
179
|
+
);
|
|
180
|
+
if (!setting?.value) return DEFAULT_PAGE_SIZES;
|
|
181
|
+
try {
|
|
182
|
+
const parsed = JSON.parse(setting.value) as string[];
|
|
183
|
+
const sizes = parsed
|
|
184
|
+
.map(Number)
|
|
185
|
+
.filter((n) => !isNaN(n) && n > 0)
|
|
186
|
+
.sort((a, b) => a - b);
|
|
187
|
+
return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
|
|
188
|
+
} catch {
|
|
189
|
+
return DEFAULT_PAGE_SIZES;
|
|
190
|
+
}
|
|
191
|
+
}, [generalSettings]);
|
|
192
|
+
|
|
143
193
|
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
144
194
|
storageKey: 'pagination:global:pageSize',
|
|
145
195
|
defaultValue: 12,
|
|
146
|
-
allowedValues:
|
|
196
|
+
allowedValues: pageSizeOptions,
|
|
147
197
|
});
|
|
148
198
|
const [isSheetOpen, setIsSheetOpen] = useState(false);
|
|
149
199
|
const [isEditSheetOpen, setIsEditSheetOpen] = useState(false);
|
|
@@ -540,6 +590,14 @@ export default function ModelsPage() {
|
|
|
540
590
|
],
|
|
541
591
|
},
|
|
542
592
|
]}
|
|
593
|
+
actions={
|
|
594
|
+
<ViewModeToggle
|
|
595
|
+
viewMode={viewMode}
|
|
596
|
+
onViewModeChange={setViewMode}
|
|
597
|
+
listLabel={t('viewMode.list')}
|
|
598
|
+
cardsLabel={t('viewMode.cards')}
|
|
599
|
+
/>
|
|
600
|
+
}
|
|
543
601
|
/>
|
|
544
602
|
|
|
545
603
|
<div className="flex items-center justify-between gap-3">
|
|
@@ -567,31 +625,67 @@ export default function ModelsPage() {
|
|
|
567
625
|
</div>
|
|
568
626
|
|
|
569
627
|
{loading ? (
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
<
|
|
579
|
-
<
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
<
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
<
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
628
|
+
viewMode === 'cards' ? (
|
|
629
|
+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
630
|
+
{Array.from({ length: 6 }).map((_, index) => (
|
|
631
|
+
<Card
|
|
632
|
+
key={index}
|
|
633
|
+
className="overflow-hidden border-border/70 py-0"
|
|
634
|
+
>
|
|
635
|
+
<div className="h-1 w-full bg-linear-to-r from-slate-300/70 via-slate-200 to-transparent" />
|
|
636
|
+
<CardContent className="space-y-4 p-5">
|
|
637
|
+
<div className="flex items-center gap-2">
|
|
638
|
+
<Skeleton className="h-6 w-24 rounded-full" />
|
|
639
|
+
<Skeleton className="h-6 w-20 rounded-full" />
|
|
640
|
+
</div>
|
|
641
|
+
<div>
|
|
642
|
+
<Skeleton className="mb-2 h-5 w-3/4" />
|
|
643
|
+
<Skeleton className="h-4 w-1/2" />
|
|
644
|
+
</div>
|
|
645
|
+
<div className="grid grid-cols-2 gap-3">
|
|
646
|
+
<Skeleton className="h-14 w-full rounded-xl" />
|
|
647
|
+
<Skeleton className="h-14 w-full rounded-xl" />
|
|
648
|
+
</div>
|
|
649
|
+
<Skeleton className="h-9 w-36 rounded-md" />
|
|
650
|
+
</CardContent>
|
|
651
|
+
</Card>
|
|
652
|
+
))}
|
|
653
|
+
</div>
|
|
654
|
+
) : (
|
|
655
|
+
<div className="overflow-hidden rounded-xl border border-border/70">
|
|
656
|
+
<Table>
|
|
657
|
+
<TableHeader>
|
|
658
|
+
<TableRow>
|
|
659
|
+
<TableHead>{t('table.name')}</TableHead>
|
|
660
|
+
<TableHead>{t('table.status')}</TableHead>
|
|
661
|
+
<TableHead>{t('table.updatedAt')}</TableHead>
|
|
662
|
+
<TableHead className="w-12" />
|
|
663
|
+
</TableRow>
|
|
664
|
+
</TableHeader>
|
|
665
|
+
<TableBody>
|
|
666
|
+
{Array.from({ length: 6 }).map((_, i) => (
|
|
667
|
+
<TableRow key={i}>
|
|
668
|
+
<TableCell>
|
|
669
|
+
<div className="space-y-1.5">
|
|
670
|
+
<Skeleton className="h-4 w-48" />
|
|
671
|
+
<Skeleton className="h-3 w-32" />
|
|
672
|
+
</div>
|
|
673
|
+
</TableCell>
|
|
674
|
+
<TableCell>
|
|
675
|
+
<Skeleton className="h-5 w-20 rounded-full" />
|
|
676
|
+
</TableCell>
|
|
677
|
+
<TableCell>
|
|
678
|
+
<Skeleton className="h-4 w-24" />
|
|
679
|
+
</TableCell>
|
|
680
|
+
<TableCell>
|
|
681
|
+
<Skeleton className="ml-auto size-8 rounded-md" />
|
|
682
|
+
</TableCell>
|
|
683
|
+
</TableRow>
|
|
684
|
+
))}
|
|
685
|
+
</TableBody>
|
|
686
|
+
</Table>
|
|
687
|
+
</div>
|
|
688
|
+
)
|
|
595
689
|
) : totalItems === 0 ? (
|
|
596
690
|
<EmptyState
|
|
597
691
|
icon={<Files className="size-12 text-muted-foreground/40" />}
|
|
@@ -602,7 +696,7 @@ export default function ModelsPage() {
|
|
|
602
696
|
onAction={openCreateSheet}
|
|
603
697
|
className="py-20"
|
|
604
698
|
/>
|
|
605
|
-
) : (
|
|
699
|
+
) : viewMode === 'cards' ? (
|
|
606
700
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
607
701
|
{templates.map((template) => (
|
|
608
702
|
<Card
|
|
@@ -684,6 +778,65 @@ export default function ModelsPage() {
|
|
|
684
778
|
</Card>
|
|
685
779
|
))}
|
|
686
780
|
</div>
|
|
781
|
+
) : (
|
|
782
|
+
<div className="overflow-hidden rounded-xl border border-border/70">
|
|
783
|
+
<Table>
|
|
784
|
+
<TableHeader>
|
|
785
|
+
<TableRow>
|
|
786
|
+
<TableHead>{t('table.name')}</TableHead>
|
|
787
|
+
<TableHead>{t('table.status')}</TableHead>
|
|
788
|
+
<TableHead>{t('table.updatedAt')}</TableHead>
|
|
789
|
+
<TableHead className="w-12" />
|
|
790
|
+
</TableRow>
|
|
791
|
+
</TableHeader>
|
|
792
|
+
<TableBody>
|
|
793
|
+
{templates.map((template) => (
|
|
794
|
+
<TableRow
|
|
795
|
+
key={template.id}
|
|
796
|
+
className="cursor-pointer"
|
|
797
|
+
onClick={() => handleCardClick(template)}
|
|
798
|
+
>
|
|
799
|
+
<TableCell>
|
|
800
|
+
<p className="font-semibold text-foreground">
|
|
801
|
+
{template.name}
|
|
802
|
+
</p>
|
|
803
|
+
<p className="font-mono text-xs text-muted-foreground">
|
|
804
|
+
{template.slug}
|
|
805
|
+
</p>
|
|
806
|
+
</TableCell>
|
|
807
|
+
<TableCell>{renderStatusBadge(template.status)}</TableCell>
|
|
808
|
+
<TableCell className="text-sm text-muted-foreground">
|
|
809
|
+
{formatDate(resolveUpdatedAt(template))}
|
|
810
|
+
</TableCell>
|
|
811
|
+
<TableCell onClick={(e) => e.stopPropagation()}>
|
|
812
|
+
<IconActionGroup
|
|
813
|
+
className="ml-auto"
|
|
814
|
+
actions={[
|
|
815
|
+
{
|
|
816
|
+
key: 'editor',
|
|
817
|
+
label: t('cards.actions.editTemplate'),
|
|
818
|
+
icon: <FileEdit className="size-4" />,
|
|
819
|
+
onClick: () =>
|
|
820
|
+
router.push(
|
|
821
|
+
`/lms/certificates/models/editor?templateId=${template.id}`
|
|
822
|
+
),
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
key: 'delete',
|
|
826
|
+
label: t('cards.actions.deleteTemplate'),
|
|
827
|
+
icon: <Trash2 className="size-4" />,
|
|
828
|
+
onClick: () => onDeleteTemplate(template),
|
|
829
|
+
destructive: true,
|
|
830
|
+
disabled: deletingTemplateId === template.id,
|
|
831
|
+
},
|
|
832
|
+
]}
|
|
833
|
+
/>
|
|
834
|
+
</TableCell>
|
|
835
|
+
</TableRow>
|
|
836
|
+
))}
|
|
837
|
+
</TableBody>
|
|
838
|
+
</Table>
|
|
839
|
+
</div>
|
|
687
840
|
)}
|
|
688
841
|
|
|
689
842
|
{!loading && totalItems > 0 ? (
|
|
@@ -696,7 +849,7 @@ export default function ModelsPage() {
|
|
|
696
849
|
setPageSize(value);
|
|
697
850
|
setCurrentPage(1);
|
|
698
851
|
}}
|
|
699
|
-
pageSizeOptions={
|
|
852
|
+
pageSizeOptions={pageSizeOptions}
|
|
700
853
|
/>
|
|
701
854
|
) : null}
|
|
702
855
|
</div>
|