@hed-hog/lms 0.0.358 → 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.
Files changed (330) hide show
  1. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +65 -0
  2. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
  3. package/dist/bitcode-wallet/bitcode-wallet.service.js +72 -0
  4. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
  5. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts +8 -0
  6. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.d.ts.map +1 -0
  7. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js +40 -0
  8. package/dist/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.js.map +1 -0
  9. package/dist/class-group/class-group.controller.d.ts +16 -16
  10. package/dist/class-group/class-group.service.d.ts +12 -12
  11. package/dist/course/course-audio-transcription.service.d.ts +3 -2
  12. package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
  13. package/dist/course/course-audio-transcription.service.js +49 -8
  14. package/dist/course/course-audio-transcription.service.js.map +1 -1
  15. package/dist/course/course-lesson.controller.d.ts +4 -0
  16. package/dist/course/course-lesson.controller.d.ts.map +1 -1
  17. package/dist/course/course-lesson.controller.js +10 -0
  18. package/dist/course/course-lesson.controller.js.map +1 -1
  19. package/dist/course/course-structure.controller.d.ts +11 -4
  20. package/dist/course/course-structure.controller.d.ts.map +1 -1
  21. package/dist/course/course-structure.controller.js +14 -0
  22. package/dist/course/course-structure.controller.js.map +1 -1
  23. package/dist/course/course-structure.service.d.ts +15 -1
  24. package/dist/course/course-structure.service.d.ts.map +1 -1
  25. package/dist/course/course-structure.service.js +139 -4
  26. package/dist/course/course-structure.service.js.map +1 -1
  27. package/dist/course/course-video-conversion.service.d.ts +8 -0
  28. package/dist/course/course-video-conversion.service.d.ts.map +1 -1
  29. package/dist/course/course-video-conversion.service.js +87 -51
  30. package/dist/course/course-video-conversion.service.js.map +1 -1
  31. package/dist/course/course.controller.d.ts +73 -1
  32. package/dist/course/course.controller.d.ts.map +1 -1
  33. package/dist/course/course.controller.js +27 -3
  34. package/dist/course/course.controller.js.map +1 -1
  35. package/dist/course/course.module.d.ts.map +1 -1
  36. package/dist/course/course.module.js +15 -3
  37. package/dist/course/course.module.js.map +1 -1
  38. package/dist/course/course.service.d.ts +108 -4
  39. package/dist/course/course.service.d.ts.map +1 -1
  40. package/dist/course/course.service.js +631 -30
  41. package/dist/course/course.service.js.map +1 -1
  42. package/dist/course/dto/cleanup-course-storage.dto.d.ts +6 -0
  43. package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -0
  44. package/dist/course/dto/cleanup-course-storage.dto.js +33 -0
  45. package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -0
  46. package/dist/course/dto/cleanup-upload-history.dto.d.ts +9 -0
  47. package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -0
  48. package/dist/course/dto/cleanup-upload-history.dto.js +36 -0
  49. package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -0
  50. package/dist/course/dto/create-course-bulk-job.dto.d.ts +4 -0
  51. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -0
  52. package/dist/course/dto/create-course-bulk-job.dto.js +21 -0
  53. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -0
  54. package/dist/course/lms-bulk-upload-automation.service.d.ts +39 -0
  55. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -0
  56. package/dist/course/lms-bulk-upload-automation.service.js +443 -0
  57. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -0
  58. package/dist/course/lms-bulk-upload-infra.service.d.ts +31 -0
  59. package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -0
  60. package/dist/course/lms-bulk-upload-infra.service.js +277 -0
  61. package/dist/course/lms-bulk-upload-infra.service.js.map +1 -0
  62. package/dist/course/lms-bulk-upload.constants.d.ts +4 -0
  63. package/dist/course/lms-bulk-upload.constants.d.ts.map +1 -0
  64. package/dist/course/lms-bulk-upload.constants.js +7 -0
  65. package/dist/course/lms-bulk-upload.constants.js.map +1 -0
  66. package/dist/course/lms-bulk-upload.controller.d.ts +153 -0
  67. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -0
  68. package/dist/course/lms-bulk-upload.controller.js +129 -0
  69. package/dist/course/lms-bulk-upload.controller.js.map +1 -0
  70. package/dist/course/lms-bulk-upload.service.d.ts +181 -0
  71. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -0
  72. package/dist/course/lms-bulk-upload.service.js +754 -0
  73. package/dist/course/lms-bulk-upload.service.js.map +1 -0
  74. package/dist/course/lms-setting.controller.d.ts +7 -1
  75. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  76. package/dist/course/lms-setting.controller.js +26 -3
  77. package/dist/course/lms-setting.controller.js.map +1 -1
  78. package/dist/enterprise/enterprise.controller.d.ts +20 -20
  79. package/dist/enterprise/enterprise.service.d.ts +20 -20
  80. package/dist/enterprise/training/training-admin.controller.d.ts +11 -11
  81. package/dist/enterprise/training/training-admin.service.d.ts +11 -11
  82. package/dist/enterprise/training/training-instructor.controller.d.ts +2 -2
  83. package/dist/enterprise/training/training-instructor.service.d.ts +2 -2
  84. package/dist/enterprise/training/training-student.controller.d.ts +1 -1
  85. package/dist/enterprise/training/training-student.service.d.ts +1 -1
  86. package/dist/enterprise/training/training-viewer.controller.d.ts +2 -2
  87. package/dist/evaluation/evaluation.controller.d.ts +8 -8
  88. package/dist/evaluation/evaluation.service.d.ts +8 -8
  89. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts +6 -0
  90. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.d.ts.map +1 -0
  91. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js +34 -0
  92. package/dist/lesson-xp-map/dto/create-lesson-xp-map.dto.js.map +1 -0
  93. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts +28 -0
  94. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.d.ts.map +1 -0
  95. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js +123 -0
  96. package/dist/lesson-xp-map/dto/create-lesson-xp-segment.dto.js.map +1 -0
  97. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts +4 -0
  98. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.d.ts.map +1 -0
  99. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js +22 -0
  100. package/dist/lesson-xp-map/dto/review-lesson-xp-map.dto.js.map +1 -0
  101. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts +10 -0
  102. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.d.ts.map +1 -0
  103. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js +52 -0
  104. package/dist/lesson-xp-map/dto/update-lesson-xp-map.dto.js.map +1 -0
  105. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts +15 -0
  106. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.d.ts.map +1 -0
  107. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js +86 -0
  108. package/dist/lesson-xp-map/dto/update-lesson-xp-segment.dto.js.map +1 -0
  109. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +26 -0
  110. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -0
  111. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +304 -0
  112. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -0
  113. package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts +87 -0
  114. package/dist/lesson-xp-map/lesson-xp-map.controller.d.ts.map +1 -0
  115. package/dist/lesson-xp-map/lesson-xp-map.controller.js +185 -0
  116. package/dist/lesson-xp-map/lesson-xp-map.controller.js.map +1 -0
  117. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts +3 -0
  118. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -0
  119. package/dist/lesson-xp-map/lesson-xp-map.module.js +34 -0
  120. package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -0
  121. package/dist/lesson-xp-map/lesson-xp-map.service.d.ts +84 -0
  122. package/dist/lesson-xp-map/lesson-xp-map.service.d.ts.map +1 -0
  123. package/dist/lesson-xp-map/lesson-xp-map.service.js +353 -0
  124. package/dist/lesson-xp-map/lesson-xp-map.service.js.map +1 -0
  125. package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts +10 -0
  126. package/dist/lesson-xp-map/lesson-xp-segment.controller.d.ts.map +1 -0
  127. package/dist/lesson-xp-map/lesson-xp-segment.controller.js +63 -0
  128. package/dist/lesson-xp-map/lesson-xp-segment.controller.js.map +1 -0
  129. package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts +27 -0
  130. package/dist/lesson-xp-map/lesson-xp-segment.service.d.ts.map +1 -0
  131. package/dist/lesson-xp-map/lesson-xp-segment.service.js +194 -0
  132. package/dist/lesson-xp-map/lesson-xp-segment.service.js.map +1 -0
  133. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -0
  134. package/dist/lms.module.d.ts.map +1 -1
  135. package/dist/lms.module.js +17 -2
  136. package/dist/lms.module.js.map +1 -1
  137. package/dist/platforma/dto/update-profile.dto.d.ts +17 -0
  138. package/dist/platforma/dto/update-profile.dto.d.ts.map +1 -0
  139. package/dist/platforma/dto/update-profile.dto.js +87 -0
  140. package/dist/platforma/dto/update-profile.dto.js.map +1 -0
  141. package/dist/platforma/platforma.controller.d.ts +94 -7
  142. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  143. package/dist/platforma/platforma.controller.js +85 -2
  144. package/dist/platforma/platforma.controller.js.map +1 -1
  145. package/dist/platforma/platforma.service.d.ts +27 -0
  146. package/dist/platforma/platforma.service.d.ts.map +1 -0
  147. package/dist/platforma/platforma.service.js +274 -0
  148. package/dist/platforma/platforma.service.js.map +1 -0
  149. package/dist/student-xp/student-xp.controller.d.ts +41 -0
  150. package/dist/student-xp/student-xp.controller.d.ts.map +1 -0
  151. package/dist/student-xp/student-xp.controller.js +114 -0
  152. package/dist/student-xp/student-xp.controller.js.map +1 -0
  153. package/dist/student-xp/student-xp.module.d.ts +3 -0
  154. package/dist/student-xp/student-xp.module.d.ts.map +1 -0
  155. package/dist/student-xp/student-xp.module.js +25 -0
  156. package/dist/student-xp/student-xp.module.js.map +1 -0
  157. package/dist/student-xp/student-xp.service.d.ts +65 -0
  158. package/dist/student-xp/student-xp.service.d.ts.map +1 -0
  159. package/dist/student-xp/student-xp.service.js +197 -0
  160. package/dist/student-xp/student-xp.service.js.map +1 -0
  161. package/dist/xp-catalog/dto/create-xp-area.dto.d.ts +12 -0
  162. package/dist/xp-catalog/dto/create-xp-area.dto.d.ts.map +1 -0
  163. package/dist/xp-catalog/dto/create-xp-area.dto.js +63 -0
  164. package/dist/xp-catalog/dto/create-xp-area.dto.js.map +1 -0
  165. package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts +11 -0
  166. package/dist/xp-catalog/dto/create-xp-learning-type.dto.d.ts.map +1 -0
  167. package/dist/xp-catalog/dto/create-xp-learning-type.dto.js +57 -0
  168. package/dist/xp-catalog/dto/create-xp-learning-type.dto.js.map +1 -0
  169. package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts +11 -0
  170. package/dist/xp-catalog/dto/create-xp-skill.dto.d.ts.map +1 -0
  171. package/dist/xp-catalog/dto/create-xp-skill.dto.js +57 -0
  172. package/dist/xp-catalog/dto/create-xp-skill.dto.js.map +1 -0
  173. package/dist/xp-catalog/dto/update-xp-area.dto.d.ts +12 -0
  174. package/dist/xp-catalog/dto/update-xp-area.dto.d.ts.map +1 -0
  175. package/dist/xp-catalog/dto/update-xp-area.dto.js +66 -0
  176. package/dist/xp-catalog/dto/update-xp-area.dto.js.map +1 -0
  177. package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts +11 -0
  178. package/dist/xp-catalog/dto/update-xp-learning-type.dto.d.ts.map +1 -0
  179. package/dist/xp-catalog/dto/update-xp-learning-type.dto.js +60 -0
  180. package/dist/xp-catalog/dto/update-xp-learning-type.dto.js.map +1 -0
  181. package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts +11 -0
  182. package/dist/xp-catalog/dto/update-xp-skill.dto.d.ts.map +1 -0
  183. package/dist/xp-catalog/dto/update-xp-skill.dto.js +60 -0
  184. package/dist/xp-catalog/dto/update-xp-skill.dto.js.map +1 -0
  185. package/dist/xp-catalog/xp-area.controller.d.ts +25 -0
  186. package/dist/xp-catalog/xp-area.controller.d.ts.map +1 -0
  187. package/dist/xp-catalog/xp-area.controller.js +105 -0
  188. package/dist/xp-catalog/xp-area.controller.js.map +1 -0
  189. package/dist/xp-catalog/xp-area.service.d.ts +35 -0
  190. package/dist/xp-catalog/xp-area.service.d.ts.map +1 -0
  191. package/dist/xp-catalog/xp-area.service.js +168 -0
  192. package/dist/xp-catalog/xp-area.service.js.map +1 -0
  193. package/dist/xp-catalog/xp-catalog.module.d.ts +3 -0
  194. package/dist/xp-catalog/xp-catalog.module.d.ts.map +1 -0
  195. package/dist/xp-catalog/xp-catalog.module.js +29 -0
  196. package/dist/xp-catalog/xp-catalog.module.js.map +1 -0
  197. package/dist/xp-catalog/xp-learning-type.controller.d.ts +20 -0
  198. package/dist/xp-catalog/xp-learning-type.controller.d.ts.map +1 -0
  199. package/dist/xp-catalog/xp-learning-type.controller.js +96 -0
  200. package/dist/xp-catalog/xp-learning-type.controller.js.map +1 -0
  201. package/dist/xp-catalog/xp-learning-type.service.d.ts +30 -0
  202. package/dist/xp-catalog/xp-learning-type.service.d.ts.map +1 -0
  203. package/dist/xp-catalog/xp-learning-type.service.js +146 -0
  204. package/dist/xp-catalog/xp-learning-type.service.js.map +1 -0
  205. package/dist/xp-catalog/xp-skill.controller.d.ts +26 -0
  206. package/dist/xp-catalog/xp-skill.controller.d.ts.map +1 -0
  207. package/dist/xp-catalog/xp-skill.controller.js +113 -0
  208. package/dist/xp-catalog/xp-skill.controller.js.map +1 -0
  209. package/dist/xp-catalog/xp-skill.service.d.ts +37 -0
  210. package/dist/xp-catalog/xp-skill.service.d.ts.map +1 -0
  211. package/dist/xp-catalog/xp-skill.service.js +174 -0
  212. package/dist/xp-catalog/xp-skill.service.js.map +1 -0
  213. package/hedhog/data/menu.yaml +101 -0
  214. package/hedhog/data/role.yaml +8 -0
  215. package/hedhog/data/route.yaml +547 -0
  216. package/hedhog/data/setting_group.yaml +33 -0
  217. package/hedhog/data/xp_area.yaml +164 -0
  218. package/hedhog/data/xp_learning_type.yaml +131 -0
  219. package/hedhog/data/xp_skill.yaml +1834 -0
  220. package/hedhog/frontend/app/achievements/page.tsx.ejs +108 -118
  221. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +22 -34
  222. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +1453 -0
  223. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +21 -45
  224. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +40 -74
  225. package/hedhog/frontend/app/classes/page.tsx.ejs +56 -85
  226. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +3 -2
  227. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +48 -5
  228. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +4 -4
  229. package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +19 -2
  230. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +1170 -0
  231. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +16 -0
  232. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +2 -0
  233. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +623 -0
  234. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +1458 -0
  235. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +55 -2
  236. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +442 -104
  237. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +296 -49
  238. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +3 -0
  239. package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +1 -0
  240. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +101 -85
  241. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +21 -1
  242. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +3 -0
  243. package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +7 -1
  244. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +44 -0
  245. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +54 -0
  246. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +52 -0
  247. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-xp-overview.ts.ejs +76 -0
  248. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-xp-map.ts.ejs +128 -0
  249. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +30 -0
  250. package/hedhog/frontend/app/courses/[id]/structure/_utils/xp-color-config.ts.ejs +115 -0
  251. package/hedhog/frontend/app/courses/_components/CourseDeleteDialog.tsx.ejs +223 -0
  252. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +89 -0
  253. package/hedhog/frontend/app/courses/page.tsx.ejs +400 -230
  254. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -63
  255. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +53 -77
  256. package/hedhog/frontend/app/exams/page.tsx.ejs +54 -90
  257. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +23 -36
  258. package/hedhog/frontend/app/instructors/page.tsx.ejs +72 -81
  259. package/hedhog/frontend/app/paths/page.tsx.ejs +40 -68
  260. package/hedhog/frontend/app/training/page.tsx.ejs +40 -68
  261. package/hedhog/frontend/app/xp/areas/page.tsx.ejs +782 -0
  262. package/hedhog/frontend/app/xp/learning-types/page.tsx.ejs +690 -0
  263. package/hedhog/frontend/app/xp/skills/page.tsx.ejs +811 -0
  264. package/hedhog/frontend/messages/en.json +386 -3
  265. package/hedhog/frontend/messages/pt.json +386 -3
  266. package/hedhog/table/lesson_xp_map.yaml +50 -0
  267. package/hedhog/table/lesson_xp_segment.yaml +40 -0
  268. package/hedhog/table/lesson_xp_segment_area.yaml +24 -0
  269. package/hedhog/table/lesson_xp_segment_learning_type.yaml +24 -0
  270. package/hedhog/table/lesson_xp_segment_skill.yaml +24 -0
  271. package/hedhog/table/lms_bulk_upload_item.yaml +44 -0
  272. package/hedhog/table/lms_bulk_upload_session.yaml +42 -0
  273. package/hedhog/table/student_area_xp.yaml +30 -0
  274. package/hedhog/table/student_learning_type_xp.yaml +30 -0
  275. package/hedhog/table/student_skill_xp.yaml +30 -0
  276. package/hedhog/table/student_xp_event.yaml +34 -0
  277. package/hedhog/table/xp_area.yaml +39 -0
  278. package/hedhog/table/xp_learning_type.yaml +34 -0
  279. package/hedhog/table/xp_skill.yaml +39 -0
  280. package/package.json +9 -8
  281. package/src/bitcode-wallet/bitcode-wallet.service.ts +113 -0
  282. package/src/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.ts +32 -0
  283. package/src/course/course-audio-transcription.service.ts +58 -21
  284. package/src/course/course-lesson.controller.ts +6 -1
  285. package/src/course/course-structure.controller.ts +10 -0
  286. package/src/course/course-structure.service.ts +174 -1
  287. package/src/course/course-video-conversion.service.ts +113 -75
  288. package/src/course/course.controller.ts +22 -3
  289. package/src/course/course.module.ts +15 -3
  290. package/src/course/course.service.ts +847 -30
  291. package/src/course/dto/cleanup-course-storage.dto.ts +22 -0
  292. package/src/course/dto/cleanup-upload-history.dto.ts +26 -0
  293. package/src/course/dto/create-course-bulk-job.dto.ts +6 -0
  294. package/src/course/lms-bulk-upload-automation.service.ts +560 -0
  295. package/src/course/lms-bulk-upload-infra.service.ts +327 -0
  296. package/src/course/lms-bulk-upload.constants.ts +5 -0
  297. package/src/course/lms-bulk-upload.controller.ts +103 -0
  298. package/src/course/lms-bulk-upload.service.ts +1029 -0
  299. package/src/course/lms-setting.controller.ts +28 -1
  300. package/src/lesson-xp-map/dto/create-lesson-xp-map.dto.ts +17 -0
  301. package/src/lesson-xp-map/dto/create-lesson-xp-segment.dto.ts +102 -0
  302. package/src/lesson-xp-map/dto/review-lesson-xp-map.dto.ts +7 -0
  303. package/src/lesson-xp-map/dto/update-lesson-xp-map.dto.ts +36 -0
  304. package/src/lesson-xp-map/dto/update-lesson-xp-segment.dto.ts +78 -0
  305. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +396 -0
  306. package/src/lesson-xp-map/lesson-xp-map.controller.ts +116 -0
  307. package/src/lesson-xp-map/lesson-xp-map.module.ts +21 -0
  308. package/src/lesson-xp-map/lesson-xp-map.service.ts +442 -0
  309. package/src/lesson-xp-map/lesson-xp-segment.controller.ts +36 -0
  310. package/src/lesson-xp-map/lesson-xp-segment.service.ts +229 -0
  311. package/src/lms.module.ts +17 -2
  312. package/src/platforma/dto/update-profile.dto.ts +59 -0
  313. package/src/platforma/platforma.controller.ts +57 -2
  314. package/src/platforma/platforma.service.ts +268 -0
  315. package/src/student-xp/student-xp.controller.ts +76 -0
  316. package/src/student-xp/student-xp.module.ts +12 -0
  317. package/src/student-xp/student-xp.service.ts +236 -0
  318. package/src/xp-catalog/dto/create-xp-area.dto.ts +40 -0
  319. package/src/xp-catalog/dto/create-xp-learning-type.dto.ts +35 -0
  320. package/src/xp-catalog/dto/create-xp-skill.dto.ts +35 -0
  321. package/src/xp-catalog/dto/update-xp-area.dto.ts +43 -0
  322. package/src/xp-catalog/dto/update-xp-learning-type.dto.ts +38 -0
  323. package/src/xp-catalog/dto/update-xp-skill.dto.ts +38 -0
  324. package/src/xp-catalog/xp-area.controller.ts +64 -0
  325. package/src/xp-catalog/xp-area.service.ts +196 -0
  326. package/src/xp-catalog/xp-catalog.module.ts +16 -0
  327. package/src/xp-catalog/xp-learning-type.controller.ts +59 -0
  328. package/src/xp-catalog/xp-learning-type.service.ts +170 -0
  329. package/src/xp-catalog/xp-skill.controller.ts +71 -0
  330. package/src/xp-catalog/xp-skill.service.ts +205 -0
