@hed-hog/lms 0.0.364 → 0.0.366

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 (290) hide show
  1. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +1 -0
  2. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
  3. package/dist/bitcode-wallet/bitcode-wallet.service.js +22 -3
  4. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
  5. package/dist/class-group/class-group.controller.d.ts +1 -0
  6. package/dist/class-group/class-group.controller.d.ts.map +1 -1
  7. package/dist/class-group/class-group.service.d.ts +1 -0
  8. package/dist/class-group/class-group.service.d.ts.map +1 -1
  9. package/dist/course/course-export-scorm12-worker.service.d.ts +21 -0
  10. package/dist/course/course-export-scorm12-worker.service.d.ts.map +1 -0
  11. package/dist/course/course-export-scorm12-worker.service.js +109 -0
  12. package/dist/course/course-export-scorm12-worker.service.js.map +1 -0
  13. package/dist/course/course-export-scorm12.service.d.ts +42 -0
  14. package/dist/course/course-export-scorm12.service.d.ts.map +1 -0
  15. package/dist/course/course-export-scorm12.service.js +628 -0
  16. package/dist/course/course-export-scorm12.service.js.map +1 -0
  17. package/dist/course/course-export.service.d.ts +84 -0
  18. package/dist/course/course-export.service.d.ts.map +1 -0
  19. package/dist/course/course-export.service.js +237 -0
  20. package/dist/course/course-export.service.js.map +1 -0
  21. package/dist/course/course-structure.controller.d.ts +20 -10
  22. package/dist/course/course-structure.controller.d.ts.map +1 -1
  23. package/dist/course/course-structure.controller.js +20 -4
  24. package/dist/course/course-structure.controller.js.map +1 -1
  25. package/dist/course/course-structure.service.d.ts +12 -4
  26. package/dist/course/course-structure.service.d.ts.map +1 -1
  27. package/dist/course/course-structure.service.js +98 -23
  28. package/dist/course/course-structure.service.js.map +1 -1
  29. package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
  30. package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
  31. package/dist/course/course-video-agent-pipeline.service.js +398 -0
  32. package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
  33. package/dist/course/course-video-hls.service.d.ts +71 -0
  34. package/dist/course/course-video-hls.service.d.ts.map +1 -0
  35. package/dist/course/course-video-hls.service.js +784 -0
  36. package/dist/course/course-video-hls.service.js.map +1 -0
  37. package/dist/course/course.controller.d.ts +47 -13
  38. package/dist/course/course.controller.d.ts.map +1 -1
  39. package/dist/course/course.controller.js +40 -26
  40. package/dist/course/course.controller.js.map +1 -1
  41. package/dist/course/course.mcp-tools.js +1 -1
  42. package/dist/course/course.mcp-tools.js.map +1 -1
  43. package/dist/course/course.module.d.ts.map +1 -1
  44. package/dist/course/course.module.js +16 -0
  45. package/dist/course/course.module.js.map +1 -1
  46. package/dist/course/course.service.d.ts +8 -9
  47. package/dist/course/course.service.d.ts.map +1 -1
  48. package/dist/course/course.service.js +93 -50
  49. package/dist/course/course.service.js.map +1 -1
  50. package/dist/course/dto/cleanup-course-storage.dto.d.ts +1 -1
  51. package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -1
  52. package/dist/course/dto/cleanup-course-storage.dto.js +1 -0
  53. package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -1
  54. package/dist/course/dto/cleanup-upload-history.dto.d.ts +1 -1
  55. package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -1
  56. package/dist/course/dto/cleanup-upload-history.dto.js +1 -1
  57. package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -1
  58. package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
  59. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
  60. package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
  61. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
  62. package/dist/course/dto/create-course-export.dto.d.ts +14 -0
  63. package/dist/course/dto/create-course-export.dto.d.ts.map +1 -0
  64. package/dist/course/dto/create-course-export.dto.js +71 -0
  65. package/dist/course/dto/create-course-export.dto.js.map +1 -0
  66. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +2 -2
  67. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  68. package/dist/course/dto/create-course-structure-lesson.dto.js +3 -2
  69. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  70. package/dist/course/ffmpeg.util.d.ts +10 -0
  71. package/dist/course/ffmpeg.util.d.ts.map +1 -0
  72. package/dist/course/ffmpeg.util.js +79 -0
  73. package/dist/course/ffmpeg.util.js.map +1 -0
  74. package/dist/course/lms-bulk-upload-automation.service.d.ts +18 -1
  75. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  76. package/dist/course/lms-bulk-upload-automation.service.js +106 -8
  77. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  78. package/dist/course/lms-bulk-upload-infra.service.d.ts +1 -0
  79. package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -1
  80. package/dist/course/lms-bulk-upload-infra.service.js +32 -8
  81. package/dist/course/lms-bulk-upload-infra.service.js.map +1 -1
  82. package/dist/course/lms-bulk-upload.controller.d.ts +30 -3
  83. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  84. package/dist/course/lms-bulk-upload.controller.js +43 -2
  85. package/dist/course/lms-bulk-upload.controller.js.map +1 -1
  86. package/dist/course/lms-bulk-upload.service.d.ts +11 -0
  87. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  88. package/dist/course/lms-bulk-upload.service.js +59 -6
  89. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  90. package/dist/course/lms-setting.controller.d.ts +2 -1
  91. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  92. package/dist/course/lms-setting.controller.js +4 -2
  93. package/dist/course/lms-setting.controller.js.map +1 -1
  94. package/dist/course/scorm12-schemas.d.ts +4 -0
  95. package/dist/course/scorm12-schemas.d.ts.map +1 -0
  96. package/dist/course/scorm12-schemas.js +9 -0
  97. package/dist/course/scorm12-schemas.js.map +1 -0
  98. package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
  99. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  100. package/dist/enterprise/training/training-admin.service.d.ts +2 -0
  101. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  102. package/dist/enterprise/training/training-student.service.d.ts +51 -0
  103. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  104. package/dist/enterprise/training/training-student.service.js +217 -4
  105. package/dist/enterprise/training/training-student.service.js.map +1 -1
  106. package/dist/evaluation/evaluation.service.d.ts +18 -0
  107. package/dist/evaluation/evaluation.service.d.ts.map +1 -1
  108. package/dist/evaluation/evaluation.service.js +125 -0
  109. package/dist/evaluation/evaluation.service.js.map +1 -1
  110. package/dist/exam/dto/create-standalone-question.dto.d.ts +12 -0
  111. package/dist/exam/dto/create-standalone-question.dto.d.ts.map +1 -0
  112. package/dist/exam/dto/create-standalone-question.dto.js +70 -0
  113. package/dist/exam/dto/create-standalone-question.dto.js.map +1 -0
  114. package/dist/exam/exam.module.d.ts.map +1 -1
  115. package/dist/exam/exam.module.js +2 -1
  116. package/dist/exam/exam.module.js.map +1 -1
  117. package/dist/exam/exam.service.d.ts +21 -0
  118. package/dist/exam/exam.service.d.ts.map +1 -1
  119. package/dist/exam/exam.service.js +80 -0
  120. package/dist/exam/exam.service.js.map +1 -1
  121. package/dist/exam/question.controller.d.ts +27 -0
  122. package/dist/exam/question.controller.d.ts.map +1 -0
  123. package/dist/exam/question.controller.js +53 -0
  124. package/dist/exam/question.controller.js.map +1 -0
  125. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +4 -0
  126. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
  127. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +161 -25
  128. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
  129. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
  130. package/dist/lms-commerce-access.subscriber.d.ts +11 -0
  131. package/dist/lms-commerce-access.subscriber.d.ts.map +1 -0
  132. package/dist/lms-commerce-access.subscriber.js +74 -0
  133. package/dist/lms-commerce-access.subscriber.js.map +1 -0
  134. package/dist/lms.module.d.ts.map +1 -1
  135. package/dist/lms.module.js +16 -5
  136. package/dist/lms.module.js.map +1 -1
  137. package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
  138. package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
  139. package/dist/platforma/dto/heartbeat.dto.js +50 -0
  140. package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
  141. package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
  142. package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
  143. package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
  144. package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
  145. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
  146. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
  147. package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
  148. package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
  149. package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
  150. package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
  151. package/dist/platforma/platforma-heartbeat.service.js +50 -0
  152. package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
  153. package/dist/platforma/platforma-performance.service.d.ts +121 -0
  154. package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
  155. package/dist/platforma/platforma-performance.service.js +500 -0
  156. package/dist/platforma/platforma-performance.service.js.map +1 -0
  157. package/dist/platforma/platforma-search.service.d.ts +21 -0
  158. package/dist/platforma/platforma-search.service.d.ts.map +1 -0
  159. package/dist/platforma/platforma-search.service.js +64 -0
  160. package/dist/platforma/platforma-search.service.js.map +1 -0
  161. package/dist/platforma/platforma-video.service.d.ts +39 -0
  162. package/dist/platforma/platforma-video.service.d.ts.map +1 -0
  163. package/dist/platforma/platforma-video.service.js +301 -0
  164. package/dist/platforma/platforma-video.service.js.map +1 -0
  165. package/dist/platforma/platforma.controller.d.ts +209 -1
  166. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  167. package/dist/platforma/platforma.controller.js +208 -2
  168. package/dist/platforma/platforma.controller.js.map +1 -1
  169. package/dist/realtime/lms-realtime.controller.d.ts +2 -0
  170. package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
  171. package/dist/realtime/lms-realtime.controller.js +31 -0
  172. package/dist/realtime/lms-realtime.controller.js.map +1 -1
  173. package/dist/realtime/lms-realtime.service.d.ts +1 -1
  174. package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
  175. package/dist/realtime/lms-realtime.service.js.map +1 -1
  176. package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts +5 -0
  177. package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts.map +1 -0
  178. package/dist/student-xp/dto/grant-skill-card-xp.dto.js +26 -0
  179. package/dist/student-xp/dto/grant-skill-card-xp.dto.js.map +1 -0
  180. package/dist/student-xp/student-xp.controller.d.ts +15 -0
  181. package/dist/student-xp/student-xp.controller.d.ts.map +1 -1
  182. package/dist/student-xp/student-xp.controller.js +24 -0
  183. package/dist/student-xp/student-xp.controller.js.map +1 -1
  184. package/dist/student-xp/student-xp.service.d.ts +16 -0
  185. package/dist/student-xp/student-xp.service.d.ts.map +1 -1
  186. package/dist/student-xp/student-xp.service.js +51 -1
  187. package/dist/student-xp/student-xp.service.js.map +1 -1
  188. package/hedhog/data/evaluation_topic.yaml +17 -0
  189. package/hedhog/data/menu.yaml +0 -17
  190. package/hedhog/data/queue_definition.yaml +48 -0
  191. package/hedhog/data/route.yaml +94 -124
  192. package/hedhog/data/setting_group.yaml +19 -19
  193. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +337 -41
  194. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
  195. package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
  196. package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
  197. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +69 -4
  198. package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +420 -0
  199. package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +308 -0
  200. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +158 -45
  201. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
  202. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
  203. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -63
  204. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +8 -3
  205. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +31 -8
  206. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +16 -9
  207. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +201 -401
  208. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +378 -690
  209. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -2
  210. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +3 -9
  211. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +1 -1
  212. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
  213. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +6 -10
  214. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +49 -0
  215. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -3
  216. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -1
  217. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +106 -0
  218. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +28 -1
  219. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +0 -2
  220. package/hedhog/frontend/app/courses/page.tsx.ejs +85 -9
  221. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
  222. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
  223. package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
  224. package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
  225. package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
  226. package/hedhog/frontend/app/paths/page.tsx.ejs +38 -4
  227. package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
  228. package/hedhog/frontend/messages/en.json +44 -28
  229. package/hedhog/frontend/messages/pt.json +47 -29
  230. package/hedhog/table/course_enrollment.yaml +3 -0
  231. package/hedhog/table/course_export.yaml +62 -0
  232. package/hedhog/table/lesson_view_event.yaml +66 -0
  233. package/package.json +14 -9
  234. package/src/bitcode-wallet/bitcode-wallet.service.ts +43 -4
  235. package/src/course/course-export-scorm12-worker.service.ts +124 -0
  236. package/src/course/course-export-scorm12.service.ts +668 -0
  237. package/src/course/course-export.service.ts +280 -0
  238. package/src/course/course-structure.controller.ts +16 -2
  239. package/src/course/course-structure.service.ts +100 -7
  240. package/src/course/course-video-agent-pipeline.service.ts +471 -0
  241. package/src/course/course-video-hls.service.ts +966 -0
  242. package/src/course/course.controller.ts +33 -19
  243. package/src/course/course.mcp-tools.ts +1 -1
  244. package/src/course/course.module.ts +16 -0
  245. package/src/course/course.service.ts +119 -61
  246. package/src/course/dto/cleanup-course-storage.dto.ts +1 -0
  247. package/src/course/dto/cleanup-upload-history.dto.ts +1 -1
  248. package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
  249. package/src/course/dto/create-course-export.dto.ts +56 -0
  250. package/src/course/dto/create-course-structure-lesson.dto.ts +4 -3
  251. package/src/course/ffmpeg.util.ts +65 -0
  252. package/src/course/lms-bulk-upload-automation.service.ts +156 -6
  253. package/src/course/lms-bulk-upload-infra.service.ts +39 -6
  254. package/src/course/lms-bulk-upload.controller.ts +32 -2
  255. package/src/course/lms-bulk-upload.service.ts +70 -7
  256. package/src/course/lms-setting.controller.ts +4 -2
  257. package/src/course/scorm12-schemas.ts +9 -0
  258. package/src/enterprise/training/training-student.service.ts +221 -2
  259. package/src/evaluation/evaluation.service.ts +123 -0
  260. package/src/exam/dto/create-standalone-question.dto.ts +66 -0
  261. package/src/exam/exam.module.ts +2 -1
  262. package/src/exam/exam.service.ts +86 -0
  263. package/src/exam/question.controller.ts +28 -0
  264. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +205 -31
  265. package/src/lms-commerce-access.subscriber.ts +88 -0
  266. package/src/lms.module.ts +16 -5
  267. package/src/platforma/dto/heartbeat.dto.ts +30 -0
  268. package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
  269. package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
  270. package/src/platforma/platforma-heartbeat.service.ts +33 -0
  271. package/src/platforma/platforma-performance.service.ts +606 -0
  272. package/src/platforma/platforma-search.service.ts +48 -0
  273. package/src/platforma/platforma-video.service.ts +346 -0
  274. package/src/platforma/platforma.controller.ts +137 -1
  275. package/src/platforma/platforma.service.ts +268 -268
  276. package/src/realtime/lms-realtime.controller.ts +27 -1
  277. package/src/realtime/lms-realtime.service.ts +2 -1
  278. package/src/student-xp/dto/grant-skill-card-xp.dto.ts +10 -0
  279. package/src/student-xp/student-xp.controller.ts +18 -2
  280. package/src/student-xp/student-xp.service.ts +84 -2
  281. package/hedhog/data/video_resolution_profile.yaml +0 -7
  282. package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +0 -607
  283. package/hedhog/table/course_video_resolution_profile.yaml +0 -22
  284. package/hedhog/table/video_resolution_profile.yaml +0 -18
  285. package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +0 -16
  286. package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +0 -16
  287. package/src/video-resolution-profile/video-resolution-profile.controller.ts +0 -62
  288. package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +0 -128
  289. package/src/video-resolution-profile/video-resolution-profile.module.ts +0 -13
  290. package/src/video-resolution-profile/video-resolution-profile.service.ts +0 -117
