@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.
Files changed (243) hide show
  1. package/dist/certificate/certificate.controller.d.ts +1 -1
  2. package/dist/certificate/certificate.controller.d.ts.map +1 -1
  3. package/dist/certificate/certificate.controller.js +4 -2
  4. package/dist/certificate/certificate.controller.js.map +1 -1
  5. package/dist/certificate/certificate.service.d.ts +50 -0
  6. package/dist/certificate/certificate.service.d.ts.map +1 -1
  7. package/dist/certificate/certificate.service.js +73 -0
  8. package/dist/certificate/certificate.service.js.map +1 -1
  9. package/dist/class-group/class-group.controller.d.ts +1 -0
  10. package/dist/class-group/class-group.controller.d.ts.map +1 -1
  11. package/dist/class-group/class-group.service.d.ts +1 -0
  12. package/dist/class-group/class-group.service.d.ts.map +1 -1
  13. package/dist/course/course-ai-usage.service.d.ts +58 -0
  14. package/dist/course/course-ai-usage.service.d.ts.map +1 -0
  15. package/dist/course/course-ai-usage.service.js +176 -0
  16. package/dist/course/course-ai-usage.service.js.map +1 -0
  17. package/dist/course/course-audio-transcription.service.d.ts +65 -1
  18. package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
  19. package/dist/course/course-audio-transcription.service.js +381 -29
  20. package/dist/course/course-audio-transcription.service.js.map +1 -1
  21. package/dist/course/course-export-scorm12.service.d.ts +3 -0
  22. package/dist/course/course-export-scorm12.service.d.ts.map +1 -1
  23. package/dist/course/course-export-scorm12.service.js +141 -6
  24. package/dist/course/course-export-scorm12.service.js.map +1 -1
  25. package/dist/course/course-export.service.d.ts.map +1 -1
  26. package/dist/course/course-export.service.js +2 -1
  27. package/dist/course/course-export.service.js.map +1 -1
  28. package/dist/course/course-lesson.controller.d.ts +25 -3
  29. package/dist/course/course-lesson.controller.d.ts.map +1 -1
  30. package/dist/course/course-lesson.controller.js +71 -8
  31. package/dist/course/course-lesson.controller.js.map +1 -1
  32. package/dist/course/course-structure.controller.d.ts +30 -7
  33. package/dist/course/course-structure.controller.d.ts.map +1 -1
  34. package/dist/course/course-structure.controller.js +37 -4
  35. package/dist/course/course-structure.controller.js.map +1 -1
  36. package/dist/course/course-structure.service.d.ts +37 -5
  37. package/dist/course/course-structure.service.d.ts.map +1 -1
  38. package/dist/course/course-structure.service.js +165 -20
  39. package/dist/course/course-structure.service.js.map +1 -1
  40. package/dist/course/course-transcription-translation.service.d.ts +31 -0
  41. package/dist/course/course-transcription-translation.service.d.ts.map +1 -0
  42. package/dist/course/course-transcription-translation.service.js +227 -0
  43. package/dist/course/course-transcription-translation.service.js.map +1 -0
  44. package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
  45. package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
  46. package/dist/course/course-video-agent-pipeline.service.js +398 -0
  47. package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
  48. package/dist/course/course-video-hls.service.d.ts +14 -0
  49. package/dist/course/course-video-hls.service.d.ts.map +1 -1
  50. package/dist/course/course-video-hls.service.js +25 -8
  51. package/dist/course/course-video-hls.service.js.map +1 -1
  52. package/dist/course/course.controller.d.ts +2 -0
  53. package/dist/course/course.controller.d.ts.map +1 -1
  54. package/dist/course/course.module.d.ts.map +1 -1
  55. package/dist/course/course.module.js +9 -0
  56. package/dist/course/course.module.js.map +1 -1
  57. package/dist/course/course.service.d.ts +2 -0
  58. package/dist/course/course.service.d.ts.map +1 -1
  59. package/dist/course/course.service.js +36 -2
  60. package/dist/course/course.service.js.map +1 -1
  61. package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
  62. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
  63. package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
  64. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
  65. package/dist/course/dto/create-course-export.dto.d.ts +1 -0
  66. package/dist/course/dto/create-course-export.dto.d.ts.map +1 -1
  67. package/dist/course/dto/create-course-export.dto.js +6 -0
  68. package/dist/course/dto/create-course-export.dto.js.map +1 -1
  69. package/dist/course/ffmpeg.util.d.ts +10 -0
  70. package/dist/course/ffmpeg.util.d.ts.map +1 -0
  71. package/dist/course/ffmpeg.util.js +79 -0
  72. package/dist/course/ffmpeg.util.js.map +1 -0
  73. package/dist/course/lms-bulk-upload-automation.service.d.ts +3 -1
  74. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  75. package/dist/course/lms-bulk-upload-automation.service.js +33 -16
  76. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  77. package/dist/course/lms-bulk-upload.controller.d.ts +3 -0
  78. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  79. package/dist/course/lms-bulk-upload.service.d.ts +3 -0
  80. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  81. package/dist/course/lms-bulk-upload.service.js +48 -29
  82. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  83. package/dist/course/subtitle.util.d.ts +46 -0
  84. package/dist/course/subtitle.util.d.ts.map +1 -0
  85. package/dist/course/subtitle.util.js +206 -0
  86. package/dist/course/subtitle.util.js.map +1 -0
  87. package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
  88. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  89. package/dist/enterprise/training/training-admin.service.d.ts +2 -0
  90. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  91. package/dist/enterprise/training/training-student.service.d.ts +27 -0
  92. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  93. package/dist/enterprise/training/training-student.service.js +197 -10
  94. package/dist/enterprise/training/training-student.service.js.map +1 -1
  95. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +3 -1
  96. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
  97. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +19 -5
  98. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
  99. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -1
  100. package/dist/lesson-xp-map/lesson-xp-map.module.js +2 -1
  101. package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -1
  102. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
  103. package/dist/lms.module.d.ts.map +1 -1
  104. package/dist/lms.module.js +14 -0
  105. package/dist/lms.module.js.map +1 -1
  106. package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
  107. package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
  108. package/dist/platforma/dto/heartbeat.dto.js +50 -0
  109. package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
  110. package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
  111. package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
  112. package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
  113. package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
  114. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
  115. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
  116. package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
  117. package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
  118. package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
  119. package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
  120. package/dist/platforma/platforma-heartbeat.service.js +50 -0
  121. package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
  122. package/dist/platforma/platforma-performance.service.d.ts +121 -0
  123. package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
  124. package/dist/platforma/platforma-performance.service.js +500 -0
  125. package/dist/platforma/platforma-performance.service.js.map +1 -0
  126. package/dist/platforma/platforma-search.service.d.ts +21 -0
  127. package/dist/platforma/platforma-search.service.d.ts.map +1 -0
  128. package/dist/platforma/platforma-search.service.js +64 -0
  129. package/dist/platforma/platforma-search.service.js.map +1 -0
  130. package/dist/platforma/platforma-video.service.d.ts +8 -0
  131. package/dist/platforma/platforma-video.service.d.ts.map +1 -1
  132. package/dist/platforma/platforma-video.service.js +45 -2
  133. package/dist/platforma/platforma-video.service.js.map +1 -1
  134. package/dist/platforma/platforma.controller.d.ts +213 -1
  135. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  136. package/dist/platforma/platforma.controller.js +159 -2
  137. package/dist/platforma/platforma.controller.js.map +1 -1
  138. package/dist/realtime/lms-realtime.controller.d.ts +2 -0
  139. package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
  140. package/dist/realtime/lms-realtime.controller.js +31 -0
  141. package/dist/realtime/lms-realtime.controller.js.map +1 -1
  142. package/dist/realtime/lms-realtime.service.d.ts +1 -1
  143. package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
  144. package/dist/realtime/lms-realtime.service.js.map +1 -1
  145. package/dist/training/dto/create-training.dto.d.ts +9 -0
  146. package/dist/training/dto/create-training.dto.d.ts.map +1 -1
  147. package/dist/training/dto/create-training.dto.js +45 -1
  148. package/dist/training/dto/create-training.dto.js.map +1 -1
  149. package/dist/training/training.controller.d.ts +144 -0
  150. package/dist/training/training.controller.d.ts.map +1 -1
  151. package/dist/training/training.service.d.ts +149 -0
  152. package/dist/training/training.service.d.ts.map +1 -1
  153. package/dist/training/training.service.js +332 -167
  154. package/dist/training/training.service.js.map +1 -1
  155. package/hedhog/data/image_type.yaml +10 -0
  156. package/hedhog/data/route.yaml +251 -0
  157. package/hedhog/data/setting_group.yaml +97 -0
  158. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +139 -27
  159. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
  160. package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
  161. package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
  162. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +69 -57
  163. package/hedhog/frontend/app/courses/[id]/_components/CourseIssuedCertificatesCard.tsx.ejs +168 -0
  164. package/hedhog/frontend/app/courses/[id]/structure/_components/course-ai-costs-tab.tsx.ejs +191 -0
  165. package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +81 -1
  166. package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +12 -0
  167. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +141 -30
  168. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
  169. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +69 -1
  170. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
  171. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +267 -19
  172. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +114 -86
  173. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +239 -31
  174. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +344 -59
  175. package/hedhog/frontend/app/courses/[id]/structure/_components/lesson-video-preview.tsx.ejs +200 -0
  176. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +1 -0
  177. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +3 -0
  178. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
  179. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -7
  180. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -0
  181. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-ai-costs.ts.ejs +40 -0
  182. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -0
  183. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +25 -0
  184. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +148 -0
  185. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +157 -8
  186. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +1 -22
  187. package/hedhog/frontend/app/courses/page.tsx.ejs +66 -13
  188. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
  189. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
  190. package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
  191. package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
  192. package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
  193. package/hedhog/frontend/app/paths/page.tsx.ejs +650 -168
  194. package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
  195. package/hedhog/frontend/messages/en.json +41 -12
  196. package/hedhog/frontend/messages/pt.json +44 -13
  197. package/hedhog/query/triggers.sql +33 -0
  198. package/hedhog/table/course_ai_usage.yaml +46 -0
  199. package/hedhog/table/course_enrollment.yaml +3 -0
  200. package/hedhog/table/course_lesson.yaml +3 -0
  201. package/hedhog/table/course_lesson_answer.yaml +37 -0
  202. package/hedhog/table/course_lesson_transcription_segment.yaml +8 -0
  203. package/hedhog/table/learning_path.yaml +6 -0
  204. package/hedhog/table/learning_path_module.yaml +22 -0
  205. package/hedhog/table/learning_path_step.yaml +9 -6
  206. package/hedhog/table/lesson_view_event.yaml +66 -0
  207. package/package.json +8 -7
  208. package/src/certificate/certificate.controller.ts +2 -0
  209. package/src/certificate/certificate.service.ts +99 -0
  210. package/src/course/course-ai-usage.service.ts +221 -0
  211. package/src/course/course-audio-transcription.service.ts +471 -43
  212. package/src/course/course-export-scorm12.service.ts +149 -5
  213. package/src/course/course-export.service.ts +1 -0
  214. package/src/course/course-lesson.controller.ts +59 -6
  215. package/src/course/course-structure.controller.ts +19 -1
  216. package/src/course/course-structure.service.ts +184 -10
  217. package/src/course/course-transcription-translation.service.ts +293 -0
  218. package/src/course/course-video-agent-pipeline.service.ts +471 -0
  219. package/src/course/course-video-hls.service.ts +30 -10
  220. package/src/course/course.module.ts +9 -0
  221. package/src/course/course.service.ts +46 -1
  222. package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
  223. package/src/course/dto/create-course-export.dto.ts +6 -0
  224. package/src/course/ffmpeg.util.ts +65 -0
  225. package/src/course/lms-bulk-upload-automation.service.ts +33 -8
  226. package/src/course/lms-bulk-upload.service.ts +20 -1
  227. package/src/course/subtitle.util.ts +220 -0
  228. package/src/enterprise/training/training-student.service.ts +224 -4
  229. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +14 -0
  230. package/src/lesson-xp-map/lesson-xp-map.module.ts +2 -1
  231. package/src/lms.module.ts +14 -0
  232. package/src/platforma/dto/heartbeat.dto.ts +30 -0
  233. package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
  234. package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
  235. package/src/platforma/platforma-heartbeat.service.ts +33 -0
  236. package/src/platforma/platforma-performance.service.ts +606 -0
  237. package/src/platforma/platforma-search.service.ts +48 -0
  238. package/src/platforma/platforma-video.service.ts +59 -3
  239. package/src/platforma/platforma.controller.ts +130 -0
  240. package/src/realtime/lms-realtime.controller.ts +27 -1
  241. package/src/realtime/lms-realtime.service.ts +2 -1
  242. package/src/training/dto/create-training.dto.ts +36 -0
  243. package/src/training/training.service.ts +360 -163