@@ -22,18 +22,154 @@ var __rest = (this && this.__rest) || function (s, e) {
22
22
  }
23
23
  return t;
24
24
  };
25
+ var CourseService_1;
25
26
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.CourseService = void 0;
27
+ exports.CourseService = exports.LMS_COURSE_STORAGE_CLEANUP_JOB = exports.LMS_COURSE_DELETE_JOB = void 0;
27
28
  const api_prisma_1 = require("@hed-hog/api-prisma");
28
29
  const core_1 = require("@hed-hog/core");
30
+ const queue_1 = require("@hed-hog/queue");
29
31
  const common_1 = require("@nestjs/common");
30
32
  const course_operations_integration_service_1 = require("./course-operations-integration.service");
31
- let CourseService = class CourseService {
32
- constructor(prisma, fileService, integrationApi, operationsIntegration) {
33
+ const cleanup_course_storage_dto_1 = require("./dto/cleanup-course-storage.dto");
34
+ exports.LMS_COURSE_DELETE_JOB = 'lms.course.delete';
35
+ exports.LMS_COURSE_STORAGE_CLEANUP_JOB = 'lms.course.storage.cleanup';
36
+ let CourseService = CourseService_1 = class CourseService {
37
+ constructor(prisma, fileService, integrationApi, queueJobService, queueRegistry, notificationService, operationsIntegration) {
33
38
  this.prisma = prisma;
34
39
  this.fileService = fileService;
35
40
  this.integrationApi = integrationApi;
41
+ this.queueJobService = queueJobService;
42
+ this.queueRegistry = queueRegistry;
43
+ this.notificationService = notificationService;
36
44
  this.operationsIntegration = operationsIntegration;
45
+ this.logger = new common_1.Logger(CourseService_1.name);
46
+ }
47
+ onModuleInit() {
48
+ this.queueRegistry.register(exports.LMS_COURSE_DELETE_JOB, this);
49
+ this.queueRegistry.register(exports.LMS_COURSE_STORAGE_CLEANUP_JOB, this);
50
+ this.logger.log(`Registered handler for "${exports.LMS_COURSE_DELETE_JOB}"`);
51
+ this.logger.log(`Registered handler for "${exports.LMS_COURSE_STORAGE_CLEANUP_JOB}"`);
52
+ }
53
+ async handle(job) {
54
+ if (job.type === exports.LMS_COURSE_STORAGE_CLEANUP_JOB) {
55
+ return this.handleStorageCleanupJob(job);
56
+ }
57
+ if (job.type === exports.LMS_COURSE_DELETE_JOB) {
58
+ return this.handleCourseDeleteJob(job);
59
+ }
60
+ throw new common_1.BadRequestException(`Unsupported job type: ${job.type}`);
61
+ }
62
+ async handleCourseDeleteJob(job) {
63
+ const { courseId, notificationId, notificationUserId } = job.payload;
64
+ if (!courseId) {
65
+ throw new common_1.BadRequestException('Missing courseId in job payload');
66
+ }
67
+ const notify = async (progress, body, success) => {
68
+ if (!notificationId || !notificationUserId)
69
+ return;
70
+ await this.notificationService
71
+ .updateProgress(notificationUserId, notificationId, Object.assign({ progress,
72
+ body }, (success !== undefined ? { success } : {})))
73
+ .catch(() => undefined);
74
+ };
75
+ try {
76
+ await notify(5, 'Preparando exclusão completa do curso...');
77
+ const course = await this.prisma.course.findUnique({
78
+ where: { id: courseId },
79
+ select: { id: true, status: true, title: true, slug: true },
80
+ });
81
+ if (!course) {
82
+ throw new common_1.NotFoundException('Course not found');
83
+ }
84
+ if (course.status !== 'archived') {
85
+ throw new common_1.BadRequestException('ONLY_ARCHIVED_COURSE_CAN_BE_DELETED');
86
+ }
87
+ const impact = await this.getCourseDeletionImpact(courseId);
88
+ await notify(20, `Removendo ${impact.deletionImpact.fileCount} arquivo(s) relacionados ao curso...`);
89
+ if (impact.deletionImpact.fileIds.length > 0) {
90
+ await this.fileService
91
+ .delete('pt', { ids: impact.deletionImpact.fileIds })
92
+ .catch(() => undefined);
93
+ }
94
+ await notify(75, 'Removendo registros do curso e dependências...');
95
+ await this.prisma.course.delete({ where: { id: courseId } });
96
+ await notify(90, 'Finalizando exclusão e publicando evento...');
97
+ await this.integrationApi
98
+ .publishEvent({
99
+ eventName: 'lms.course.deleted',
100
+ sourceModule: 'lms',
101
+ aggregateType: 'course',
102
+ aggregateId: String(courseId),
103
+ payload: { id: courseId },
104
+ })
105
+ .catch(() => null);
106
+ await notify(100, 'Curso e todos os arquivos relacionados foram excluídos.', true);
107
+ return { success: true, courseId };
108
+ }
109
+ catch (error) {
110
+ const message = error instanceof Error && error.message
111
+ ? error.message
112
+ : 'Falha ao excluir o curso.';
113
+ await notify(100, message, false);
114
+ throw error;
115
+ }
116
+ }
117
+ async handleStorageCleanupJob(job) {
118
+ const { courseId, category, notificationId, notificationUserId } = job.payload;
119
+ if (!courseId) {
120
+ throw new common_1.BadRequestException('Missing courseId in job payload');
121
+ }
122
+ if (!this.isSupportedStorageCleanupCategory(category)) {
123
+ throw new common_1.BadRequestException('Invalid storage category');
124
+ }
125
+ const notify = async (progress, body, success) => {
126
+ if (!notificationId || !notificationUserId)
127
+ return;
128
+ await this.notificationService
129
+ .updateProgress(notificationUserId, notificationId, Object.assign({ progress,
130
+ body }, (success !== undefined ? { success } : {})))
131
+ .catch(() => undefined);
132
+ };
133
+ try {
134
+ const categoryLabel = this.getStorageCategoryLabel(category);
135
+ await notify(5, `Preparando limpeza da categoria ${categoryLabel}...`);
136
+ const impact = await this.getCourseStorageCategoryImpact(courseId, category);
137
+ if (impact.fileCount === 0) {
138
+ await notify(100, `Nenhum arquivo encontrado em ${categoryLabel}.`, true);
139
+ return {
140
+ success: true,
141
+ status: 'empty',
142
+ courseId,
143
+ category,
144
+ cleanupImpact: impact,
145
+ };
146
+ }
147
+ await notify(25, `Removendo vínculos da categoria ${categoryLabel} (${impact.fileCount} arquivo(s))...`);
148
+ await this.deleteCourseStorageCategoryLinks(courseId, category);
149
+ await notify(65, `Excluindo ${impact.fileCount} arquivo(s) da categoria ${categoryLabel}...`);
150
+ if (impact.fileIds.length > 0) {
151
+ await this.fileService.delete('pt', { ids: impact.fileIds }).catch(() => undefined);
152
+ }
153
+ await notify(100, `Categoria ${categoryLabel} limpa com sucesso (${this.formatBytes(impact.totalBytes)} liberados).`, true);
154
+ return {
155
+ success: true,
156
+ status: 'processed',
157
+ courseId,
158
+ category,
159
+ cleanupImpact: {
160
+ fileCount: impact.fileCount,
161
+ totalBytes: impact.totalBytes,
162
+ formattedSize: this.formatBytes(impact.totalBytes),
163
+ },
164
+ };
165
+ }
166
+ catch (error) {
167
+ const message = error instanceof Error && error.message
168
+ ? error.message
169
+ : 'Falha ao limpar a categoria de armazenamento.';
170
+ await notify(100, message, false);
171
+ throw error;
172
+ }
37
173
  }
38
174
  normalizeLevel(value) {
39
175
  if (!value)
@@ -216,7 +352,7 @@ let CourseService = class CourseService {
216
352
  };
217
353
  }
218
354
  async getById(id) {
219
- var _a;
355
+ var _a, _b;
220
356
  const [c, lessonInstructors] = await Promise.all([
221
357
  this.prisma.course.findUnique({
222
358
  where: { id },
@@ -243,7 +379,16 @@ let CourseService = class CourseService {
243
379
  },
244
380
  },
245
381
  file: {
246
- select: { id: true, filename: true },
382
+ select: {
383
+ id: true,
384
+ filename: true,
385
+ size: true,
386
+ file_mimetype: {
387
+ select: {
388
+ name: true,
389
+ },
390
+ },
391
+ },
247
392
  },
248
393
  },
249
394
  orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
@@ -334,13 +479,21 @@ let CourseService = class CourseService {
334
479
  progress: average,
335
480
  };
336
481
  });
337
- return this.mapCourse(c, {
482
+ const mapped = this.mapCourse(c, {
338
483
  lessonCount,
339
484
  sessionCount,
340
485
  certificatesIssued,
341
486
  averageCompletion: (_a = avgProgress._avg.progress_percent) !== null && _a !== void 0 ? _a : 0,
342
487
  progressByModule,
343
488
  }, extrasById.get(id), projectLinksById.get(id), lessonInstructors);
489
+ const deletionImpact = await this.getCourseDeletionImpact(id);
490
+ return Object.assign(Object.assign({}, mapped), { deletionImpact: {
491
+ fileCount: deletionImpact.deletionImpact.fileCount,
492
+ totalBytes: deletionImpact.deletionImpact.totalBytes,
493
+ formattedSize: this.formatBytes(deletionImpact.deletionImpact.totalBytes),
494
+ countsBySource: deletionImpact.deletionImpact.countsBySource,
495
+ enrollmentCount: (_b = mapped.enrollmentCount) !== null && _b !== void 0 ? _b : 0,
496
+ } });
344
497
  }
345
498
  async create(dto) {
346
499
  var _a, _b, _c, _d, _e;
@@ -548,10 +701,11 @@ let CourseService = class CourseService {
548
701
  }).catch(() => null);
549
702
  return updateResult;
550
703
  }
551
- async remove(id) {
704
+ async remove(id, userId) {
705
+ var _a;
552
706
  const course = await this.prisma.course.findUnique({
553
707
  where: { id },
554
- select: { id: true, status: true },
708
+ select: { id: true, status: true, title: true, slug: true },
555
709
  });
556
710
  if (!course) {
557
711
  throw new common_1.BadRequestException('Course not found');
@@ -559,62 +713,362 @@ let CourseService = class CourseService {
559
713
  if (course.status !== 'archived') {
560
714
  throw new common_1.BadRequestException('ONLY_ARCHIVED_COURSE_CAN_BE_DELETED');
561
715
  }
562
- const fileIds = await this.getCourseRelatedFileIds(id);
563
- if (fileIds.length > 0) {
564
- await this.fileService.delete('pt', { ids: fileIds }).catch(() => undefined);
716
+ if (!userId) {
717
+ throw new common_1.BadRequestException('Authenticated user is required');
565
718
  }
566
- await this.prisma.course.delete({ where: { id } });
567
- await this.integrationApi.publishEvent({
568
- eventName: 'lms.course.deleted',
719
+ const impact = await this.getCourseDeletionImpact(id);
720
+ const notification = await this.notificationService.create({
721
+ user_id: userId,
722
+ title: `Excluindo curso "${(_a = course.title) !== null && _a !== void 0 ? _a : course.slug}"`,
723
+ body: `Preparando remoção de ${impact.deletionImpact.fileCount} arquivo(s) (${this.formatBytes(impact.deletionImpact.totalBytes)}).`,
724
+ type: 'progress',
725
+ progress: 1,
726
+ started_at: new Date().toISOString(),
727
+ auto_remove: false,
728
+ action_type: 'url',
729
+ action_url: '/lms/courses',
730
+ action_data: {
731
+ source: 'lms-course-delete',
732
+ courseId: id,
733
+ },
734
+ });
735
+ const job = await this.queueJobService.enqueue({
736
+ type: exports.LMS_COURSE_DELETE_JOB,
737
+ queueName: exports.LMS_COURSE_DELETE_JOB,
738
+ payload: {
739
+ courseId: id,
740
+ notificationId: notification.id,
741
+ notificationUserId: userId,
742
+ },
569
743
  sourceModule: 'lms',
570
- aggregateType: 'course',
571
- aggregateId: String(id),
572
- payload: { id },
573
- }).catch(() => null);
574
- return { success: true };
744
+ sourceEntity: 'course',
745
+ sourceEntityId: String(id),
746
+ maxAttempts: 3,
747
+ }, userId);
748
+ return {
749
+ success: true,
750
+ status: 'queued',
751
+ queueJobId: job.id,
752
+ notificationId: notification.id,
753
+ deletionImpact: {
754
+ fileCount: impact.deletionImpact.fileCount,
755
+ totalBytes: impact.deletionImpact.totalBytes,
756
+ formattedSize: this.formatBytes(impact.deletionImpact.totalBytes),
757
+ },
758
+ };
575
759
  }
576
- async getCourseRelatedFileIds(courseId) {
760
+ async enqueueStorageCategoryCleanup(courseId, category, userId) {
761
+ if (!userId) {
762
+ throw new common_1.BadRequestException('Authenticated user is required');
763
+ }
764
+ if (!this.isSupportedStorageCleanupCategory(category)) {
765
+ throw new common_1.BadRequestException('Invalid storage category');
766
+ }
767
+ const course = await this.prisma.course.findUnique({
768
+ where: { id: courseId },
769
+ select: { id: true, title: true, slug: true },
770
+ });
771
+ if (!course) {
772
+ throw new common_1.NotFoundException('Course not found');
773
+ }
774
+ const categoryLabel = this.getStorageCategoryLabel(category);
775
+ const impact = await this.getCourseStorageCategoryImpact(courseId, category);
776
+ if (impact.fileCount === 0) {
777
+ return {
778
+ success: true,
779
+ status: 'empty',
780
+ cleanupImpact: {
781
+ fileCount: 0,
782
+ totalBytes: 0,
783
+ formattedSize: this.formatBytes(0),
784
+ },
785
+ };
786
+ }
787
+ const notification = await this.notificationService.create({
788
+ user_id: userId,
789
+ title: `Limpando armazenamento (${categoryLabel})`,
790
+ body: `Preparando remoção de ${impact.fileCount} arquivo(s) (${this.formatBytes(impact.totalBytes)}).`,
791
+ type: 'progress',
792
+ progress: 1,
793
+ started_at: new Date().toISOString(),
794
+ auto_remove: false,
795
+ action_type: 'url',
796
+ action_url: `/lms/courses/${courseId}`,
797
+ action_data: {
798
+ source: 'lms-course-storage-cleanup',
799
+ courseId,
800
+ category,
801
+ },
802
+ });
803
+ const job = await this.queueJobService.enqueue({
804
+ type: exports.LMS_COURSE_STORAGE_CLEANUP_JOB,
805
+ queueName: exports.LMS_COURSE_STORAGE_CLEANUP_JOB,
806
+ payload: {
807
+ courseId,
808
+ category,
809
+ notificationId: notification.id,
810
+ notificationUserId: userId,
811
+ },
812
+ sourceModule: 'lms',
813
+ sourceEntity: 'course',
814
+ sourceEntityId: String(courseId),
815
+ maxAttempts: 3,
816
+ }, userId);
817
+ return {
818
+ success: true,
819
+ status: 'queued',
820
+ queueJobId: job.id,
821
+ notificationId: notification.id,
822
+ category,
823
+ cleanupImpact: {
824
+ fileCount: impact.fileCount,
825
+ totalBytes: impact.totalBytes,
826
+ formattedSize: this.formatBytes(impact.totalBytes),
827
+ },
828
+ };
829
+ }
830
+ isSupportedStorageCleanupCategory(value) {
831
+ return (typeof value === 'string' &&
832
+ cleanup_course_storage_dto_1.COURSE_STORAGE_CLEANUP_CATEGORIES.includes(value));
833
+ }
834
+ getStorageCategoryLabel(category) {
835
+ const labels = {
836
+ video_original: 'Vídeos originais',
837
+ video_profile: 'Vídeos convertidos',
838
+ lesson_audio: 'Áudios',
839
+ extracted_image: 'Imagens extraídas',
840
+ student_download: 'Downloads do aluno',
841
+ supplementary_material: 'Materiais de apoio',
842
+ course_image: 'Imagens do curso',
843
+ course_file: 'Arquivos do curso',
844
+ other_lesson_file: 'Outros arquivos',
845
+ };
846
+ return labels[category];
847
+ }
848
+ async getCourseStorageCategoryImpact(courseId, category) {
849
+ var _a, _b;
850
+ const rows = await this.getCourseStorageRows(courseId);
851
+ const uniqueFiles = new Map();
852
+ for (const row of rows) {
853
+ const rowCategory = String(row.storage_category || 'other_lesson_file');
854
+ if (rowCategory !== category)
855
+ continue;
856
+ const fileId = Number((_a = row.file_id) !== null && _a !== void 0 ? _a : 0);
857
+ if (fileId <= 0)
858
+ continue;
859
+ if (!uniqueFiles.has(fileId)) {
860
+ uniqueFiles.set(fileId, Number((_b = row.file_size) !== null && _b !== void 0 ? _b : 0) || 0);
861
+ }
862
+ }
863
+ const fileIds = Array.from(uniqueFiles.keys());
864
+ const totalBytes = Array.from(uniqueFiles.values()).reduce((sum, size) => sum + size, 0);
865
+ return {
866
+ category,
867
+ fileIds,
868
+ fileCount: fileIds.length,
869
+ totalBytes,
870
+ };
871
+ }
872
+ async deleteCourseStorageCategoryLinks(courseId, category) {
873
+ if (category === 'course_image') {
874
+ await this.prisma.course_image.deleteMany({ where: { course_id: courseId } });
875
+ return;
876
+ }
877
+ if (category === 'course_file') {
878
+ await this.prisma.course_file.deleteMany({ where: { course_id: courseId } });
879
+ return;
880
+ }
881
+ if (category === 'extracted_image') {
882
+ await this.prisma.$executeRaw `
883
+ DELETE FROM course_lesson_video_frame clvf
884
+ USING course_lesson cl, course_module cm
885
+ WHERE clvf.course_lesson_id = cl.id
886
+ AND cl.course_module_id = cm.id
887
+ AND cm.course_id = ${courseId}
888
+ `;
889
+ return;
890
+ }
891
+ if (category === 'video_profile') {
892
+ await this.prisma.$executeRaw `
893
+ DELETE FROM course_lesson_file clf
894
+ USING course_lesson cl, course_module cm
895
+ WHERE clf.course_lesson_id = cl.id
896
+ AND cl.course_module_id = cm.id
897
+ AND cm.course_id = ${courseId}
898
+ AND clf.type LIKE 'video_profile:%'
899
+ `;
900
+ return;
901
+ }
902
+ if (category === 'other_lesson_file') {
903
+ await this.prisma.$executeRaw `
904
+ DELETE FROM course_lesson_file clf
905
+ USING course_lesson cl, course_module cm
906
+ WHERE clf.course_lesson_id = cl.id
907
+ AND cl.course_module_id = cm.id
908
+ AND cm.course_id = ${courseId}
909
+ AND (
910
+ clf.type IS NULL
911
+ OR (
912
+ clf.type <> 'video_original'
913
+ AND clf.type <> 'lesson_audio'
914
+ AND clf.type <> 'student_download'
915
+ AND clf.type <> 'supplementary_material'
916
+ AND clf.type NOT LIKE 'video_profile:%'
917
+ )
918
+ )
919
+ `;
920
+ return;
921
+ }
922
+ await this.prisma.course_lesson_file.deleteMany({
923
+ where: {
924
+ course_lesson: { course_module: { course_id: courseId } },
925
+ type: category,
926
+ },
927
+ });
928
+ }
929
+ async getCourseStorageRows(courseId) {
930
+ return this.prisma.$queryRaw `
931
+ SELECT files.file_id, files.file_size, files.storage_category
932
+ FROM (
933
+ SELECT ci.file_id, f.size AS file_size, 'course_image' AS storage_category
934
+ FROM course_image ci
935
+ INNER JOIN file f ON f.id = ci.file_id
936
+ WHERE ci.course_id = ${courseId}
937
+
938
+ UNION ALL
939
+
940
+ SELECT cf.file_id, f.size AS file_size, 'course_file' AS storage_category
941
+ FROM course_file cf
942
+ INNER JOIN file f ON f.id = cf.file_id
943
+ WHERE cf.course_id = ${courseId}
944
+
945
+ UNION ALL
946
+
947
+ SELECT clf.file_id,
948
+ f.size AS file_size,
949
+ CASE
950
+ WHEN clf.type = 'video_original' THEN 'video_original'
951
+ WHEN clf.type LIKE 'video_profile:%' THEN 'video_profile'
952
+ WHEN clf.type = 'lesson_audio' THEN 'lesson_audio'
953
+ WHEN clf.type = 'student_download' THEN 'student_download'
954
+ WHEN clf.type = 'supplementary_material' THEN 'supplementary_material'
955
+ ELSE 'other_lesson_file'
956
+ END AS storage_category
957
+ FROM course_lesson_file clf
958
+ INNER JOIN file f ON f.id = clf.file_id
959
+ INNER JOIN course_lesson cl ON cl.id = clf.course_lesson_id
960
+ INNER JOIN course_module cm ON cm.id = cl.course_module_id
961
+ WHERE cm.course_id = ${courseId}
962
+
963
+ UNION ALL
964
+
965
+ SELECT clvf.file_id, f.size AS file_size, 'extracted_image' AS storage_category
966
+ FROM course_lesson_video_frame clvf
967
+ INNER JOIN file f ON f.id = clvf.file_id
968
+ INNER JOIN course_lesson cl ON cl.id = clvf.course_lesson_id
969
+ INNER JOIN course_module cm ON cm.id = cl.course_module_id
970
+ WHERE cm.course_id = ${courseId}
971
+ ) AS files
972
+ WHERE files.file_id IS NOT NULL
973
+ `;
974
+ }
975
+ async getCourseDeletionImpact(courseId) {
577
976
  const rows = await this.prisma.$queryRaw `
578
- SELECT DISTINCT files.file_id
977
+ SELECT DISTINCT files.file_id, files.file_size, files.source_type
579
978
  FROM (
580
- SELECT ci.file_id
979
+ SELECT ci.file_id, f.size AS file_size, 'course_image' AS source_type
581
980
  FROM course_image ci
981
+ INNER JOIN file f ON f.id = ci.file_id
582
982
  WHERE ci.course_id = ${courseId}
583
983
 
584
984
  UNION ALL
585
985
 
586
- SELECT cf.file_id
986
+ SELECT cf.file_id, f.size AS file_size, 'course_file' AS source_type
587
987
  FROM course_file cf
988
+ INNER JOIN file f ON f.id = cf.file_id
588
989
  WHERE cf.course_id = ${courseId}
589
990
 
590
991
  UNION ALL
591
992
 
592
- SELECT clf.file_id
993
+ SELECT clf.file_id, f.size AS file_size, 'course_lesson_file' AS source_type
593
994
  FROM course_lesson_file clf
995
+ INNER JOIN file f ON f.id = clf.file_id
594
996
  INNER JOIN course_lesson cl ON cl.id = clf.course_lesson_id
595
997
  INNER JOIN course_module cm ON cm.id = cl.course_module_id
596
998
  WHERE cm.course_id = ${courseId}
597
999
 
598
1000
  UNION ALL
599
1001
 
600
- SELECT clvf.file_id
1002
+ SELECT clvf.file_id, f.size AS file_size, 'course_lesson_video_frame' AS source_type
601
1003
  FROM course_lesson_video_frame clvf
1004
+ INNER JOIN file f ON f.id = clvf.file_id
602
1005
  INNER JOIN course_lesson cl ON cl.id = clvf.course_lesson_id
603
1006
  INNER JOIN course_module cm ON cm.id = cl.course_module_id
604
1007
  WHERE cm.course_id = ${courseId}
605
1008
 
606
1009
  UNION ALL
607
1010
 
608
- SELECT cgm.file_id
1011
+ SELECT cgm.file_id, f.size AS file_size, 'course_class_group_material' AS source_type
609
1012
  FROM course_class_group_material cgm
1013
+ INNER JOIN file f ON f.id = cgm.file_id
610
1014
  INNER JOIN course_class_group ccg ON ccg.id = cgm.course_class_group_id
611
1015
  WHERE ccg.course_id = ${courseId}
1016
+
1017
+ UNION ALL
1018
+
1019
+ SELECT cln.frame_file_id AS file_id, f.size AS file_size, 'course_lesson_note_frame' AS source_type
1020
+ FROM course_lesson_note cln
1021
+ INNER JOIN file f ON f.id = cln.frame_file_id
1022
+ INNER JOIN course_lesson cl ON cl.id = cln.course_lesson_id
1023
+ INNER JOIN course_module cm ON cm.id = cl.course_module_id
1024
+ WHERE cm.course_id = ${courseId}
612
1025
  ) AS files
613
1026
  WHERE files.file_id IS NOT NULL
614
1027
  `;
615
- return rows
616
- .map((row) => Number(row.file_id))
617
- .filter((id) => Number.isInteger(id) && id > 0);
1028
+ const uniqueRows = Array.from(rows.reduce((map, row) => {
1029
+ var _a, _b;
1030
+ const fileId = Number((_a = row.file_id) !== null && _a !== void 0 ? _a : 0);
1031
+ if (fileId > 0 && !map.has(fileId)) {
1032
+ map.set(fileId, {
1033
+ fileId,
1034
+ size: Number((_b = row.file_size) !== null && _b !== void 0 ? _b : 0) || 0,
1035
+ sourceType: row.source_type,
1036
+ });
1037
+ }
1038
+ return map;
1039
+ }, new Map()).values());
1040
+ const countsBySource = rows.reduce((acc, row) => {
1041
+ var _a;
1042
+ const key = String(row.source_type || 'unknown');
1043
+ acc[key] = ((_a = acc[key]) !== null && _a !== void 0 ? _a : 0) + 1;
1044
+ return acc;
1045
+ }, {});
1046
+ return {
1047
+ deletionImpact: {
1048
+ fileIds: uniqueRows.map((row) => row.fileId),
1049
+ fileCount: uniqueRows.length,
1050
+ totalBytes: uniqueRows.reduce((sum, row) => sum + row.size, 0),
1051
+ countsBySource,
1052
+ },
1053
+ };
1054
+ }
1055
+ formatBytes(bytes) {
1056
+ if (!Number.isFinite(bytes) || bytes <= 0) {
1057
+ return '0 B';
1058
+ }
1059
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
1060
+ let value = bytes;
1061
+ let unitIndex = 0;
1062
+ while (value >= 1024 && unitIndex < units.length - 1) {
1063
+ value /= 1024;
1064
+ unitIndex += 1;
1065
+ }
1066
+ const digits = unitIndex === 0 ? 0 : value >= 10 ? 1 : 2;
1067
+ return `${value.toFixed(digits)} ${units[unitIndex]}`;
1068
+ }
1069
+ async getCourseRelatedFileIds(courseId) {
1070
+ const impact = await this.getCourseDeletionImpact(courseId);
1071
+ return impact.deletionImpact.fileIds.filter((id) => Number.isInteger(id) && id > 0);
618
1072
  }
619
1073
  mapCourse(c, metrics, extras, project, lessonInstructors) {
620
1074
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38;
@@ -925,14 +1379,161 @@ let CourseService = class CourseService {
925
1379
  status: row.video_resolution_profile.status,
926
1380
  })));
927
1381
  }
1382
+ async getCourseContentOverview(courseId) {
1383
+ var _a, _b, _c;
1384
+ const [moduleCount, lessons, transcriptionRows, xpMapRows, extractedImageCount, resourceFileCount, storageRows,] = await Promise.all([
1385
+ this.prisma.course_module.count({ where: { course_id: courseId } }),
1386
+ this.prisma.course_lesson.findMany({
1387
+ where: { course_module: { course_id: courseId } },
1388
+ select: { id: true, type: true, content: true, published: true },
1389
+ }),
1390
+ this.prisma.course_lesson_transcription_segment.findMany({
1391
+ where: { course_lesson: { course_module: { course_id: courseId } } },
1392
+ select: { course_lesson_id: true },
1393
+ distinct: ['course_lesson_id'],
1394
+ }),
1395
+ this.prisma.lesson_xp_map.findMany({
1396
+ where: { course_lesson: { course_module: { course_id: courseId } } },
1397
+ select: { course_lesson_id: true },
1398
+ }),
1399
+ this.prisma.course_lesson_video_frame.count({
1400
+ where: { course_lesson: { course_module: { course_id: courseId } } },
1401
+ }),
1402
+ this.prisma.course_lesson_file.count({
1403
+ where: {
1404
+ course_lesson: { course_module: { course_id: courseId } },
1405
+ type: { in: ['student_download', 'supplementary_material'] },
1406
+ },
1407
+ }),
1408
+ this.getCourseStorageRows(courseId),
1409
+ ]);
1410
+ const transcriptionLessonIds = new Set(transcriptionRows.map((r) => r.course_lesson_id));
1411
+ const xpLessonIds = new Set(xpMapRows.map((r) => r.course_lesson_id));
1412
+ const lessonsByType = { video: 0, questao: 0, post: 0, exercicio: 0 };
1413
+ let publishedLessonCount = 0;
1414
+ let videoWithTranscription = 0;
1415
+ let videoWithXp = 0;
1416
+ const categoryOrder = [
1417
+ 'video_original',
1418
+ 'video_profile',
1419
+ 'lesson_audio',
1420
+ 'extracted_image',
1421
+ 'student_download',
1422
+ 'supplementary_material',
1423
+ 'course_image',
1424
+ 'course_file',
1425
+ 'other_lesson_file',
1426
+ ];
1427
+ const uniqueStorageFiles = new Map();
1428
+ const uniqueStorageCategoryEntries = new Set();
1429
+ const storageCategoryMap = new Map();
1430
+ for (const row of storageRows) {
1431
+ const fileId = Number((_a = row.file_id) !== null && _a !== void 0 ? _a : 0);
1432
+ if (fileId <= 0)
1433
+ continue;
1434
+ const size = Number((_b = row.file_size) !== null && _b !== void 0 ? _b : 0) || 0;
1435
+ const category = String(row.storage_category || 'other_lesson_file');
1436
+ if (!uniqueStorageFiles.has(fileId)) {
1437
+ uniqueStorageFiles.set(fileId, size);
1438
+ }
1439
+ const categoryEntryKey = `${category}:${fileId}`;
1440
+ if (uniqueStorageCategoryEntries.has(categoryEntryKey)) {
1441
+ continue;
1442
+ }
1443
+ uniqueStorageCategoryEntries.add(categoryEntryKey);
1444
+ const current = (_c = storageCategoryMap.get(category)) !== null && _c !== void 0 ? _c : {
1445
+ key: category,
1446
+ fileCount: 0,
1447
+ totalBytes: 0,
1448
+ };
1449
+ current.fileCount += 1;
1450
+ current.totalBytes += size;
1451
+ storageCategoryMap.set(category, current);
1452
+ }
1453
+ const storageCategories = Array.from(storageCategoryMap.values()).sort((a, b) => {
1454
+ const orderA = categoryOrder.indexOf(a.key);
1455
+ const orderB = categoryOrder.indexOf(b.key);
1456
+ if (orderA !== -1 || orderB !== -1) {
1457
+ return (orderA === -1 ? Number.MAX_SAFE_INTEGER : orderA) -
1458
+ (orderB === -1 ? Number.MAX_SAFE_INTEGER : orderB);
1459
+ }
1460
+ return b.totalBytes - a.totalBytes;
1461
+ });
1462
+ for (const lesson of lessons) {
1463
+ if (lesson.published)
1464
+ publishedLessonCount++;
1465
+ let sourceType;
1466
+ try {
1467
+ const parsed = lesson.content
1468
+ ? JSON.parse(lesson.content)
1469
+ : null;
1470
+ sourceType =
1471
+ typeof (parsed === null || parsed === void 0 ? void 0 : parsed.sourceType) === 'string'
1472
+ ? parsed.sourceType
1473
+ : undefined;
1474
+ }
1475
+ catch (_d) {
1476
+ // ignore malformed content
1477
+ }
1478
+ let uiType;
1479
+ if (sourceType === 'video' ||
1480
+ sourceType === 'questao' ||
1481
+ sourceType === 'post' ||
1482
+ sourceType === 'exercicio') {
1483
+ uiType = sourceType;
1484
+ }
1485
+ else if (lesson.type === 'video') {
1486
+ uiType = 'video';
1487
+ }
1488
+ else if (lesson.type === 'quiz') {
1489
+ uiType = 'questao';
1490
+ }
1491
+ else {
1492
+ uiType = 'post';
1493
+ }
1494
+ lessonsByType[uiType]++;
1495
+ if (uiType === 'video') {
1496
+ if (transcriptionLessonIds.has(lesson.id))
1497
+ videoWithTranscription++;
1498
+ if (xpLessonIds.has(lesson.id))
1499
+ videoWithXp++;
1500
+ }
1501
+ }
1502
+ return {
1503
+ structure: {
1504
+ moduleCount,
1505
+ lessonCount: lessons.length,
1506
+ publishedLessonCount,
1507
+ lessonsByType,
1508
+ },
1509
+ videos: {
1510
+ lessonCount: lessonsByType.video,
1511
+ withTranscription: videoWithTranscription,
1512
+ withXp: videoWithXp,
1513
+ },
1514
+ media: { extractedImageCount },
1515
+ resources: { fileCount: resourceFileCount },
1516
+ storage: {
1517
+ totalBytes: Array.from(uniqueStorageFiles.values()).reduce((sum, size) => sum + size, 0),
1518
+ totalFileCount: uniqueStorageFiles.size,
1519
+ categories: storageCategories,
1520
+ },
1521
+ };
1522
+ }
928
1523
  };
929
1524
  exports.CourseService = CourseService;
930
- exports.CourseService = CourseService = __decorate([
1525
+ exports.CourseService = CourseService = CourseService_1 = __decorate([
931
1526
  (0, common_1.Injectable)(),
932
1527
  __param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.IntegrationDeveloperApiService))),
1528
+ __param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => queue_1.QueueJobService))),
1529
+ __param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => queue_1.QueueHandlerRegistry))),
1530
+ __param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.NotificationService))),
933
1531
  __metadata("design:paramtypes", [api_prisma_1.PrismaService,
934
1532
  core_1.FileService,
935
1533
  core_1.IntegrationDeveloperApiService,
1534
+ queue_1.QueueJobService,
1535
+ queue_1.QueueHandlerRegistry,
1536
+ core_1.NotificationService,
936
1537
  course_operations_integration_service_1.CourseOperationsIntegrationService])
937
1538
  ], CourseService);
938
1539
  //# sourceMappingURL=course.service.js.map