@@ -0,0 +1,420 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { toast } from 'sonner';
5
+ import {
6
+ AlignLeft,
7
+ AlignRight,
8
+ Download,
9
+ FileArchive,
10
+ Info,
11
+ Loader2,
12
+ Monitor,
13
+ Moon,
14
+ PieChart,
15
+ Sun,
16
+ Type,
17
+ } from 'lucide-react';
18
+ import { cn } from '@/lib/utils';
19
+ import { Alert, AlertDescription } from '@/components/ui/alert';
20
+ import { Badge } from '@/components/ui/badge';
21
+ import { Button } from '@/components/ui/button';
22
+ import { Label } from '@/components/ui/label';
23
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
24
+ import {
25
+ Sheet,
26
+ SheetDescription,
27
+ SheetFooter,
28
+ SheetHeader,
29
+ SheetTitle,
30
+ } from '@/components/ui/sheet';
31
+ import {
32
+ ScormVisualSettings,
33
+ useCreateCourseExportMutation,
34
+ } from '../_data/use-course-exports';
35
+
36
+ // ── Color presets ────────────────────────────────────────────
37
+ const PRESET_COLORS = [
38
+ '#2563eb',
39
+ '#4338ca',
40
+ '#7c3aed',
41
+ '#e11d48',
42
+ '#ea580c',
43
+ '#059669',
44
+ '#0891b2',
45
+ '#475569',
46
+ ];
47
+
48
+ const DEFAULT_VISUAL: Required<ScormVisualSettings> = {
49
+ primaryColor: '#2563eb',
50
+ fontFamily: 'system',
51
+ fontSize: 'md',
52
+ sidebarWidth: 280,
53
+ sidebarPosition: 'left',
54
+ progressStyle: 'bar',
55
+ sidebarTheme: 'light',
56
+ };
57
+
58
+ // ── Segmented control ────────────────────────────────────────
59
+ type SegOption<T extends string> = {
60
+ label: string;
61
+ value: T;
62
+ icon?: React.ReactNode;
63
+ };
64
+
65
+ function Seg<T extends string>({
66
+ value,
67
+ onChange,
68
+ options,
69
+ }: {
70
+ value: T;
71
+ onChange: (v: T) => void;
72
+ options: SegOption<T>[];
73
+ }) {
74
+ return (
75
+ <div className="flex rounded-md border overflow-hidden bg-background">
76
+ {options.map((opt, i) => (
77
+ <button
78
+ key={opt.value}
79
+ type="button"
80
+ onClick={() => onChange(opt.value)}
81
+ className={cn(
82
+ 'flex items-center gap-1.5 flex-1 justify-center px-2.5 py-1.5 text-xs font-medium transition-colors',
83
+ i < options.length - 1 && 'border-r',
84
+ value === opt.value
85
+ ? 'bg-primary text-primary-foreground'
86
+ : 'text-muted-foreground hover:bg-muted/60',
87
+ )}
88
+ >
89
+ {opt.icon}
90
+ {opt.label}
91
+ </button>
92
+ ))}
93
+ </div>
94
+ );
95
+ }
96
+
97
+ // ── Props ─────────────────────────────────────────────────────
98
+ interface CourseExportSheetProps {
99
+ open: boolean;
100
+ onOpenChange: (open: boolean) => void;
101
+ courseId: string;
102
+ sessionCount: number;
103
+ videoLessonCount: number;
104
+ estimatedSizeMb?: number | null;
105
+ }
106
+
107
+ function formatMb(mb: number): string {
108
+ if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`;
109
+ return `${mb.toFixed(0)} MB`;
110
+ }
111
+
112
+ // ── Component ─────────────────────────────────────────────────
113
+ export function CourseExportSheet({
114
+ open,
115
+ onOpenChange,
116
+ courseId,
117
+ sessionCount,
118
+ videoLessonCount,
119
+ estimatedSizeMb,
120
+ }: CourseExportSheetProps) {
121
+ const [visual, setVisual] = useState<Required<ScormVisualSettings>>(DEFAULT_VISUAL);
122
+ const createExport = useCreateCourseExportMutation(courseId);
123
+
124
+ const setV = <K extends keyof ScormVisualSettings>(
125
+ key: K,
126
+ value: Required<ScormVisualSettings>[K],
127
+ ) => setVisual((prev) => ({ ...prev, [key]: value }));
128
+
129
+ const handleConfirm = async () => {
130
+ try {
131
+ await createExport.mutateAsync({
132
+ format: 'scorm_1_2',
133
+ visualSettings: visual,
134
+ });
135
+ onOpenChange(false);
136
+ toast.success(
137
+ 'Exportação SCORM 1.2 iniciada. Acompanhe o progresso nas notificações.',
138
+ );
139
+ } catch {
140
+ toast.error('Erro ao iniciar exportação. Tente novamente.');
141
+ }
142
+ };
143
+
144
+ return (
145
+ <Sheet open={open} onOpenChange={onOpenChange}>
146
+ <ResizableSheetContent
147
+ sheetId="lms-course-export-scorm12-sheet"
148
+ defaultWidth={540}
149
+ minWidth={420}
150
+ maxWidth={780}
151
+ >
152
+ <SheetHeader>
153
+ <SheetTitle className="flex items-center gap-2">
154
+ <FileArchive className="size-4" />
155
+ Exportar para SCORM 1.2
156
+ </SheetTitle>
157
+ <SheetDescription>
158
+ Gera um pacote SCORM 1.2 com as aulas de vídeo do curso. O
159
+ processamento é feito em background.
160
+ </SheetDescription>
161
+ </SheetHeader>
162
+
163
+ <div className="flex flex-col gap-4 px-4 py-4">
164
+
165
+ {/* ── Stats ── */}
166
+ <div className="rounded-lg border bg-muted/30 p-3 flex flex-col gap-2">
167
+ <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
168
+ Conteúdo a exportar
169
+ </p>
170
+ <div className="flex flex-wrap gap-2">
171
+ <Badge variant="secondary">
172
+ {sessionCount} {sessionCount === 1 ? 'sessão' : 'sessões'}
173
+ </Badge>
174
+ <Badge variant="secondary">
175
+ {videoLessonCount}{' '}
176
+ {videoLessonCount === 1 ? 'aula de vídeo' : 'aulas de vídeo'}
177
+ </Badge>
178
+ {estimatedSizeMb != null && (
179
+ <Badge variant="outline">~{formatMb(estimatedSizeMb)}</Badge>
180
+ )}
181
+ </div>
182
+ {videoLessonCount === 0 && (
183
+ <p className="text-xs text-destructive mt-1">
184
+ Este curso não possui aulas de vídeo. Não é possível gerar o
185
+ pacote SCORM.
186
+ </p>
187
+ )}
188
+ </div>
189
+
190
+ {estimatedSizeMb != null && estimatedSizeMb > 500 && (
191
+ <Alert>
192
+ <Info className="size-4" />
193
+ <AlertDescription className="text-xs">
194
+ O pacote gerado pode ter aproximadamente{' '}
195
+ <strong>{formatMb(estimatedSizeMb)}</strong>. Certifique-se que
196
+ seu LMS suporta pacotes desse tamanho.
197
+ </AlertDescription>
198
+ </Alert>
199
+ )}
200
+
201
+ {/* ── Appearance ── */}
202
+ <div className="rounded-lg border overflow-hidden">
203
+ <div className="flex items-center gap-2 px-3 py-2.5 border-b bg-muted/20">
204
+ <Monitor className="size-4 text-muted-foreground" />
205
+ <span className="text-sm font-medium">Personalizar aparência</span>
206
+ </div>
207
+
208
+ <div className="px-3 pt-3 pb-4 flex flex-col gap-4">
209
+
210
+ {/* Primary color */}
211
+ <div className="flex flex-col gap-2">
212
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
213
+ Cor Principal
214
+ </Label>
215
+ <div className="flex flex-wrap items-center gap-2">
216
+ {PRESET_COLORS.map((c) => (
217
+ <button
218
+ key={c}
219
+ type="button"
220
+ onClick={() => setV('primaryColor', c)}
221
+ title={c}
222
+ className={cn(
223
+ 'w-7 h-7 rounded-full border-2 transition-all hover:scale-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
224
+ visual.primaryColor === c
225
+ ? 'border-foreground ring-2 ring-offset-1 ring-foreground/30 scale-110'
226
+ : 'border-transparent',
227
+ )}
228
+ style={{ background: c }}
229
+ />
230
+ ))}
231
+ {/* Custom color picker */}
232
+ <label className="cursor-pointer" title="Cor personalizada">
233
+ <input
234
+ type="color"
235
+ value={visual.primaryColor}
236
+ onChange={(e) => setV('primaryColor', e.target.value)}
237
+ className="sr-only"
238
+ />
239
+ <div
240
+ className={cn(
241
+ 'w-7 h-7 rounded-full border-2 border-dashed transition-all hover:scale-110',
242
+ !PRESET_COLORS.includes(visual.primaryColor)
243
+ ? 'border-foreground ring-2 ring-offset-1 ring-foreground/30 scale-110'
244
+ : 'border-muted-foreground/50',
245
+ )}
246
+ style={{ background: visual.primaryColor }}
247
+ />
248
+ </label>
249
+ </div>
250
+ <div className="flex items-center gap-1.5">
251
+ <div
252
+ className="w-3 h-3 rounded-sm border border-border/60 shrink-0"
253
+ style={{ background: visual.primaryColor }}
254
+ />
255
+ <span className="text-xs font-mono text-muted-foreground">
256
+ {visual.primaryColor.toUpperCase()}
257
+ </span>
258
+ </div>
259
+ </div>
260
+
261
+ {/* Font family */}
262
+ <div className="flex flex-col gap-2">
263
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide flex items-center gap-1.5">
264
+ <Type className="size-3" />
265
+ Fonte
266
+ </Label>
267
+ <Seg
268
+ value={visual.fontFamily as 'system' | 'humanist' | 'serif' | 'mono'}
269
+ onChange={(v) => setV('fontFamily', v)}
270
+ options={[
271
+ { label: 'Sistema', value: 'system' as const },
272
+ { label: 'Humanista', value: 'humanist' as const },
273
+ { label: 'Serifada', value: 'serif' as const },
274
+ { label: 'Mono', value: 'mono' as const },
275
+ ]}
276
+ />
277
+ </div>
278
+
279
+ {/* Font size + Sidebar width */}
280
+ <div className="grid grid-cols-2 gap-3">
281
+ <div className="flex flex-col gap-2">
282
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
283
+ Tamanho da fonte
284
+ </Label>
285
+ <Seg
286
+ value={visual.fontSize as 'sm' | 'md' | 'lg'}
287
+ onChange={(v) => setV('fontSize', v)}
288
+ options={[
289
+ { label: 'Peq.', value: 'sm' as const },
290
+ { label: 'Méd.', value: 'md' as const },
291
+ { label: 'Grd.', value: 'lg' as const },
292
+ ]}
293
+ />
294
+ </div>
295
+ <div className="flex flex-col gap-2">
296
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
297
+ Largura do painel —{' '}
298
+ <span className="text-foreground font-normal normal-case">
299
+ {visual.sidebarWidth}px
300
+ </span>
301
+ </Label>
302
+ <div className="flex items-center gap-2 pt-1">
303
+ <span className="text-xs text-muted-foreground">200</span>
304
+ <input
305
+ type="range"
306
+ min={200}
307
+ max={420}
308
+ step={10}
309
+ value={visual.sidebarWidth}
310
+ onChange={(e) => setV('sidebarWidth', Number(e.target.value))}
311
+ className="flex-1 h-1.5 rounded-full cursor-pointer accent-primary"
312
+ />
313
+ <span className="text-xs text-muted-foreground">420</span>
314
+ </div>
315
+ </div>
316
+ </div>
317
+
318
+ {/* Sidebar position + Progress style */}
319
+ <div className="grid grid-cols-2 gap-3">
320
+ <div className="flex flex-col gap-2">
321
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
322
+ Posição do painel
323
+ </Label>
324
+ <Seg
325
+ value={visual.sidebarPosition as 'left' | 'right'}
326
+ onChange={(v) => setV('sidebarPosition', v)}
327
+ options={[
328
+ {
329
+ label: 'Esq.',
330
+ value: 'left' as const,
331
+ icon: <AlignLeft className="size-3" />,
332
+ },
333
+ {
334
+ label: 'Dir.',
335
+ value: 'right' as const,
336
+ icon: <AlignRight className="size-3" />,
337
+ },
338
+ ]}
339
+ />
340
+ </div>
341
+ <div className="flex flex-col gap-2">
342
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
343
+ Indicador de progresso
344
+ </Label>
345
+ <Seg
346
+ value={visual.progressStyle as 'bar' | 'pie'}
347
+ onChange={(v) => setV('progressStyle', v)}
348
+ options={[
349
+ { label: 'Barra', value: 'bar' as const },
350
+ {
351
+ label: 'Pizza',
352
+ value: 'pie' as const,
353
+ icon: <PieChart className="size-3" />,
354
+ },
355
+ ]}
356
+ />
357
+ </div>
358
+ </div>
359
+
360
+ {/* Sidebar theme */}
361
+ <div className="flex flex-col gap-2">
362
+ <Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
363
+ Tema do painel
364
+ </Label>
365
+ <Seg
366
+ value={visual.sidebarTheme as 'light' | 'dark'}
367
+ onChange={(v) => setV('sidebarTheme', v)}
368
+ options={[
369
+ {
370
+ label: 'Claro',
371
+ value: 'light' as const,
372
+ icon: <Sun className="size-3" />,
373
+ },
374
+ {
375
+ label: 'Escuro',
376
+ value: 'dark' as const,
377
+ icon: <Moon className="size-3" />,
378
+ },
379
+ ]}
380
+ />
381
+ </div>
382
+ </div>
383
+ </div>
384
+
385
+ {/* ── Info ── */}
386
+ <div className="rounded-lg border bg-muted/20 p-3">
387
+ <p className="text-xs text-muted-foreground">
388
+ <strong>Padrão SCORM 1.2</strong> — Compatível com a maioria dos
389
+ sistemas de gestão de aprendizagem (LMS). Os arquivos de vídeo são
390
+ incluídos no pacote e o progresso de cada aula é rastreado
391
+ automaticamente.
392
+ </p>
393
+ </div>
394
+ </div>
395
+
396
+ <SheetFooter>
397
+ <Button
398
+ variant="outline"
399
+ onClick={() => onOpenChange(false)}
400
+ disabled={createExport.isPending}
401
+ >
402
+ Cancelar
403
+ </Button>
404
+ <Button
405
+ onClick={handleConfirm}
406
+ disabled={createExport.isPending || videoLessonCount === 0}
407
+ className="gap-2"
408
+ >
409
+ {createExport.isPending ? (
410
+ <Loader2 className="size-4 animate-spin" />
411
+ ) : (
412
+ <Download className="size-4" />
413
+ )}
414
+ Exportar SCORM 1.2
415
+ </Button>
416
+ </SheetFooter>
417
+ </ResizableSheetContent>
418
+ </Sheet>
419
+ );
420
+ }