@@ -6,17 +6,19 @@ import { zodResolver } from '@hookform/resolvers/zod';
6
6
  import {
7
7
  AlertTriangle,
8
8
  BookOpen,
9
- Briefcase,
10
9
  CheckCircle2,
11
10
  CircleDot,
12
11
  Clock,
13
12
  Download,
13
+ Languages,
14
14
  Layers,
15
15
  Loader2,
16
16
  Mic,
17
+ MoreVertical,
17
18
  Pencil,
18
19
  Plus,
19
20
  Save,
21
+ Trash2,
20
22
  Undo2,
21
23
  UploadCloud,
22
24
  Video,
@@ -28,6 +30,7 @@ import { useRouter } from 'next/navigation';
28
30
  import { type ChangeEvent, useEffect, useMemo, useRef, useState } from 'react';
29
31
  import { useForm } from 'react-hook-form';
30
32
  import { toast } from 'sonner';
33
+ import { useDeleteCourseTranscriptionsMutation } from '../_data/use-transcription-segments';
31
34
  import { z } from 'zod';
32
35
 
33
36
  import { createDefaultTemplate } from '@/app/(app)/(libraries)/lms/_lib/editor/types';
@@ -105,6 +108,7 @@ import { CourseClassificationCard } from '../../_components/CourseClassification
105
108
  import { CourseContentCard } from '../../_components/CourseContentCard';
106
109
  import { CourseDangerZoneCard } from '../../_components/CourseDangerZoneCard';
107
110
  import { CourseFlagsCard } from '../../_components/CourseFlagsCard';
111
+ import { CourseIssuedCertificatesCard } from '../../_components/CourseIssuedCertificatesCard';
108
112
  import { CourseMediaCard } from '../../_components/CourseMediaCard';
109
113
  import { CourseRelationsCard } from '../../_components/CourseRelationsCard';
110
114
  import {
@@ -120,6 +124,7 @@ import { CourseExportSheet } from './course-export-sheet';
120
124
  import { CourseExportsTab } from './course-exports-tab';
121
125
  import { CourseOperationsTab } from './course-operations-tab';
122
126
  import { CourseOverviewTab } from './course-overview-tab';
127
+ import { CourseAiCostsTab } from './course-ai-costs-tab';
123
128
  import { CourseXpOverviewTab } from './course-xp-overview-tab';
124
129
  import { CourseInstructorsSummaryCard } from './CourseInstructorsSummaryCard';
125
130
  import { IconActionTooltip } from './icon-action-tooltip';
@@ -511,6 +516,7 @@ const COURSE_TABS = [
511
516
  'publicacao',
512
517
  'extra',
513
518
  'exportacoes',
519
+ 'custos-ia',
514
520
  ] as const;
515
521
  type CourseTab = (typeof COURSE_TABS)[number];
516
522
 
@@ -521,6 +527,7 @@ interface EditorCourseProps {
521
527
 
522
528
  export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
523
529
  const courseId = useStructureStore((s) => s.courseId);
530
+ const showConfirm = useStructureStore((s) => s.showConfirm);
524
531
  const course = useStructureStore((s) => s.course);
525
532
  const sessions = useStructureStore((s) => s.sessions);
526
533
  const lessons = useStructureStore((s) => s.lessons);
@@ -535,6 +542,39 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
535
542
  const router = useRouter();
536
543
  const isMobile = useIsMobile();
537
544
  const queryClient = useQueryClient();
545
+ const { mutate: deleteCourseTranscriptions, isPending: isDeletingTranscriptions } =
546
+ useDeleteCourseTranscriptionsMutation(courseId);
547
+
548
+ const handleDeleteAllTranscriptions = () => {
549
+ showConfirm({
550
+ title: 'Excluir todas as transcrições e traduções',
551
+ description:
552
+ 'Isso remove permanentemente todas as transcrições e traduções de todas as aulas deste curso. Esta ação não pode ser desfeita.',
553
+ confirmText: 'Excluir tudo',
554
+ destructive: true,
555
+ onConfirm: () => {
556
+ deleteCourseTranscriptions({
557
+ onSuccess: (result) => {
558
+ toast.success(
559
+ `Transcrições removidas (${result.deleted} segmento(s) excluído(s)).`,
560
+ );
561
+ queryClient.invalidateQueries({
562
+ queryKey: ['course-content-overview', courseId],
563
+ });
564
+ queryClient.invalidateQueries({
565
+ queryKey: ['lesson-transcription-locales'],
566
+ });
567
+ queryClient.invalidateQueries({
568
+ queryKey: ['lesson-transcription-segments'],
569
+ });
570
+ },
571
+ onError: (err) => {
572
+ toast.error(err.message || 'Erro ao excluir as transcrições.');
573
+ },
574
+ });
575
+ },
576
+ });
577
+ };
538
578
  const createSessionMutation = useCreateSessionMutation();
539
579
  const lmsSettings = useLmsSettingsQuery();
540
580
 
@@ -547,6 +587,9 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
547
587
  const [exportSheetOpen, setExportSheetOpen] = useState(false);
548
588
  const [pendingBulkJob, setPendingBulkJob] = useState<'transcription' | 'xp_recalculation' | 'video_processing' | null>(null);
549
589
  const [isBulkJobPending, setIsBulkJobPending] = useState(false);
590
+ const [translateTranscriptionSheetOpen, setTranslateTranscriptionSheetOpen] = useState(false);
591
+ const [bulkTranslateLocaleId, setBulkTranslateLocaleId] = useState<number | null>(null);
592
+ const [isBulkTranslating, setIsBulkTranslating] = useState(false);
550
593
  const [videoProcessingStats, setVideoProcessingStats] = useState<{
551
594
  withOriginal: number;
552
595
  withoutOriginal: number;
@@ -951,6 +994,12 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
951
994
  String(formValues.modeloCertificado ?? '').trim().length > 0,
952
995
  required: formValues.certificado,
953
996
  },
997
+ {
998
+ id: 'listed',
999
+ label: t('structureEditor.publishChecklist.items.listed'),
1000
+ done: formValues.listado,
1001
+ required: false,
1002
+ },
954
1003
  ];
955
1004
  const requiredChecklist = checklistItems.filter((item) => item.required);
956
1005
  const completedRequired = requiredChecklist.filter(
@@ -1589,6 +1638,26 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
1589
1638
  }
1590
1639
 
1591
1640
  // ── Bulk jobs ─────────────────────────────────────────────────────────────────
1641
+ const handleBulkTranslateTranscription = async () => {
1642
+ if (!bulkTranslateLocaleId) return;
1643
+ setIsBulkTranslating(true);
1644
+ try {
1645
+ const res = await request<{ queued: number; skipped: number }>({
1646
+ url: `/lms/courses/${courseId}/structure/bulk-jobs`,
1647
+ method: 'POST',
1648
+ data: { jobType: 'translate_transcription', targetLocaleId: bulkTranslateLocaleId },
1649
+ });
1650
+ const { queued = 0, skipped = 0 } = res.data ?? {};
1651
+ toast.success(`${queued} job(s) de tradução enfileirado(s). ${skipped} aula(s) ignoradas.`);
1652
+ setTranslateTranscriptionSheetOpen(false);
1653
+ setBulkTranslateLocaleId(null);
1654
+ } catch (err) {
1655
+ toast.error(err instanceof Error ? err.message : 'Erro ao enfileirar traduções em massa.');
1656
+ } finally {
1657
+ setIsBulkTranslating(false);
1658
+ }
1659
+ };
1660
+
1592
1661
  const handleBulkJob = async (
1593
1662
  jobType: 'transcription' | 'xp_recalculation' | 'video_processing',
1594
1663
  opts?: { reprocessAlreadyProcessed?: boolean },
@@ -1771,7 +1840,7 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
1771
1840
  {isBulkJobPending || isFetchingVideoStats ? (
1772
1841
  <Loader2 className="size-3.5 animate-spin" />
1773
1842
  ) : (
1774
- <Briefcase className="size-3.5" />
1843
+ <MoreVertical className="size-3.5" />
1775
1844
  )}
1776
1845
  </Button>
1777
1846
  </DropdownMenuTrigger>
@@ -1779,37 +1848,90 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
1779
1848
  <DropdownMenuLabel className="text-xs">Jobs em massa</DropdownMenuLabel>
1780
1849
  <DropdownMenuSeparator />
1781
1850
  {lmsSettings.transcriptionEnabled && (
1851
+ <IconActionTooltip
1852
+ side="right"
1853
+ label="Reprocessa a transcrição de áudio de todas as aulas do curso"
1854
+ >
1855
+ <DropdownMenuItem
1856
+ onSelect={() => setPendingBulkJob('transcription')}
1857
+ className="gap-2"
1858
+ >
1859
+ <Mic className="size-4 text-muted-foreground" />
1860
+ Refazer transcrições
1861
+ </DropdownMenuItem>
1862
+ </IconActionTooltip>
1863
+ )}
1864
+ {lmsSettings.transcriptionEnabled && (
1865
+ <IconActionTooltip
1866
+ side="right"
1867
+ label="Gera traduções das transcrições das aulas para outro idioma"
1868
+ >
1869
+ <DropdownMenuItem
1870
+ onSelect={() => setTranslateTranscriptionSheetOpen(true)}
1871
+ className="gap-2"
1872
+ >
1873
+ <Languages className="size-4 text-muted-foreground" />
1874
+ Traduzir transcrições
1875
+ </DropdownMenuItem>
1876
+ </IconActionTooltip>
1877
+ )}
1878
+ <IconActionTooltip
1879
+ side="right"
1880
+ label="Recalcula a pontuação de XP de todas as aulas do curso"
1881
+ >
1782
1882
  <DropdownMenuItem
1783
- onSelect={() => setPendingBulkJob('transcription')}
1883
+ onSelect={() => setPendingBulkJob('xp_recalculation')}
1784
1884
  className="gap-2"
1785
1885
  >
1786
- <Mic className="size-4 text-muted-foreground" />
1787
- Refazer transcrição de todas as aulas
1886
+ <Zap className="size-4 text-muted-foreground" />
1887
+ Recalcular XP
1788
1888
  </DropdownMenuItem>
1789
- )}
1790
- <DropdownMenuItem
1791
- onSelect={() => setPendingBulkJob('xp_recalculation')}
1792
- className="gap-2"
1889
+ </IconActionTooltip>
1890
+ <IconActionTooltip
1891
+ side="right"
1892
+ label="Inicia o processamento de todos os vídeos do curso"
1793
1893
  >
1794
- <Zap className="size-4 text-muted-foreground" />
1795
- Recalcular XP de todas as aulas
1796
- </DropdownMenuItem>
1797
- <DropdownMenuItem
1798
- onSelect={handleOpenVideoProcessingDialog}
1799
- className="gap-2"
1800
- >
1801
- <Video className="size-4 text-muted-foreground" />
1802
- Processar todos os vídeos
1803
- </DropdownMenuItem>
1894
+ <DropdownMenuItem
1895
+ onSelect={handleOpenVideoProcessingDialog}
1896
+ className="gap-2"
1897
+ >
1898
+ <Video className="size-4 text-muted-foreground" />
1899
+ Processar vídeos
1900
+ </DropdownMenuItem>
1901
+ </IconActionTooltip>
1804
1902
  <DropdownMenuSeparator />
1805
1903
  <DropdownMenuLabel className="text-xs">Exportar</DropdownMenuLabel>
1806
- <DropdownMenuItem
1807
- onSelect={() => setExportSheetOpen(true)}
1808
- className="gap-2"
1904
+ <IconActionTooltip
1905
+ side="right"
1906
+ label="Exporta o curso no formato SCORM 1.2"
1809
1907
  >
1810
- <Download className="size-4 text-muted-foreground" />
1811
- SCORM 1.2
1812
- </DropdownMenuItem>
1908
+ <DropdownMenuItem
1909
+ onSelect={() => setExportSheetOpen(true)}
1910
+ className="gap-2"
1911
+ >
1912
+ <Download className="size-4 text-muted-foreground" />
1913
+ SCORM 1.2
1914
+ </DropdownMenuItem>
1915
+ </IconActionTooltip>
1916
+ {lmsSettings.transcriptionEnabled && (
1917
+ <>
1918
+ <DropdownMenuSeparator />
1919
+ <DropdownMenuLabel className="text-xs">Exclusão</DropdownMenuLabel>
1920
+ <IconActionTooltip
1921
+ side="right"
1922
+ label="Remove todas as transcrições e traduções de todas as aulas do curso"
1923
+ >
1924
+ <DropdownMenuItem
1925
+ onSelect={handleDeleteAllTranscriptions}
1926
+ disabled={isDeletingTranscriptions}
1927
+ className="gap-2 text-destructive focus:text-destructive"
1928
+ >
1929
+ <Trash2 className="size-4" />
1930
+ Excluir transcrições
1931
+ </DropdownMenuItem>
1932
+ </IconActionTooltip>
1933
+ </>
1934
+ )}
1813
1935
  </DropdownMenuContent>
1814
1936
  </DropdownMenu>
1815
1937
 
@@ -1897,6 +2019,71 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
1897
2019
  </DialogFooter>
1898
2020
  </DialogContent>
1899
2021
  </Dialog>
2022
+
2023
+ {/* ── Sheet: Traduzir transcrições em massa ──────────────────── */}
2024
+ <Sheet
2025
+ open={translateTranscriptionSheetOpen}
2026
+ onOpenChange={(open) => {
2027
+ setTranslateTranscriptionSheetOpen(open);
2028
+ if (!open) setBulkTranslateLocaleId(null);
2029
+ }}
2030
+ >
2031
+ <ResizableSheetContent sheetId="bulk-translate-transcription" side="right">
2032
+ <SheetHeader className="px-4 pt-4">
2033
+ <SheetTitle>Traduzir transcrições — todas as aulas</SheetTitle>
2034
+ <SheetDescription>
2035
+ Selecione o idioma-alvo. Um job de tradução será criado para cada
2036
+ aula que já tiver transcrição neste curso. Aulas sem transcrição
2037
+ serão ignoradas.
2038
+ </SheetDescription>
2039
+ </SheetHeader>
2040
+ <div className="flex flex-col gap-4 px-4 pt-4">
2041
+ <EntityPicker
2042
+ placeholder="Selecione o idioma"
2043
+ value={bulkTranslateLocaleId ?? undefined}
2044
+ onChange={(val) => setBulkTranslateLocaleId(val ? Number(val) : null)}
2045
+ showCreateButton
2046
+ loadOptions={async ({ page, search }) => {
2047
+ const res = await request<{
2048
+ data: Array<{ id: number; name: string; code: string; region?: string }>;
2049
+ }>({
2050
+ url: `/core/locales?page=${page}&search=${encodeURIComponent(search)}`,
2051
+ method: 'GET',
2052
+ });
2053
+ return res.data?.data ?? [];
2054
+ }}
2055
+ getOptionValue={(l) => l.id}
2056
+ getOptionLabel={(l) => l.name}
2057
+ createTitle="Adicionar idioma"
2058
+ createFields={[
2059
+ { name: 'name', label: 'Nome', placeholder: 'Ex: Français (France)' },
2060
+ { name: 'code', label: 'Código', placeholder: 'fr' },
2061
+ { name: 'region', label: 'Região', placeholder: 'FR' },
2062
+ ]}
2063
+ onCreate={async (values) => {
2064
+ const res = await request<{
2065
+ id: number;
2066
+ name: string;
2067
+ code: string;
2068
+ region?: string;
2069
+ }>({
2070
+ url: '/core/locales',
2071
+ method: 'POST',
2072
+ data: { ...values, enabled: false },
2073
+ });
2074
+ return res.data ?? null;
2075
+ }}
2076
+ />
2077
+ <Button
2078
+ onClick={handleBulkTranslateTranscription}
2079
+ disabled={!bulkTranslateLocaleId || isBulkTranslating}
2080
+ >
2081
+ {isBulkTranslating && <Loader2 className="size-4 mr-2 animate-spin" />}
2082
+ Enfileirar traduções
2083
+ </Button>
2084
+ </div>
2085
+ </ResizableSheetContent>
2086
+ </Sheet>
1900
2087
  </div>
1901
2088
  </div>
1902
2089
 
@@ -1930,6 +2117,7 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
1930
2117
  'publicacao',
1931
2118
  'extra',
1932
2119
  'exportacoes',
2120
+ 'custos-ia',
1933
2121
  ] as const
