@hed-hog/lms 0.0.361 → 0.0.364
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +65 -0
- package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
- package/dist/bitcode-wallet/bitcode-wallet.service.js +72 -0
- package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts +8 -0
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts.map +1 -0
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js +40 -0
- package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js.map +1 -0
- package/dist/class-group/class-group.controller.d.ts +16 -16
- package/dist/class-group/class-group.service.d.ts +12 -12
- package/dist/course/course-audio-transcription.service.d.ts +3 -2
- package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
- package/dist/course/course-audio-transcription.service.js +49 -8
- package/dist/course/course-audio-transcription.service.js.map +1 -1
- package/dist/course/course-lesson.controller.d.ts +4 -0
- package/dist/course/course-lesson.controller.d.ts.map +1 -1
- package/dist/course/course-lesson.controller.js +10 -0
- package/dist/course/course-lesson.controller.js.map +1 -1
- package/dist/course/course-structure.controller.d.ts +11 -4
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +14 -0
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +15 -1
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +139 -4
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-conversion.service.d.ts +8 -0
- package/dist/course/course-video-conversion.service.d.ts.map +1 -1
- package/dist/course/course-video-conversion.service.js +87 -51
- package/dist/course/course-video-conversion.service.js.map +1 -1
- package/dist/course/course.controller.d.ts +73 -1
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +27 -3
- 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 +2 -0
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +108 -4
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +631 -30
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/cleanup-course-storage.dto.d.ts +6 -0
- package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -0
- package/dist/course/dto/cleanup-course-storage.dto.js +33 -0
- package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -0
- package/dist/course/dto/cleanup-upload-history.dto.d.ts +9 -0
- package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -0
- package/dist/course/dto/cleanup-upload-history.dto.js +36 -0
- package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -0
- package/dist/course/dto/create-course-bulk-job.dto.d.ts +4 -0
- package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -0
- package/dist/course/dto/create-course-bulk-job.dto.js +21 -0
- package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -0
- package/dist/course/lms-bulk-upload-automation.service.d.ts +39 -0
- package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -0
- package/dist/course/lms-bulk-upload-automation.service.js +443 -0
- package/dist/course/lms-bulk-upload-automation.service.js.map +1 -0
- package/dist/course/lms-bulk-upload-infra.service.d.ts +31 -0
- package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -0
- package/dist/course/lms-bulk-upload-infra.service.js +277 -0
- package/dist/course/lms-bulk-upload-infra.service.js.map +1 -0
- package/dist/course/lms-bulk-upload.constants.d.ts +4 -0
- package/dist/course/lms-bulk-upload.constants.d.ts.map +1 -0
- package/dist/course/lms-bulk-upload.constants.js +7 -0
- package/dist/course/lms-bulk-upload.constants.js.map +1 -0
- package/dist/course/lms-bulk-upload.controller.d.ts +116 -0
- package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.controller.js +71 -2
- package/dist/course/lms-bulk-upload.controller.js.map +1 -1
- package/dist/course/lms-bulk-upload.service.d.ts +142 -3
- package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
- package/dist/course/lms-bulk-upload.service.js +606 -21
- package/dist/course/lms-bulk-upload.service.js.map +1 -1
- package/dist/course/lms-setting.controller.d.ts +4 -1
- package/dist/course/lms-setting.controller.d.ts.map +1 -1
- package/dist/course/lms-setting.controller.js +21 -6
- package/dist/course/lms-setting.controller.js.map +1 -1
- package/dist/enterprise/enterprise.controller.d.ts +20 -20
- package/dist/enterprise/enterprise.service.d.ts +20 -20
- package/dist/enterprise/training/training-admin.controller.d.ts +11 -11
- package/dist/enterprise/training/training-admin.service.d.ts +11 -11
- package/dist/enterprise/training/training-instructor.controller.d.ts +2 -2
- package/dist/enterprise/training/training-instructor.service.d.ts +2 -2
- package/dist/enterprise/training/training-student.controller.d.ts +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +1 -1
- package/dist/enterprise/training/training-viewer.controller.d.ts +2 -2
- package/dist/evaluation/evaluation.controller.d.ts +8 -8
- package/dist/evaluation/evaluation.service.d.ts +8 -8
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts +6 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js +34 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts +28 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js +123 -0
- package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts +4 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js +22 -0
- package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts +10 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js +52 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts +15 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts.map +1 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js +86 -0
- package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +26 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +304 -0
- package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts +87 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.js +185 -0
- package/dist/lesson-xp-map/lesson-xp-map.controller.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.d.ts +3 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.js +34 -0
- package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.d.ts +84 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.js +353 -0
- package/dist/lesson-xp-map/lesson-xp-map.service.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts +10 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.js +63 -0
- package/dist/lesson-xp-map/lesson-xp-segment.controller.js.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts +27 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts.map +1 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.js +194 -0
- package/dist/lesson-xp-map/lesson-xp-segment.service.js.map +1 -0
- package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -0
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +17 -2
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/dto/update-profile.dto.d.ts +17 -0
- package/dist/platforma/dto/update-profile.dto.d.ts.map +1 -0
- package/dist/platforma/dto/update-profile.dto.js +87 -0
- package/dist/platforma/dto/update-profile.dto.js.map +1 -0
- package/dist/platforma/platforma.controller.d.ts +88 -1
- package/dist/platforma/platforma.controller.d.ts.map +1 -1
- package/dist/platforma/platforma.controller.js +85 -2
- package/dist/platforma/platforma.controller.js.map +1 -1
- package/dist/platforma/platforma.service.d.ts +27 -0
- package/dist/platforma/platforma.service.d.ts.map +1 -0
- package/dist/platforma/platforma.service.js +274 -0
- package/dist/platforma/platforma.service.js.map +1 -0
- package/dist/student-xp/student-xp.controller.d.ts +41 -0
- package/dist/student-xp/student-xp.controller.d.ts.map +1 -0
- package/dist/student-xp/student-xp.controller.js +114 -0
- package/dist/student-xp/student-xp.controller.js.map +1 -0
- package/dist/student-xp/student-xp.module.d.ts +3 -0
- package/dist/student-xp/student-xp.module.d.ts.map +1 -0
- package/dist/student-xp/student-xp.module.js +25 -0
- package/dist/student-xp/student-xp.module.js.map +1 -0
- package/dist/student-xp/student-xp.service.d.ts +65 -0
- package/dist/student-xp/student-xp.service.d.ts.map +1 -0
- package/dist/student-xp/student-xp.service.js +197 -0
- package/dist/student-xp/student-xp.service.js.map +1 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.d.ts +12 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.js +63 -0
- package/dist/xp-catalog/dto/create-xp-area.dto.js.map +1 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.js +57 -0
- package/dist/xp-catalog/dto/create-xp-learning-type.dto.js.map +1 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.js +57 -0
- package/dist/xp-catalog/dto/create-xp-skill.dto.js.map +1 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.d.ts +12 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.js +66 -0
- package/dist/xp-catalog/dto/update-xp-area.dto.js.map +1 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.js +60 -0
- package/dist/xp-catalog/dto/update-xp-learning-type.dto.js.map +1 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts +11 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts.map +1 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.js +60 -0
- package/dist/xp-catalog/dto/update-xp-skill.dto.js.map +1 -0
- package/dist/xp-catalog/xp-area.controller.d.ts +25 -0
- package/dist/xp-catalog/xp-area.controller.d.ts.map +1 -0
- package/dist/xp-catalog/xp-area.controller.js +105 -0
- package/dist/xp-catalog/xp-area.controller.js.map +1 -0
- package/dist/xp-catalog/xp-area.service.d.ts +35 -0
- package/dist/xp-catalog/xp-area.service.d.ts.map +1 -0
- package/dist/xp-catalog/xp-area.service.js +168 -0
- package/dist/xp-catalog/xp-area.service.js.map +1 -0
- package/dist/xp-catalog/xp-catalog.module.d.ts +3 -0
- package/dist/xp-catalog/xp-catalog.module.d.ts.map +1 -0
- package/dist/xp-catalog/xp-catalog.module.js +29 -0
- package/dist/xp-catalog/xp-catalog.module.js.map +1 -0
- package/dist/xp-catalog/xp-learning-type.controller.d.ts +20 -0
- package/dist/xp-catalog/xp-learning-type.controller.d.ts.map +1 -0
- package/dist/xp-catalog/xp-learning-type.controller.js +96 -0
- package/dist/xp-catalog/xp-learning-type.controller.js.map +1 -0
- package/dist/xp-catalog/xp-learning-type.service.d.ts +30 -0
- package/dist/xp-catalog/xp-learning-type.service.d.ts.map +1 -0
- package/dist/xp-catalog/xp-learning-type.service.js +146 -0
- package/dist/xp-catalog/xp-learning-type.service.js.map +1 -0
- package/dist/xp-catalog/xp-skill.controller.d.ts +26 -0
- package/dist/xp-catalog/xp-skill.controller.d.ts.map +1 -0
- package/dist/xp-catalog/xp-skill.controller.js +113 -0
- package/dist/xp-catalog/xp-skill.controller.js.map +1 -0
- package/dist/xp-catalog/xp-skill.service.d.ts +37 -0
- package/dist/xp-catalog/xp-skill.service.d.ts.map +1 -0
- package/dist/xp-catalog/xp-skill.service.js +174 -0
- package/dist/xp-catalog/xp-skill.service.js.map +1 -0
- package/hedhog/data/menu.yaml +101 -0
- package/hedhog/data/route.yaml +512 -0
- package/hedhog/data/setting_group.yaml +1 -1
- package/hedhog/data/xp_area.yaml +164 -0
- package/hedhog/data/xp_learning_type.yaml +131 -0
- package/hedhog/data/xp_skill.yaml +1834 -0
- package/hedhog/frontend/app/achievements/page.tsx.ejs +108 -118
- package/hedhog/frontend/app/bitcodes/page.tsx.ejs +22 -34
- package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +1453 -0
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +21 -45
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +40 -74
- package/hedhog/frontend/app/classes/page.tsx.ejs +56 -85
- package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +3 -2
- package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +48 -5
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +4 -4
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +19 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +1170 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +16 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +2 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +623 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +1458 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +55 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +442 -104
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +296 -49
- package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +101 -85
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +21 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +7 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +44 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +54 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +52 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-xp-overview.ts.ejs +76 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-xp-map.ts.ejs +128 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +30 -0
- package/hedhog/frontend/app/courses/[id]/structure/_utils/xp-color-config.ts.ejs +115 -0
- package/hedhog/frontend/app/courses/_components/CourseDeleteDialog.tsx.ejs +223 -0
- package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +89 -0
- package/hedhog/frontend/app/courses/page.tsx.ejs +400 -230
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -63
- package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +53 -77
- package/hedhog/frontend/app/exams/page.tsx.ejs +54 -90
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +23 -36
- package/hedhog/frontend/app/instructors/page.tsx.ejs +72 -81
- package/hedhog/frontend/app/paths/page.tsx.ejs +40 -68
- package/hedhog/frontend/app/training/page.tsx.ejs +40 -68
- package/hedhog/frontend/app/xp/areas/page.tsx.ejs +782 -0
- package/hedhog/frontend/app/xp/learning-types/page.tsx.ejs +690 -0
- package/hedhog/frontend/app/xp/skills/page.tsx.ejs +811 -0
- package/hedhog/frontend/messages/en.json +386 -3
- package/hedhog/frontend/messages/pt.json +386 -3
- package/hedhog/table/lesson_xp_map.yaml +50 -0
- package/hedhog/table/lesson_xp_segment.yaml +40 -0
- package/hedhog/table/lesson_xp_segment_area.yaml +24 -0
- package/hedhog/table/lesson_xp_segment_learning_type.yaml +24 -0
- package/hedhog/table/lesson_xp_segment_skill.yaml +24 -0
- package/hedhog/table/lms_bulk_upload_item.yaml +44 -0
- package/hedhog/table/lms_bulk_upload_session.yaml +42 -0
- package/hedhog/table/student_area_xp.yaml +30 -0
- package/hedhog/table/student_learning_type_xp.yaml +30 -0
- package/hedhog/table/student_skill_xp.yaml +30 -0
- package/hedhog/table/student_xp_event.yaml +34 -0
- package/hedhog/table/xp_area.yaml +39 -0
- package/hedhog/table/xp_learning_type.yaml +34 -0
- package/hedhog/table/xp_skill.yaml +39 -0
- package/package.json +8 -7
- package/src/bitcode-wallet/bitcode-wallet.service.ts +113 -0
- package/src/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.ts +32 -0
- package/src/course/course-audio-transcription.service.ts +58 -21
- package/src/course/course-lesson.controller.ts +6 -1
- package/src/course/course-structure.controller.ts +10 -0
- package/src/course/course-structure.service.ts +174 -1
- package/src/course/course-video-conversion.service.ts +113 -75
- package/src/course/course.controller.ts +22 -3
- package/src/course/course.module.ts +2 -0
- package/src/course/course.service.ts +847 -30
- package/src/course/dto/cleanup-course-storage.dto.ts +22 -0
- package/src/course/dto/cleanup-upload-history.dto.ts +26 -0
- package/src/course/dto/create-course-bulk-job.dto.ts +6 -0
- package/src/course/lms-bulk-upload-automation.service.ts +560 -0
- package/src/course/lms-bulk-upload-infra.service.ts +327 -0
- package/src/course/lms-bulk-upload.constants.ts +5 -0
- package/src/course/lms-bulk-upload.controller.ts +79 -3
- package/src/course/lms-bulk-upload.service.ts +1029 -204
- package/src/course/lms-setting.controller.ts +22 -6
- package/src/lesson-xp-map/dto/create-lesson-xp-map.dto.ts +17 -0
- package/src/lesson-xp-map/dto/create-lesson-xp-segment.dto.ts +102 -0
- package/src/lesson-xp-map/dto/review-lesson-xp-map.dto.ts +7 -0
- package/src/lesson-xp-map/dto/update-lesson-xp-map.dto.ts +36 -0
- package/src/lesson-xp-map/dto/update-lesson-xp-segment.dto.ts +78 -0
- package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +396 -0
- package/src/lesson-xp-map/lesson-xp-map.controller.ts +116 -0
- package/src/lesson-xp-map/lesson-xp-map.module.ts +21 -0
- package/src/lesson-xp-map/lesson-xp-map.service.ts +442 -0
- package/src/lesson-xp-map/lesson-xp-segment.controller.ts +36 -0
- package/src/lesson-xp-map/lesson-xp-segment.service.ts +229 -0
- package/src/lms.module.ts +17 -2
- package/src/platforma/dto/update-profile.dto.ts +59 -0
- package/src/platforma/platforma.controller.ts +57 -2
- package/src/platforma/platforma.service.ts +268 -0
- package/src/student-xp/student-xp.controller.ts +76 -0
- package/src/student-xp/student-xp.module.ts +12 -0
- package/src/student-xp/student-xp.service.ts +236 -0
- package/src/xp-catalog/dto/create-xp-area.dto.ts +40 -0
- package/src/xp-catalog/dto/create-xp-learning-type.dto.ts +35 -0
- package/src/xp-catalog/dto/create-xp-skill.dto.ts +35 -0
- package/src/xp-catalog/dto/update-xp-area.dto.ts +43 -0
- package/src/xp-catalog/dto/update-xp-learning-type.dto.ts +38 -0
- package/src/xp-catalog/dto/update-xp-skill.dto.ts +38 -0
- package/src/xp-catalog/xp-area.controller.ts +64 -0
- package/src/xp-catalog/xp-area.service.ts +196 -0
- package/src/xp-catalog/xp-catalog.module.ts +16 -0
- package/src/xp-catalog/xp-learning-type.controller.ts +59 -0
- package/src/xp-catalog/xp-learning-type.service.ts +170 -0
- package/src/xp-catalog/xp-skill.controller.ts +71 -0
- package/src/xp-catalog/xp-skill.service.ts +205 -0
|
@@ -6,12 +6,14 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
6
6
|
import {
|
|
7
7
|
AlertTriangle,
|
|
8
8
|
BookOpen,
|
|
9
|
+
Briefcase,
|
|
9
10
|
CheckCircle2,
|
|
10
11
|
CircleDot,
|
|
11
12
|
Clock,
|
|
12
13
|
Download,
|
|
13
14
|
Layers,
|
|
14
15
|
Loader2,
|
|
16
|
+
Mic,
|
|
15
17
|
Pencil,
|
|
16
18
|
Plus,
|
|
17
19
|
Save,
|
|
@@ -19,6 +21,7 @@ import {
|
|
|
19
21
|
UploadCloud,
|
|
20
22
|
Video,
|
|
21
23
|
X,
|
|
24
|
+
Zap,
|
|
22
25
|
} from 'lucide-react';
|
|
23
26
|
import { useTranslations } from 'next-intl';
|
|
24
27
|
import { useRouter } from 'next/navigation';
|
|
@@ -31,17 +34,27 @@ import { createDefaultTemplate } from '@/app/(app)/(libraries)/lms/_lib/editor/t
|
|
|
31
34
|
import { FfmpegParamsEditor } from '@/components/ffmpeg-params-editor';
|
|
32
35
|
import { FileTypeIcon } from '@/components/file-type-icon';
|
|
33
36
|
import { RichTextEditor } from '@/components/rich-text-editor';
|
|
37
|
+
import {
|
|
38
|
+
AlertDialog,
|
|
39
|
+
AlertDialogAction,
|
|
40
|
+
AlertDialogCancel,
|
|
41
|
+
AlertDialogContent,
|
|
42
|
+
AlertDialogDescription,
|
|
43
|
+
AlertDialogFooter,
|
|
44
|
+
AlertDialogHeader,
|
|
45
|
+
AlertDialogTitle,
|
|
46
|
+
} from '@/components/ui/alert-dialog';
|
|
34
47
|
import { Badge } from '@/components/ui/badge';
|
|
35
48
|
import { Button } from '@/components/ui/button';
|
|
36
49
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
37
50
|
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} from '@/components/ui/
|
|
51
|
+
DropdownMenu,
|
|
52
|
+
DropdownMenuContent,
|
|
53
|
+
DropdownMenuItem,
|
|
54
|
+
DropdownMenuLabel,
|
|
55
|
+
DropdownMenuSeparator,
|
|
56
|
+
DropdownMenuTrigger,
|
|
57
|
+
} from '@/components/ui/dropdown-menu';
|
|
45
58
|
import { EntityPicker } from '@/components/ui/entity-picker';
|
|
46
59
|
import {
|
|
47
60
|
Form,
|
|
@@ -74,6 +87,7 @@ import { cn } from '@/lib/utils';
|
|
|
74
87
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
75
88
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
76
89
|
|
|
90
|
+
import { CourseDeleteDialog } from '../../../_components/CourseDeleteDialog';
|
|
77
91
|
import type {
|
|
78
92
|
CourseEditFormValues,
|
|
79
93
|
PickerOption,
|
|
@@ -83,7 +97,6 @@ import { CourseClassificationCard } from '../../_components/CourseClassification
|
|
|
83
97
|
import { CourseContentCard } from '../../_components/CourseContentCard';
|
|
84
98
|
import { CourseDangerZoneCard } from '../../_components/CourseDangerZoneCard';
|
|
85
99
|
import { CourseFlagsCard } from '../../_components/CourseFlagsCard';
|
|
86
|
-
import { CourseInstructorsSummaryCard } from './CourseInstructorsSummaryCard';
|
|
87
100
|
import { CourseMediaCard } from '../../_components/CourseMediaCard';
|
|
88
101
|
import { CourseRelationsCard } from '../../_components/CourseRelationsCard';
|
|
89
102
|
import {
|
|
@@ -94,8 +107,12 @@ import {
|
|
|
94
107
|
} from '../_data/services/course-structure.service';
|
|
95
108
|
import { useCreateSessionMutation } from '../_data/use-course-structure-mutations';
|
|
96
109
|
import { courseStructureQueryKey } from '../_data/use-course-structure-query';
|
|
97
|
-
import {
|
|
110
|
+
import { useLmsSettingsQuery } from '../_data/use-lms-settings-query';
|
|
98
111
|
import { CourseOperationsTab } from './course-operations-tab';
|
|
112
|
+
import { CourseOverviewTab } from './course-overview-tab';
|
|
113
|
+
import { CourseXpOverviewTab } from './course-xp-overview-tab';
|
|
114
|
+
import { CourseInstructorsSummaryCard } from './CourseInstructorsSummaryCard';
|
|
115
|
+
import { IconActionTooltip } from './icon-action-tooltip';
|
|
99
116
|
import { useStructureStore } from './store';
|
|
100
117
|
import { Resource } from './types';
|
|
101
118
|
|
|
@@ -129,6 +146,8 @@ type ApiCourseDetail = {
|
|
|
129
146
|
instructors?: Array<{ id: number; name: string; avatarId: number | null }>;
|
|
130
147
|
logoFileId?: number | null;
|
|
131
148
|
logoFilename?: string | null;
|
|
149
|
+
logoFileSize?: number | null;
|
|
150
|
+
logoMimeType?: string | null;
|
|
132
151
|
logoImageType?: {
|
|
133
152
|
suggestedWidth: number | null;
|
|
134
153
|
suggestedHeight: number | null;
|
|
@@ -137,6 +156,8 @@ type ApiCourseDetail = {
|
|
|
137
156
|
} | null;
|
|
138
157
|
bannerFileId?: number | null;
|
|
139
158
|
bannerFilename?: string | null;
|
|
159
|
+
bannerFileSize?: number | null;
|
|
160
|
+
bannerMimeType?: string | null;
|
|
140
161
|
bannerImageType?: {
|
|
141
162
|
suggestedWidth: number | null;
|
|
142
163
|
suggestedHeight: number | null;
|
|
@@ -147,6 +168,26 @@ type ApiCourseDetail = {
|
|
|
147
168
|
operationsProjectId?: number | null;
|
|
148
169
|
operationsProjectCode?: string | null;
|
|
149
170
|
operationsProjectName?: string | null;
|
|
171
|
+
deletionImpact?: ApiCourseDeletionImpact;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
type ApiCourseDeletionImpact = {
|
|
175
|
+
fileCount: number;
|
|
176
|
+
totalBytes: number;
|
|
177
|
+
formattedSize: string;
|
|
178
|
+
countsBySource?: Record<string, number>;
|
|
179
|
+
enrollmentCount?: number;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
type ApiQueuedDeleteResponse = {
|
|
183
|
+
success: boolean;
|
|
184
|
+
status: 'queued';
|
|
185
|
+
queueJobId: number;
|
|
186
|
+
notificationId: number;
|
|
187
|
+
deletionImpact?: Pick<
|
|
188
|
+
ApiCourseDeletionImpact,
|
|
189
|
+
'fileCount' | 'totalBytes' | 'formattedSize'
|
|
190
|
+
>;
|
|
150
191
|
};
|
|
151
192
|
|
|
152
193
|
type VideoProfileOption = {
|
|
@@ -316,6 +357,24 @@ function formatFileSize(bytes: number): string {
|
|
|
316
357
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
317
358
|
}
|
|
318
359
|
|
|
360
|
+
function formatBytes(bytes: number) {
|
|
361
|
+
if (!Number.isFinite(bytes) || bytes <= 0) {
|
|
362
|
+
return '0 B';
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
366
|
+
let value = bytes;
|
|
367
|
+
let unitIndex = 0;
|
|
368
|
+
|
|
369
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
370
|
+
value /= 1024;
|
|
371
|
+
unitIndex += 1;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const digits = unitIndex === 0 ? 0 : value >= 10 ? 1 : 2;
|
|
375
|
+
return `${value.toFixed(digits)} ${units[unitIndex]}`;
|
|
376
|
+
}
|
|
377
|
+
|
|
319
378
|
function mapApiCourseResourceToLocal(
|
|
320
379
|
item: CourseResourceApi
|
|
321
380
|
): CourseResourceItem {
|
|
@@ -378,6 +437,65 @@ function buildSchema(t: (key: string) => string) {
|
|
|
378
437
|
});
|
|
379
438
|
}
|
|
380
439
|
|
|
440
|
+
// ── Skeleton ──────────────────────────────────────────────────────────────────
|
|
441
|
+
|
|
442
|
+
function EditorCourseSkeleton() {
|
|
443
|
+
return (
|
|
444
|
+
<div className="flex flex-col h-full min-h-0">
|
|
445
|
+
{/* Header */}
|
|
446
|
+
<div className="flex items-center gap-3 px-4 py-3 border-b bg-muted/30 shrink-0">
|
|
447
|
+
<Skeleton className="size-9 rounded-lg shrink-0" />
|
|
448
|
+
<div className="flex-1 min-w-0 flex flex-col gap-1">
|
|
449
|
+
<Skeleton className="h-4 w-16" />
|
|
450
|
+
<Skeleton className="h-3 w-32" />
|
|
451
|
+
</div>
|
|
452
|
+
<div className="flex items-center gap-1.5">
|
|
453
|
+
<Skeleton className="h-5 w-12 rounded-full" />
|
|
454
|
+
<Skeleton className="h-5 w-12 rounded-full" />
|
|
455
|
+
<Skeleton className="h-5 w-14 rounded-full" />
|
|
456
|
+
<Skeleton className="h-5 w-16 rounded-full" />
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
{/* Tabs bar */}
|
|
460
|
+
<div className="flex items-center gap-1 px-3 py-1 border-b bg-muted/50 shrink-0 overflow-hidden">
|
|
461
|
+
{Array.from({ length: 6 }).map((_, i) => (
|
|
462
|
+
<Skeleton key={i} className="h-7 w-16 rounded-md shrink-0" />
|
|
463
|
+
))}
|
|
464
|
+
</div>
|
|
465
|
+
{/* Tab content — estrutura */}
|
|
466
|
+
<div className="flex-1 min-h-0 overflow-y-auto p-3 flex flex-col gap-3">
|
|
467
|
+
<div className="rounded-lg border bg-muted/20 p-3 flex flex-col gap-3">
|
|
468
|
+
<Skeleton className="h-3 w-28" />
|
|
469
|
+
{[80, 56, 96].map((w, i) => (
|
|
470
|
+
<div key={i} className="flex flex-col gap-1">
|
|
471
|
+
<Skeleton className="h-3 w-20" />
|
|
472
|
+
<Skeleton className={`h-8 w-full max-w-[${w}%]`} />
|
|
473
|
+
</div>
|
|
474
|
+
))}
|
|
475
|
+
</div>
|
|
476
|
+
<div className="rounded-lg border bg-muted/20 p-3 flex flex-col gap-3">
|
|
477
|
+
<Skeleton className="h-3 w-24" />
|
|
478
|
+
<div className="flex flex-col gap-1">
|
|
479
|
+
<Skeleton className="h-3 w-16" />
|
|
480
|
+
<Skeleton className="h-20 w-full" />
|
|
481
|
+
</div>
|
|
482
|
+
</div>
|
|
483
|
+
<div className="rounded-lg border bg-muted/20 p-3 flex flex-col gap-3">
|
|
484
|
+
<Skeleton className="h-3 w-20" />
|
|
485
|
+
<div className="grid grid-cols-2 gap-2">
|
|
486
|
+
{[1, 2, 3, 4].map((i) => (
|
|
487
|
+
<div key={i} className="flex flex-col gap-1">
|
|
488
|
+
<Skeleton className="h-3 w-14" />
|
|
489
|
+
<Skeleton className="h-8 w-full" />
|
|
490
|
+
</div>
|
|
491
|
+
))}
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
381
499
|
// ── Component ─────────────────────────────────────────────────────────────────
|
|
382
500
|
|
|
383
501
|
export function EditorCourse() {
|
|
@@ -389,6 +507,7 @@ export function EditorCourse() {
|
|
|
389
507
|
|
|
390
508
|
const { request, currentLocaleCode, locales } = useApp();
|
|
391
509
|
const t = useTranslations('lms.CursoEditPage');
|
|
510
|
+
const courseListT = useTranslations('lms.CoursesPage');
|
|
392
511
|
const courseLocaleLabel = currentLocaleCode?.startsWith('en')
|
|
393
512
|
? 'Course Language'
|
|
394
513
|
: 'Idioma do Curso';
|
|
@@ -396,9 +515,12 @@ export function EditorCourse() {
|
|
|
396
515
|
const isMobile = useIsMobile();
|
|
397
516
|
const queryClient = useQueryClient();
|
|
398
517
|
const createSessionMutation = useCreateSessionMutation();
|
|
518
|
+
const lmsSettings = useLmsSettingsQuery();
|
|
399
519
|
|
|
400
520
|
// ── UI state ────────────────────────────────────────────────────────────────
|
|
401
|
-
const [activeTab, setActiveTab] = useState('
|
|
521
|
+
const [activeTab, setActiveTab] = useState('visao-geral');
|
|
522
|
+
const [pendingBulkJob, setPendingBulkJob] = useState<'transcription' | 'xp_recalculation' | null>(null);
|
|
523
|
+
const [isBulkJobPending, setIsBulkJobPending] = useState(false);
|
|
402
524
|
const [categoryEditSheetOpen, setCategoryEditSheetOpen] = useState(false);
|
|
403
525
|
const [editingCategoryId, setEditingCategoryId] = useState<number | null>(
|
|
404
526
|
null
|
|
@@ -417,7 +539,15 @@ export function EditorCourse() {
|
|
|
417
539
|
>('active');
|
|
418
540
|
const [savingCategoryEdit, setSavingCategoryEdit] = useState(false);
|
|
419
541
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
420
|
-
const [
|
|
542
|
+
const [confirmationInput, setConfirmationInput] = useState('');
|
|
543
|
+
const [archivingCourse, setArchivingCourse] = useState(false);
|
|
544
|
+
const [queuingDelete, setQueuingDelete] = useState(false);
|
|
545
|
+
const [loadingDeleteImpact, setLoadingDeleteImpact] = useState(false);
|
|
546
|
+
const [deleteImpact, setDeleteImpact] =
|
|
547
|
+
useState<ApiCourseDeletionImpact | null>(null);
|
|
548
|
+
const [deleteCourseStatus, setDeleteCourseStatus] = useState<
|
|
549
|
+
CourseEditFormValues['status'] | null
|
|
550
|
+
>(null);
|
|
421
551
|
const [logoPreview, setLogoPreview] = useState<string | null>(null);
|
|
422
552
|
const [bannerPreview, setBannerPreview] = useState<string | null>(null);
|
|
423
553
|
const [uploadingLogo, setUploadingLogo] = useState(false);
|
|
@@ -453,19 +583,21 @@ export function EditorCourse() {
|
|
|
453
583
|
const [savingVideoProfileEdit, setSavingVideoProfileEdit] = useState(false);
|
|
454
584
|
|
|
455
585
|
// ── Queries ─────────────────────────────────────────────────────────────────
|
|
456
|
-
const {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
586
|
+
const {
|
|
587
|
+
data: apiCourse,
|
|
588
|
+
refetch: refetchCourse,
|
|
589
|
+
isLoading: isLoadingCourse,
|
|
590
|
+
} = useQuery<ApiCourseDetail>({
|
|
591
|
+
queryKey: ['lms-course-detail', courseId],
|
|
592
|
+
enabled: Boolean(courseId),
|
|
593
|
+
queryFn: async () => {
|
|
594
|
+
const response = await request<ApiCourseDetail>({
|
|
595
|
+
url: `/lms/courses/${courseId}`,
|
|
596
|
+
method: 'GET',
|
|
597
|
+
});
|
|
598
|
+
return response.data;
|
|
599
|
+
},
|
|
600
|
+
});
|
|
469
601
|
|
|
470
602
|
const { data: categoryListData, refetch: refetchCategoryOptions } =
|
|
471
603
|
useQuery<ApiCategoryList>({
|
|
@@ -571,6 +703,12 @@ export function EditorCourse() {
|
|
|
571
703
|
|
|
572
704
|
const showOperationsTab = operationsConfig?.isAvailable === true;
|
|
573
705
|
|
|
706
|
+
useEffect(() => {
|
|
707
|
+
if (apiCourse?.status) {
|
|
708
|
+
setDeleteCourseStatus(toPtStatus(apiCourse.status));
|
|
709
|
+
}
|
|
710
|
+
}, [apiCourse?.status]);
|
|
711
|
+
|
|
574
712
|
// ── Save mutation ───────────────────────────────────────────────────────────
|
|
575
713
|
const { mutate: saveCourse, isPending: saving } = useMutation({
|
|
576
714
|
mutationFn: async (data: CourseEditFormValues) => {
|
|
@@ -913,7 +1051,7 @@ export function EditorCourse() {
|
|
|
913
1051
|
|
|
914
1052
|
useEffect(() => {
|
|
915
1053
|
if (!showVideoTab && activeTab === 'videos') {
|
|
916
|
-
setActiveTab('
|
|
1054
|
+
setActiveTab('detalhes');
|
|
917
1055
|
}
|
|
918
1056
|
}, [showVideoTab, activeTab]);
|
|
919
1057
|
|
|
@@ -1380,15 +1518,132 @@ export function EditorCourse() {
|
|
|
1380
1518
|
}
|
|
1381
1519
|
}
|
|
1382
1520
|
|
|
1521
|
+
const deleteCourseTitle = apiCourse?.title?.trim() || course.title.trim();
|
|
1522
|
+
const deleteConfirmMatches = confirmationInput === deleteCourseTitle;
|
|
1523
|
+
const deleteRequiresArchive = deleteCourseStatus !== 'arquivado';
|
|
1524
|
+
const canDeleteCourse =
|
|
1525
|
+
Boolean(courseId) &&
|
|
1526
|
+
!deleteRequiresArchive &&
|
|
1527
|
+
deleteConfirmMatches &&
|
|
1528
|
+
!loadingDeleteImpact &&
|
|
1529
|
+
!queuingDelete &&
|
|
1530
|
+
!archivingCourse &&
|
|
1531
|
+
Boolean(deleteImpact);
|
|
1532
|
+
|
|
1533
|
+
async function loadDeleteImpact() {
|
|
1534
|
+
if (!courseId) return;
|
|
1535
|
+
|
|
1536
|
+
setLoadingDeleteImpact(true);
|
|
1537
|
+
setDeleteImpact(null);
|
|
1538
|
+
|
|
1539
|
+
try {
|
|
1540
|
+
const response = await request<ApiCourseDetail>({
|
|
1541
|
+
url: `/lms/courses/${courseId}`,
|
|
1542
|
+
method: 'GET',
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
const impact = response.data.deletionImpact;
|
|
1546
|
+
|
|
1547
|
+
setDeleteCourseStatus(toPtStatus(response.data.status));
|
|
1548
|
+
setDeleteImpact(
|
|
1549
|
+
impact
|
|
1550
|
+
? {
|
|
1551
|
+
...impact,
|
|
1552
|
+
formattedSize:
|
|
1553
|
+
impact.formattedSize || formatBytes(impact.totalBytes ?? 0),
|
|
1554
|
+
}
|
|
1555
|
+
: null
|
|
1556
|
+
);
|
|
1557
|
+
} catch {
|
|
1558
|
+
toast.error(courseListT('toasts.courseDeleteImpactLoadError'));
|
|
1559
|
+
} finally {
|
|
1560
|
+
setLoadingDeleteImpact(false);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function openDeleteDialog() {
|
|
1565
|
+
setConfirmationInput('');
|
|
1566
|
+
setDeleteImpact(null);
|
|
1567
|
+
setDeleteDialogOpen(true);
|
|
1568
|
+
void loadDeleteImpact();
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
async function archiveCourseForDelete() {
|
|
1572
|
+
if (!courseId || deleteCourseStatus === 'arquivado') return;
|
|
1573
|
+
|
|
1574
|
+
setArchivingCourse(true);
|
|
1575
|
+
try {
|
|
1576
|
+
await request({
|
|
1577
|
+
url: `/lms/courses/${courseId}`,
|
|
1578
|
+
method: 'PATCH',
|
|
1579
|
+
data: { status: 'archived' },
|
|
1580
|
+
});
|
|
1581
|
+
|
|
1582
|
+
setDeleteCourseStatus('arquivado');
|
|
1583
|
+
form.setValue('status', 'arquivado', {
|
|
1584
|
+
shouldDirty: false,
|
|
1585
|
+
shouldTouch: false,
|
|
1586
|
+
shouldValidate: false,
|
|
1587
|
+
});
|
|
1588
|
+
toast.success(
|
|
1589
|
+
courseListT('toasts.courseArchived', { title: deleteCourseTitle })
|
|
1590
|
+
);
|
|
1591
|
+
await refetchCourse();
|
|
1592
|
+
await loadDeleteImpact();
|
|
1593
|
+
} catch {
|
|
1594
|
+
toast.error(courseListT('toasts.courseArchiveError'));
|
|
1595
|
+
} finally {
|
|
1596
|
+
setArchivingCourse(false);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1383
1600
|
async function handleDelete() {
|
|
1384
|
-
|
|
1601
|
+
if (!courseId) return;
|
|
1602
|
+
if (confirmationInput !== deleteCourseTitle) {
|
|
1603
|
+
toast.error(
|
|
1604
|
+
courseListT('form.validationErrors.confirmationMismatch') ||
|
|
1605
|
+
'Nome do curso não corresponde.'
|
|
1606
|
+
);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
if (deleteCourseStatus !== 'arquivado') {
|
|
1611
|
+
toast.error(courseListT('toasts.deleteOnlyArchived'));
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
if (!deleteImpact) {
|
|
1616
|
+
toast.error(courseListT('toasts.courseDeleteImpactLoadError'));
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
setQueuingDelete(true);
|
|
1385
1621
|
try {
|
|
1386
|
-
await request({
|
|
1622
|
+
const response = await request<ApiQueuedDeleteResponse>({
|
|
1623
|
+
url: `/lms/courses/${courseId}`,
|
|
1624
|
+
method: 'DELETE',
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1387
1627
|
setDeleteDialogOpen(false);
|
|
1388
|
-
toast.success(
|
|
1628
|
+
toast.success(
|
|
1629
|
+
courseListT('toasts.courseDeleteQueued', {
|
|
1630
|
+
title: deleteCourseTitle,
|
|
1631
|
+
files:
|
|
1632
|
+
response.data?.deletionImpact?.fileCount ?? deleteImpact.fileCount,
|
|
1633
|
+
})
|
|
1634
|
+
);
|
|
1389
1635
|
router.push('/lms/courses');
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
const message =
|
|
1638
|
+
error instanceof Error && error.message ? error.message : '';
|
|
1639
|
+
|
|
1640
|
+
if (message.includes('ONLY_ARCHIVED_COURSE_CAN_BE_DELETED')) {
|
|
1641
|
+
toast.error(courseListT('toasts.deleteOnlyArchived'));
|
|
1642
|
+
} else {
|
|
1643
|
+
toast.error(courseListT('toasts.courseDeleteQueueError'));
|
|
1644
|
+
}
|
|
1390
1645
|
} finally {
|
|
1391
|
-
|
|
1646
|
+
setQueuingDelete(false);
|
|
1392
1647
|
}
|
|
1393
1648
|
}
|
|
1394
1649
|
|
|
@@ -1431,7 +1686,29 @@ export function EditorCourse() {
|
|
|
1431
1686
|
toast.error(first?.message ?? 'Verifique os campos obrigatórios');
|
|
1432
1687
|
}
|
|
1433
1688
|
|
|
1689
|
+
// ── Bulk jobs ─────────────────────────────────────────────────────────────────
|
|
1690
|
+
const handleBulkJob = async (jobType: 'transcription' | 'xp_recalculation') => {
|
|
1691
|
+
setIsBulkJobPending(true);
|
|
1692
|
+
try {
|
|
1693
|
+
const res = await request<{ queued: number; skipped: number }>({
|
|
1694
|
+
url: `/lms/courses/${courseId}/structure/bulk-jobs`,
|
|
1695
|
+
method: 'POST',
|
|
1696
|
+
data: { jobType },
|
|
1697
|
+
});
|
|
1698
|
+
toast.success(`${res.data?.queued ?? 0} job(s) enfileirado(s), ${res.data?.skipped ?? 0} ignorado(s).`);
|
|
1699
|
+
} catch {
|
|
1700
|
+
toast.error('Erro ao enfileirar jobs em massa.');
|
|
1701
|
+
} finally {
|
|
1702
|
+
setIsBulkJobPending(false);
|
|
1703
|
+
setPendingBulkJob(null);
|
|
1704
|
+
}
|
|
1705
|
+
};
|
|
1706
|
+
|
|
1434
1707
|
// ── Render ────────────────────────────────────────────────────────────────────
|
|
1708
|
+
if (isLoadingCourse && !apiCourse) {
|
|
1709
|
+
return <EditorCourseSkeleton />;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1435
1712
|
return (
|
|
1436
1713
|
<Form {...form}>
|
|
1437
1714
|
<form
|
|
@@ -1553,6 +1830,73 @@ export function EditorCourse() {
|
|
|
1553
1830
|
</TooltipContent>
|
|
1554
1831
|
</Tooltip>
|
|
1555
1832
|
</TooltipProvider>
|
|
1833
|
+
|
|
1834
|
+
{/* ── Bulk jobs menu ────────────────────────────────────────── */}
|
|
1835
|
+
<DropdownMenu>
|
|
1836
|
+
<DropdownMenuTrigger asChild>
|
|
1837
|
+
<Button
|
|
1838
|
+
type="button"
|
|
1839
|
+
variant="ghost"
|
|
1840
|
+
size="icon"
|
|
1841
|
+
className="size-6 shrink-0"
|
|
1842
|
+
disabled={isBulkJobPending}
|
|
1843
|
+
>
|
|
1844
|
+
{isBulkJobPending ? (
|
|
1845
|
+
<Loader2 className="size-3.5 animate-spin" />
|
|
1846
|
+
) : (
|
|
1847
|
+
<Briefcase className="size-3.5" />
|
|
1848
|
+
)}
|
|
1849
|
+
</Button>
|
|
1850
|
+
</DropdownMenuTrigger>
|
|
1851
|
+
<DropdownMenuContent align="end" className="w-56">
|
|
1852
|
+
<DropdownMenuLabel className="text-xs">Jobs em massa</DropdownMenuLabel>
|
|
1853
|
+
<DropdownMenuSeparator />
|
|
1854
|
+
{lmsSettings.transcriptionEnabled && (
|
|
1855
|
+
<DropdownMenuItem
|
|
1856
|
+
onSelect={() => setPendingBulkJob('transcription')}
|
|
1857
|
+
className="gap-2"
|
|
1858
|
+
>
|
|
1859
|
+
<Mic className="size-4 text-muted-foreground" />
|
|
1860
|
+
Refazer transcrição de todas as aulas
|
|
1861
|
+
</DropdownMenuItem>
|
|
1862
|
+
)}
|
|
1863
|
+
<DropdownMenuItem
|
|
1864
|
+
onSelect={() => setPendingBulkJob('xp_recalculation')}
|
|
1865
|
+
className="gap-2"
|
|
1866
|
+
>
|
|
1867
|
+
<Zap className="size-4 text-muted-foreground" />
|
|
1868
|
+
Recalcular XP de todas as aulas
|
|
1869
|
+
</DropdownMenuItem>
|
|
1870
|
+
</DropdownMenuContent>
|
|
1871
|
+
</DropdownMenu>
|
|
1872
|
+
|
|
1873
|
+
<AlertDialog
|
|
1874
|
+
open={pendingBulkJob !== null}
|
|
1875
|
+
onOpenChange={(open) => { if (!open) setPendingBulkJob(null); }}
|
|
1876
|
+
>
|
|
1877
|
+
<AlertDialogContent>
|
|
1878
|
+
<AlertDialogHeader>
|
|
1879
|
+
<AlertDialogTitle>
|
|
1880
|
+
{pendingBulkJob === 'transcription'
|
|
1881
|
+
? 'Refazer transcrição de todas as aulas?'
|
|
1882
|
+
: 'Recalcular XP de todas as aulas?'}
|
|
1883
|
+
</AlertDialogTitle>
|
|
1884
|
+
<AlertDialogDescription>
|
|
1885
|
+
{pendingBulkJob === 'transcription'
|
|
1886
|
+
? `Um job de transcrição será enfileirado para cada aula que possuir arquivo de áudio (${lessons.length} aulas no total). Aulas sem áudio serão ignoradas.`
|
|
1887
|
+
: `Um job de recálculo de XP será enfileirado para cada aula que possuir transcrição (${lessons.length} aulas no total). Aulas sem transcrição serão ignoradas.`}
|
|
1888
|
+
</AlertDialogDescription>
|
|
1889
|
+
</AlertDialogHeader>
|
|
1890
|
+
<AlertDialogFooter>
|
|
1891
|
+
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
|
1892
|
+
<AlertDialogAction
|
|
1893
|
+
onClick={() => pendingBulkJob && handleBulkJob(pendingBulkJob)}
|
|
1894
|
+
>
|
|
1895
|
+
Confirmar
|
|
1896
|
+
</AlertDialogAction>
|
|
1897
|
+
</AlertDialogFooter>
|
|
1898
|
+
</AlertDialogContent>
|
|
1899
|
+
</AlertDialog>
|
|
1556
1900
|
</div>
|
|
1557
1901
|
</div>
|
|
1558
1902
|
|
|
@@ -1573,14 +1917,15 @@ export function EditorCourse() {
|
|
|
1573
1917
|
>
|
|
1574
1918
|
{(
|
|
1575
1919
|
[
|
|
1576
|
-
'
|
|
1577
|
-
'
|
|
1920
|
+
'visao-geral',
|
|
1921
|
+
'detalhes',
|
|
1922
|
+
'xp',
|
|
1578
1923
|
'midia',
|
|
1579
1924
|
'recursos',
|
|
1580
|
-
'extra',
|
|
1581
1925
|
...(showVideoTab ? ['videos'] : []),
|
|
1582
1926
|
...(showOperationsTab ? ['operacoes'] : []),
|
|
1583
1927
|
'publicacao',
|
|
1928
|
+
'extra',
|
|
1584
1929
|
] as const
|
|
1585
1930
|
).map((tab) => (
|
|
1586
1931
|
<TabsTrigger
|
|
@@ -1593,30 +1938,40 @@ export function EditorCourse() {
|
|
|
1593
1938
|
: 'h-8 px-3 text-xs'
|
|
1594
1939
|
)}
|
|
1595
1940
|
>
|
|
1596
|
-
{tab === '
|
|
1597
|
-
? t('structureEditor.tabs.
|
|
1598
|
-
: tab === '
|
|
1599
|
-
? t('structureEditor.tabs.
|
|
1600
|
-
: tab === '
|
|
1601
|
-
? t('structureEditor.tabs.
|
|
1602
|
-
: tab === '
|
|
1603
|
-
? t('structureEditor.tabs.
|
|
1604
|
-
: tab === '
|
|
1605
|
-
? t('structureEditor.tabs.
|
|
1606
|
-
: tab === '
|
|
1607
|
-
? t('structureEditor.tabs.
|
|
1608
|
-
: tab === '
|
|
1609
|
-
? '
|
|
1610
|
-
:
|
|
1941
|
+
{tab === 'visao-geral'
|
|
1942
|
+
? t('structureEditor.tabs.overview')
|
|
1943
|
+
: tab === 'detalhes'
|
|
1944
|
+
? t('structureEditor.tabs.details')
|
|
1945
|
+
: tab === 'xp'
|
|
1946
|
+
? t('structureEditor.tabs.xp')
|
|
1947
|
+
: tab === 'midia'
|
|
1948
|
+
? t('structureEditor.tabs.media')
|
|
1949
|
+
: tab === 'recursos'
|
|
1950
|
+
? t('structureEditor.tabs.resources')
|
|
1951
|
+
: tab === 'extra'
|
|
1952
|
+
? t('structureEditor.tabs.extra')
|
|
1953
|
+
: tab === 'videos'
|
|
1954
|
+
? t('structureEditor.tabs.videoProfiles')
|
|
1955
|
+
: tab === 'operacoes'
|
|
1956
|
+
? 'Operações'
|
|
1957
|
+
: t('structureEditor.tabs.publish')}
|
|
1611
1958
|
</TabsTrigger>
|
|
1612
1959
|
))}
|
|
1613
1960
|
</TabsList>
|
|
1614
1961
|
|
|
1615
1962
|
<div className="flex-1 min-h-0 min-w-0 overflow-x-hidden overflow-y-auto overscroll-contain">
|
|
1616
1963
|
<div className="flex min-w-0 flex-col gap-3 p-2 sm:p-3">
|
|
1617
|
-
{/* ── Tab:
|
|
1964
|
+
{/* ── Tab: Visão Geral ─────────────────────────────────────── */}
|
|
1965
|
+
<TabsContent value="visao-geral" className="mt-0 min-w-0">
|
|
1966
|
+
<CourseOverviewTab
|
|
1967
|
+
courseId={courseId}
|
|
1968
|
+
locale={currentLocaleCode || 'pt-BR'}
|
|
1969
|
+
/>
|
|
1970
|
+
</TabsContent>
|
|
1971
|
+
|
|
1972
|
+
{/* ── Tab: Detalhes ───────────────────────────────────────── */}
|
|
1618
1973
|
<TabsContent
|
|
1619
|
-
value="
|
|
1974
|
+
value="detalhes"
|
|
1620
1975
|
className="mt-0 flex min-w-0 flex-col gap-3"
|
|
1621
1976
|
>
|
|
1622
1977
|
{/* Dados principais */}
|
|
@@ -1828,13 +2183,6 @@ export function EditorCourse() {
|
|
|
1828
2183
|
/>
|
|
1829
2184
|
</CardContent>
|
|
1830
2185
|
</Card>
|
|
1831
|
-
</TabsContent>
|
|
1832
|
-
|
|
1833
|
-
{/* ── Tab: Sobre ──────────────────────────────────────────── */}
|
|
1834
|
-
<TabsContent
|
|
1835
|
-
value="sobre"
|
|
1836
|
-
className="mt-0 flex min-w-0 flex-col gap-3"
|
|
1837
|
-
>
|
|
1838
2186
|
<CourseClassificationCard
|
|
1839
2187
|
form={form}
|
|
1840
2188
|
t={t}
|
|
@@ -1859,6 +2207,13 @@ export function EditorCourse() {
|
|
|
1859
2207
|
<CourseContentCard form={form} compact />
|
|
1860
2208
|
</TabsContent>
|
|
1861
2209
|
|
|
2210
|
+
<TabsContent value="xp" className="mt-0 min-w-0">
|
|
2211
|
+
<CourseXpOverviewTab
|
|
2212
|
+
courseId={courseId}
|
|
2213
|
+
locale={currentLocaleCode || 'pt-BR'}
|
|
2214
|
+
/>
|
|
2215
|
+
</TabsContent>
|
|
2216
|
+
|
|
1862
2217
|
{/* ── Tab: Mídia ──────────────────────────────────────────── */}
|
|
1863
2218
|
<TabsContent value="midia" className="mt-0 min-w-0">
|
|
1864
2219
|
<CourseMediaCard
|
|
@@ -1879,6 +2234,8 @@ export function EditorCourse() {
|
|
|
1879
2234
|
name:
|
|
1880
2235
|
apiCourse.logoFilename ||
|
|
1881
2236
|
`#${apiCourse.logoFileId}`,
|
|
2237
|
+
size: apiCourse.logoFileSize ?? null,
|
|
2238
|
+
mimeType: apiCourse.logoMimeType ?? null,
|
|
1882
2239
|
}
|
|
1883
2240
|
: undefined
|
|
1884
2241
|
}
|
|
@@ -1889,6 +2246,8 @@ export function EditorCourse() {
|
|
|
1889
2246
|
name:
|
|
1890
2247
|
apiCourse.bannerFilename ||
|
|
1891
2248
|
`#${apiCourse.bannerFileId}`,
|
|
2249
|
+
size: apiCourse.bannerFileSize ?? null,
|
|
2250
|
+
mimeType: apiCourse.bannerMimeType ?? null,
|
|
1892
2251
|
}
|
|
1893
2252
|
: undefined
|
|
1894
2253
|
}
|
|
@@ -2121,7 +2480,7 @@ export function EditorCourse() {
|
|
|
2121
2480
|
<CourseFlagsCard form={form} t={t} compact />
|
|
2122
2481
|
<CourseDangerZoneCard
|
|
2123
2482
|
t={t}
|
|
2124
|
-
onDelete={
|
|
2483
|
+
onDelete={openDeleteDialog}
|
|
2125
2484
|
compact
|
|
2126
2485
|
/>
|
|
2127
2486
|
</TabsContent>
|
|
@@ -2556,53 +2915,32 @@ export function EditorCourse() {
|
|
|
2556
2915
|
</ResizableSheetContent>
|
|
2557
2916
|
</Sheet>
|
|
2558
2917
|
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
</DialogDescription>
|
|
2586
|
-
</DialogHeader>
|
|
2587
|
-
<DialogFooter className="gap-2">
|
|
2588
|
-
<Button
|
|
2589
|
-
variant="outline"
|
|
2590
|
-
onClick={() => setDeleteDialogOpen(false)}
|
|
2591
|
-
>
|
|
2592
|
-
{t('deleteDialog.actions.cancel')}
|
|
2593
|
-
</Button>
|
|
2594
|
-
<Button
|
|
2595
|
-
variant="destructive"
|
|
2596
|
-
onClick={handleDelete}
|
|
2597
|
-
disabled={deleting}
|
|
2598
|
-
className="gap-2"
|
|
2599
|
-
>
|
|
2600
|
-
{deleting ? <Loader2 className="size-4 animate-spin" /> : null}
|
|
2601
|
-
{t('deleteDialog.actions.delete')}
|
|
2602
|
-
</Button>
|
|
2603
|
-
</DialogFooter>
|
|
2604
|
-
</DialogContent>
|
|
2605
|
-
</Dialog>
|
|
2918
|
+
<CourseDeleteDialog
|
|
2919
|
+
open={deleteDialogOpen}
|
|
2920
|
+
onOpenChange={(open) => {
|
|
2921
|
+
setDeleteDialogOpen(open);
|
|
2922
|
+
if (!open) {
|
|
2923
|
+
setConfirmationInput('');
|
|
2924
|
+
setDeleteImpact(null);
|
|
2925
|
+
}
|
|
2926
|
+
}}
|
|
2927
|
+
course={{
|
|
2928
|
+
title: deleteCourseTitle || course.title,
|
|
2929
|
+
status: deleteCourseStatus ?? 'rascunho',
|
|
2930
|
+
enrollmentCount:
|
|
2931
|
+
deleteImpact?.enrollmentCount ?? apiCourse?.enrollmentCount ?? 0,
|
|
2932
|
+
}}
|
|
2933
|
+
impact={deleteImpact}
|
|
2934
|
+
loadingImpact={loadingDeleteImpact}
|
|
2935
|
+
confirmationInput={confirmationInput}
|
|
2936
|
+
onConfirmationInputChange={setConfirmationInput}
|
|
2937
|
+
archiving={archivingCourse}
|
|
2938
|
+
queuingDelete={queuingDelete}
|
|
2939
|
+
canDelete={canDeleteCourse}
|
|
2940
|
+
onArchive={archiveCourseForDelete}
|
|
2941
|
+
onDelete={handleDelete}
|
|
2942
|
+
t={courseListT}
|
|
2943
|
+
/>
|
|
2606
2944
|
</Form>
|
|
2607
2945
|
);
|
|
2608
2946
|
}
|