1934
2122
  ).map((tab) => (
1935
2123
  <TabsTrigger
@@ -1958,7 +2146,9 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
1958
2146
  ? 'Operações'
1959
2147
  : tab === 'exportacoes'
1960
2148
  ? 'Exportações'
1961
- : t('structureEditor.tabs.publish')}
2149
+ : tab === 'custos-ia'
2150
+ ? 'Custos de IA'
2151
+ : t('structureEditor.tabs.publish')}
1962
2152
  </TabsTrigger>
1963
2153
  ))}
1964
2154
  </TabsList>
@@ -2111,7 +2301,7 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
2111
2301
  region?: string;
2112
2302
  }>
2113
2303
  >({
2114
- url: `/core/locales?page=${page}&search=${encodeURIComponent(search ?? '')}`,
2304
+ url: `/core/locales?page=${page}&search=${encodeURIComponent(search ?? '')}&enabled=true`,
2115
2305
  method: 'GET',
2116
2306
  });
2117
2307
 
@@ -2209,6 +2399,11 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
2209
2399
  compact
2210
2400
  />
2211
2401
  <CourseContentCard form={form} compact />
2402
+ <CourseDangerZoneCard
2403
+ t={t}
2404
+ onDelete={openDeleteDialog}
2405
+ compact
2406
+ />
2212
2407
  </TabsContent>
2213
2408
 
2214
2409
  <TabsContent value="xp" className="mt-0 min-w-0">
@@ -2218,6 +2413,11 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
2218
2413
  />
2219
2414
  </TabsContent>
2220
2415
 
2416
+ {/* ── Tab: Custos de IA ───────────────────────────────────── */}
2417
+ <TabsContent value="custos-ia" className="mt-0 min-w-0">
2418
+ <CourseAiCostsTab courseId={courseId} />
2419
+ </TabsContent>
2420
+
2221
2421
  {/* ── Tab: Mídia ──────────────────────────────────────────── */}
2222
2422
  <TabsContent value="midia" className="mt-0 min-w-0">
2223
2423
  <CourseMediaCard
@@ -2469,7 +2669,7 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
2469
2669
  </Card>
2470
2670
  </TabsContent>
2471
2671
 
2472
- {/* ── Tab: Extra ──────────────────────────────────────────── */}
2672
+ {/* ── Tab: Certificado ────────────────────────────────────── */}
2473
2673
  <TabsContent
2474
2674
  value="extra"
2475
2675
  className="mt-0 flex min-w-0 flex-col gap-3"
@@ -2481,12 +2681,13 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
2481
2681
  onCreateTemplate={handleCreateCertificateTemplate}
2482
2682
  compact
2483
2683
  />
2484
- <CourseFlagsCard form={form} t={t} compact />
2485
- <CourseDangerZoneCard
2684
+ <CourseFlagsCard
2685
+ form={form}
2486
2686
  t={t}
2487
- onDelete={openDeleteDialog}
2687
+ flags={['certificado']}
2488
2688
  compact
2489
2689
  />
2690
+ <CourseIssuedCertificatesCard courseId={courseId} compact />
2490
2691
  </TabsContent>
2491
2692
 
2492
2693
  {/* ── Tab: Operações ──────────────────────────────────────── */}
@@ -2578,6 +2779,13 @@ export function EditorCourse({ defaultTab, onTabChange }: EditorCourseProps) {
2578
2779
  </Button>
2579
2780
  </CardContent>
2580
2781
  </Card>
2782
+
2783
+ <CourseFlagsCard
2784
+ form={form}
2785
+ t={t}
2786
+ flags={['destaque', 'listado']}
2787
+ compact
2788
+ />
2581
2789
  </TabsContent>
2582
2790
 
2583
2791
  {/* ── Tab: Exportações ────────────────────────────────────────── */}