@hed-hog/lms 0.0.361 → 0.0.364

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) 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 +2 -0
  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 +116 -0
  67. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  68. package/dist/course/lms-bulk-upload.controller.js +71 -2
  69. package/dist/course/lms-bulk-upload.controller.js.map +1 -1
  70. package/dist/course/lms-bulk-upload.service.d.ts +142 -3
  71. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  72. package/dist/course/lms-bulk-upload.service.js +606 -21
  73. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  74. package/dist/course/lms-setting.controller.d.ts +4 -1
  75. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  76. package/dist/course/lms-setting.controller.js +21 -6
  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 +88 -1
  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/route.yaml +512 -0
  215. package/hedhog/data/setting_group.yaml +1 -1
  216. package/hedhog/data/xp_area.yaml +164 -0
  217. package/hedhog/data/xp_learning_type.yaml +131 -0
  218. package/hedhog/data/xp_skill.yaml +1834 -0
  219. package/hedhog/frontend/app/achievements/page.tsx.ejs +108 -118
  220. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +22 -34
  221. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +1453 -0
  222. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +21 -45
  223. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +40 -74
  224. package/hedhog/frontend/app/classes/page.tsx.ejs +56 -85
  225. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +3 -2
  226. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +48 -5
  227. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +4 -4
  228. package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +19 -2
  229. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +1170 -0
  230. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +16 -0
  231. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +2 -0
  232. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +623 -0
  233. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +1458 -0
  234. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +55 -2
  235. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +442 -104
  236. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +296 -49
  237. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +3 -0
  238. package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +1 -0
  239. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +101 -85
  240. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +21 -1
  241. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +3 -0
  242. package/hedhog/frontend/app/courses/[id]/structure/_components/use-tree-display-settings.ts.ejs +7 -1
  243. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +44 -0
  244. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +54 -0
  245. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +52 -0
  246. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-xp-overview.ts.ejs +76 -0
  247. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-xp-map.ts.ejs +128 -0
  248. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +30 -0
  249. package/hedhog/frontend/app/courses/[id]/structure/_utils/xp-color-config.ts.ejs +115 -0
  250. package/hedhog/frontend/app/courses/_components/CourseDeleteDialog.tsx.ejs +223 -0
  251. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +89 -0
  252. package/hedhog/frontend/app/courses/page.tsx.ejs +400 -230
  253. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -63
  254. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +53 -77
  255. package/hedhog/frontend/app/exams/page.tsx.ejs +54 -90
  256. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +23 -36
  257. package/hedhog/frontend/app/instructors/page.tsx.ejs +72 -81
  258. package/hedhog/frontend/app/paths/page.tsx.ejs +40 -68
  259. package/hedhog/frontend/app/training/page.tsx.ejs +40 -68
  260. package/hedhog/frontend/app/xp/areas/page.tsx.ejs +782 -0
  261. package/hedhog/frontend/app/xp/learning-types/page.tsx.ejs +690 -0
  262. package/hedhog/frontend/app/xp/skills/page.tsx.ejs +811 -0
  263. package/hedhog/frontend/messages/en.json +386 -3
  264. package/hedhog/frontend/messages/pt.json +386 -3
  265. package/hedhog/table/lesson_xp_map.yaml +50 -0
  266. package/hedhog/table/lesson_xp_segment.yaml +40 -0
  267. package/hedhog/table/lesson_xp_segment_area.yaml +24 -0
  268. package/hedhog/table/lesson_xp_segment_learning_type.yaml +24 -0
  269. package/hedhog/table/lesson_xp_segment_skill.yaml +24 -0
  270. package/hedhog/table/lms_bulk_upload_item.yaml +44 -0
  271. package/hedhog/table/lms_bulk_upload_session.yaml +42 -0
  272. package/hedhog/table/student_area_xp.yaml +30 -0
  273. package/hedhog/table/student_learning_type_xp.yaml +30 -0
  274. package/hedhog/table/student_skill_xp.yaml +30 -0
  275. package/hedhog/table/student_xp_event.yaml +34 -0
  276. package/hedhog/table/xp_area.yaml +39 -0
  277. package/hedhog/table/xp_learning_type.yaml +34 -0
  278. package/hedhog/table/xp_skill.yaml +39 -0
  279. package/package.json +8 -7
  280. package/src/bitcode-wallet/bitcode-wallet.service.ts +113 -0
  281. package/src/bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto.ts +32 -0
  282. package/src/course/course-audio-transcription.service.ts +58 -21
  283. package/src/course/course-lesson.controller.ts +6 -1
  284. package/src/course/course-structure.controller.ts +10 -0
  285. package/src/course/course-structure.service.ts +174 -1
  286. package/src/course/course-video-conversion.service.ts +113 -75
  287. package/src/course/course.controller.ts +22 -3
  288. package/src/course/course.module.ts +2 -0
  289. package/src/course/course.service.ts +847 -30
  290. package/src/course/dto/cleanup-course-storage.dto.ts +22 -0
  291. package/src/course/dto/cleanup-upload-history.dto.ts +26 -0
  292. package/src/course/dto/create-course-bulk-job.dto.ts +6 -0
  293. package/src/course/lms-bulk-upload-automation.service.ts +560 -0
  294. package/src/course/lms-bulk-upload-infra.service.ts +327 -0
  295. package/src/course/lms-bulk-upload.constants.ts +5 -0
  296. package/src/course/lms-bulk-upload.controller.ts +79 -3
  297. package/src/course/lms-bulk-upload.service.ts +1029 -204
  298. package/src/course/lms-setting.controller.ts +22 -6
  299. package/src/lesson-xp-map/dto/create-lesson-xp-map.dto.ts +17 -0
  300. package/src/lesson-xp-map/dto/create-lesson-xp-segment.dto.ts +102 -0
  301. package/src/lesson-xp-map/dto/review-lesson-xp-map.dto.ts +7 -0
  302. package/src/lesson-xp-map/dto/update-lesson-xp-map.dto.ts +36 -0
  303. package/src/lesson-xp-map/dto/update-lesson-xp-segment.dto.ts +78 -0
  304. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +396 -0
  305. package/src/lesson-xp-map/lesson-xp-map.controller.ts +116 -0
  306. package/src/lesson-xp-map/lesson-xp-map.module.ts +21 -0
  307. package/src/lesson-xp-map/lesson-xp-map.service.ts +442 -0
  308. package/src/lesson-xp-map/lesson-xp-segment.controller.ts +36 -0
  309. package/src/lesson-xp-map/lesson-xp-segment.service.ts +229 -0
  310. package/src/lms.module.ts +17 -2
  311. package/src/platforma/dto/update-profile.dto.ts +59 -0
  312. package/src/platforma/platforma.controller.ts +57 -2
  313. package/src/platforma/platforma.service.ts +268 -0
  314. package/src/student-xp/student-xp.controller.ts +76 -0
  315. package/src/student-xp/student-xp.module.ts +12 -0
  316. package/src/student-xp/student-xp.service.ts +236 -0
  317. package/src/xp-catalog/dto/create-xp-area.dto.ts +40 -0
  318. package/src/xp-catalog/dto/create-xp-learning-type.dto.ts +35 -0
  319. package/src/xp-catalog/dto/create-xp-skill.dto.ts +35 -0
  320. package/src/xp-catalog/dto/update-xp-area.dto.ts +43 -0
  321. package/src/xp-catalog/dto/update-xp-learning-type.dto.ts +38 -0
  322. package/src/xp-catalog/dto/update-xp-skill.dto.ts +38 -0
  323. package/src/xp-catalog/xp-area.controller.ts +64 -0
  324. package/src/xp-catalog/xp-area.service.ts +196 -0
  325. package/src/xp-catalog/xp-catalog.module.ts +16 -0
  326. package/src/xp-catalog/xp-learning-type.controller.ts +59 -0
  327. package/src/xp-catalog/xp-learning-type.service.ts +170 -0
  328. package/src/xp-catalog/xp-skill.controller.ts +71 -0
  329. package/src/xp-catalog/xp-skill.service.ts +205 -0
@@ -13,27 +13,182 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.LmsBulkUploadService = void 0;
16
- const core_1 = require("@hed-hog/core");
17
16
  const api_prisma_1 = require("@hed-hog/api-prisma");
18
- const core_2 = require("@hed-hog/core");
17
+ const core_1 = require("@hed-hog/core");
19
18
  const common_1 = require("@nestjs/common");
19
+ const cleanup_upload_history_dto_1 = require("./dto/cleanup-upload-history.dto");
20
+ const lms_bulk_upload_infra_service_1 = require("./lms-bulk-upload-infra.service");
21
+ const lms_bulk_upload_constants_1 = require("./lms-bulk-upload.constants");
20
22
  let LmsBulkUploadService = class LmsBulkUploadService {
21
- constructor(settingService, prismaService, fileService) {
23
+ constructor(settingService, prismaService, fileService, webhookIntegrationService, bulkUploadInfraService) {
22
24
  this.settingService = settingService;
23
25
  this.prismaService = prismaService;
24
26
  this.fileService = fileService;
27
+ this.webhookIntegrationService = webhookIntegrationService;
28
+ this.bulkUploadInfraService = bulkUploadInfraService;
29
+ }
30
+ async configureBulkUpload(userId, payload) {
31
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
32
+ const storageProfileId = Number((_a = payload === null || payload === void 0 ? void 0 : payload.storageProfileId) !== null && _a !== void 0 ? _a : 0);
33
+ const settings = await this.settingService.getSettingValues([
34
+ 'lms-bulk-upload-lambda-role-arn',
35
+ ]);
36
+ const lambdaRoleArn = String((_b = settings['lms-bulk-upload-lambda-role-arn']) !== null && _b !== void 0 ? _b : '').trim();
37
+ if (!Number.isFinite(storageProfileId) || storageProfileId <= 0) {
38
+ throw new common_1.BadRequestException('Select a valid storage integration profile.');
39
+ }
40
+ const { profile, region, bucket, durationSeconds } = await this.resolveStorageProfileAndBucket(userId, {
41
+ storageProfileId,
42
+ });
43
+ const credentials = profile.config;
44
+ const accessKeyId = String((_c = credentials.access_key_id) !== null && _c !== void 0 ? _c : '').trim();
45
+ const secretAccessKey = String((_d = credentials.secret_access_key) !== null && _d !== void 0 ? _d : '').trim();
46
+ const sessionToken = String((_e = credentials.session_token) !== null && _e !== void 0 ? _e : '').trim() || undefined;
47
+ if (!accessKeyId || !secretAccessKey) {
48
+ throw new common_1.BadRequestException('Storage profile does not contain AWS credentials (access_key_id/secret_access_key).');
49
+ }
50
+ await this.fileService.headS3Bucket({
51
+ accessKeyId,
52
+ secretAccessKey,
53
+ sessionToken,
54
+ region,
55
+ bucket,
56
+ });
57
+ const roleArn = String((_f = credentials.role_arn) !== null && _f !== void 0 ? _f : '').trim();
58
+ const externalId = String((_g = credentials.external_id) !== null && _g !== void 0 ? _g : '').trim();
59
+ const tempCredentials = await this.fileService.getTemporaryCredentials({
60
+ accessKeyId,
61
+ secretAccessKey,
62
+ sessionToken,
63
+ region,
64
+ roleArn: roleArn || undefined,
65
+ externalId: externalId || undefined,
66
+ durationSeconds,
67
+ sessionName: `hedhog-desktop-u${userId}-${Date.now()}`,
68
+ });
69
+ await this.fileService.headS3Bucket({
70
+ accessKeyId: tempCredentials.AccessKeyId,
71
+ secretAccessKey: tempCredentials.SecretAccessKey,
72
+ sessionToken: tempCredentials.SessionToken,
73
+ region,
74
+ bucket,
75
+ });
76
+ const webhook = await this.ensureBulkUploadWebhook();
77
+ if (!webhook.public_url || !webhook.plainToken) {
78
+ throw new common_1.BadRequestException('Bulk upload webhook could not be provisioned with URL/token.');
79
+ }
80
+ const infrastructure = await this.bulkUploadInfraService.syncDesktopUploadInfrastructure({
81
+ bucketName: bucket,
82
+ region,
83
+ webhookUrl: String(webhook.public_url),
84
+ webhookSecret: String(webhook.plainToken),
85
+ accessKeyId,
86
+ secretAccessKey,
87
+ sessionToken,
88
+ lambdaRoleArn,
89
+ });
90
+ await this.settingService.setManySettings({
91
+ setting: [
92
+ {
93
+ slug: 'lms-bulk-upload-storage-profile-id',
94
+ value: String(storageProfileId),
95
+ },
96
+ ],
97
+ });
98
+ return {
99
+ success: true,
100
+ storageProfileId,
101
+ bucketName: bucket,
102
+ region,
103
+ credentials: {
104
+ expiresAt: (_j = (_h = tempCredentials.Expiration) === null || _h === void 0 ? void 0 : _h.toISOString()) !== null && _j !== void 0 ? _j : null,
105
+ },
106
+ infrastructure,
107
+ webhook,
108
+ };
109
+ }
110
+ async regenerateWebhookTokenAndSync(userId) {
111
+ var _a, _b, _c, _d;
112
+ const { profile, region, bucket } = await this.resolveStorageProfileAndBucket(userId);
113
+ const settings = await this.settingService.getSettingValues([
114
+ 'lms-bulk-upload-lambda-role-arn',
115
+ ]);
116
+ const lambdaRoleArn = String((_a = settings['lms-bulk-upload-lambda-role-arn']) !== null && _a !== void 0 ? _a : '').trim();
117
+ const credentials = profile.config;
118
+ const accessKeyId = String((_b = credentials.access_key_id) !== null && _b !== void 0 ? _b : '').trim();
119
+ const secretAccessKey = String((_c = credentials.secret_access_key) !== null && _c !== void 0 ? _c : '').trim();
120
+ const sessionToken = String((_d = credentials.session_token) !== null && _d !== void 0 ? _d : '').trim() || undefined;
121
+ if (!accessKeyId || !secretAccessKey) {
122
+ throw new common_1.BadRequestException('Storage profile does not contain AWS credentials (access_key_id/secret_access_key).');
123
+ }
124
+ await this.fileService.headS3Bucket({
125
+ accessKeyId,
126
+ secretAccessKey,
127
+ sessionToken,
128
+ region,
129
+ bucket,
130
+ });
131
+ const webhook = await this.ensureBulkUploadWebhook();
132
+ if (!webhook.public_url || !webhook.plainToken) {
133
+ throw new common_1.BadRequestException('Bulk upload webhook could not be provisioned with URL/token.');
134
+ }
135
+ const infrastructure = await this.bulkUploadInfraService.syncDesktopUploadInfrastructure({
136
+ bucketName: bucket,
137
+ region,
138
+ webhookUrl: String(webhook.public_url),
139
+ webhookSecret: String(webhook.plainToken),
140
+ accessKeyId,
141
+ secretAccessKey,
142
+ sessionToken,
143
+ lambdaRoleArn,
144
+ });
145
+ return {
146
+ success: true,
147
+ storageProfileId: Number(profile.id),
148
+ bucketName: bucket,
149
+ region,
150
+ infrastructure,
151
+ webhook,
152
+ };
153
+ }
154
+ async setBulkUploadLambdaRoleArn(value) {
155
+ const lambdaRoleArn = String(value !== null && value !== void 0 ? value : '').trim();
156
+ await this.settingService.setManySettings({
157
+ setting: [
158
+ {
159
+ slug: 'lms-bulk-upload-lambda-role-arn',
160
+ value: lambdaRoleArn,
161
+ },
162
+ ],
163
+ });
164
+ return {
165
+ success: true,
166
+ lambdaRoleArn,
167
+ };
25
168
  }
26
169
  async getBulkUploadSettings() {
170
+ var _a, _b, _c;
27
171
  const settings = await this.settingService.getSettingValues([
28
172
  'lms-bulk-upload-storage-profile-id',
29
- 'lms-bulk-upload-s3-bucket',
173
+ 'lms-bulk-upload-lambda-role-arn',
30
174
  'lms-bulk-upload-sts-duration-seconds',
31
175
  ]);
176
+ const storageProfileId = Number(settings['lms-bulk-upload-storage-profile-id'] || 0) || null;
177
+ let bucketName = '';
178
+ if (storageProfileId) {
179
+ const profile = await this.prismaService.integration_profile.findUnique({
180
+ where: { id: storageProfileId },
181
+ select: {
182
+ config: true,
183
+ },
184
+ });
185
+ const config = (_a = profile === null || profile === void 0 ? void 0 : profile.config) !== null && _a !== void 0 ? _a : null;
186
+ bucketName = String((_b = config === null || config === void 0 ? void 0 : config.bucket) !== null && _b !== void 0 ? _b : '').trim();
187
+ }
32
188
  return {
33
- storageProfileId: Number(settings['lms-bulk-upload-storage-profile-id'] || 0) || null,
34
- bucketName: typeof settings['lms-bulk-upload-s3-bucket'] === 'string'
35
- ? settings['lms-bulk-upload-s3-bucket'].trim()
36
- : '',
189
+ storageProfileId,
190
+ bucketName,
191
+ lambdaRoleArn: String((_c = settings['lms-bulk-upload-lambda-role-arn']) !== null && _c !== void 0 ? _c : '').trim(),
37
192
  sessionDurationSeconds: Number(settings['lms-bulk-upload-sts-duration-seconds'] || 3600) || 3600,
38
193
  };
39
194
  }
@@ -109,14 +264,442 @@ let LmsBulkUploadService = class LmsBulkUploadService {
109
264
  };
110
265
  }
111
266
  }
112
- async resolveStorageProfileAndBucket(userId) {
267
+ async ensureBulkUploadWebhook() {
268
+ const existing = await this.prismaService.webhook_integration.findFirst({
269
+ where: {
270
+ OR: [
271
+ { slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_WEBHOOK_SLUG },
272
+ { slug: { startsWith: `${lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_WEBHOOK_SLUG}-` } },
273
+ ],
274
+ },
275
+ orderBy: { id: 'asc' },
276
+ select: {
277
+ id: true,
278
+ slug: true,
279
+ status: true,
280
+ },
281
+ });
282
+ const webhook = !existing
283
+ ? await this.webhookIntegrationService.create({
284
+ slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_WEBHOOK_SLUG,
285
+ name: 'LMS Bulk Upload',
286
+ description: 'Webhook para integração de upload em massa do LMS',
287
+ status: 'active',
288
+ require_token: true,
289
+ })
290
+ : existing.status !== 'active'
291
+ ? await this.webhookIntegrationService.update(existing.id, {
292
+ status: 'active',
293
+ require_token: true,
294
+ })
295
+ : await this.webhookIntegrationService.get(existing.id);
296
+ const actions = await this.webhookIntegrationService.listActions(webhook.id);
297
+ const receiveAction = actions.find((action) => action.type === 'app_command' &&
298
+ action.app_command_slug === lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_RECEIVE_VIDEO_COMMAND);
299
+ if (!receiveAction) {
300
+ await this.webhookIntegrationService.createAction(webhook.id, {
301
+ type: 'app_command',
302
+ name: 'Receber vídeo do upload em massa',
303
+ status: 'active',
304
+ order: 1,
305
+ app_command_slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_RECEIVE_VIDEO_COMMAND,
306
+ app_command_params: {},
307
+ });
308
+ }
309
+ else if (receiveAction.status !== 'active' || receiveAction.order !== 1) {
310
+ await this.webhookIntegrationService.updateAction(webhook.id, receiveAction.id, {
311
+ type: 'app_command',
312
+ name: 'Receber vídeo do upload em massa',
313
+ status: 'active',
314
+ order: 1,
315
+ app_command_slug: lms_bulk_upload_constants_1.LMS_BULK_UPLOAD_RECEIVE_VIDEO_COMMAND,
316
+ app_command_params: {},
317
+ });
318
+ }
319
+ return this.webhookIntegrationService.regenerateToken(webhook.id);
320
+ }
321
+ async startUploadSession(userId, payload) {
113
322
  var _a, _b, _c, _d, _e;
323
+ const files = ((_a = payload === null || payload === void 0 ? void 0 : payload.files) !== null && _a !== void 0 ? _a : [])
324
+ .map((file) => {
325
+ var _a, _b, _c;
326
+ return ({
327
+ uploadId: String((_a = file === null || file === void 0 ? void 0 : file.uploadId) !== null && _a !== void 0 ? _a : '').trim(),
328
+ fileName: String((_b = file === null || file === void 0 ? void 0 : file.fileName) !== null && _b !== void 0 ? _b : '').trim(),
329
+ sizeBytes: Math.max(0, Number((_c = file === null || file === void 0 ? void 0 : file.sizeBytes) !== null && _c !== void 0 ? _c : 0) || 0),
330
+ });
331
+ })
332
+ .filter((file) => file.uploadId.length > 0 && file.fileName.length > 0);
333
+ if (files.length === 0) {
334
+ throw new common_1.BadRequestException('Provide at least one file to register bulk upload session.');
335
+ }
336
+ const appName = String((_b = payload === null || payload === void 0 ? void 0 : payload.appName) !== null && _b !== void 0 ? _b : 'hedhog-desktop').trim().slice(0, 80) || 'hedhog-desktop';
337
+ const folderPath = String((_c = payload === null || payload === void 0 ? void 0 : payload.folderPath) !== null && _c !== void 0 ? _c : '').trim() || null;
338
+ const totalFiles = files.length;
339
+ const totalBytes = files.reduce((sum, file) => sum + file.sizeBytes, 0);
340
+ const now = new Date();
341
+ const insertedSession = await this.prismaService.$queryRawUnsafe(`INSERT INTO "lms_bulk_upload_session"
342
+ ("user_id", "app_name", "folder_path", "status", "progress_percent", "total_files", "total_bytes", "heartbeat_at", "started_at", "created_at", "updated_at")
343
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
344
+ RETURNING "id"`, userId, appName, folderPath, 'started', 0, totalFiles, totalBytes, now, now, now, now);
345
+ const sessionId = Number((_e = (_d = insertedSession === null || insertedSession === void 0 ? void 0 : insertedSession[0]) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : 0);
346
+ if (!Number.isFinite(sessionId) || sessionId <= 0) {
347
+ throw new common_1.BadRequestException('Could not register bulk upload session.');
348
+ }
349
+ for (const file of files) {
350
+ await this.prismaService.$executeRawUnsafe(`INSERT INTO "lms_bulk_upload_item"
351
+ ("session_id", "upload_id", "file_name", "size_bytes", "status", "progress_percent", "last_heartbeat_at", "created_at", "updated_at")
352
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, sessionId, file.uploadId, file.fileName, file.sizeBytes, 'queued', 0, now, now, now);
353
+ }
354
+ return {
355
+ sessionId,
356
+ totalFiles,
357
+ totalBytes,
358
+ status: 'started',
359
+ };
360
+ }
361
+ async heartbeatUploadSession(userId, payload) {
362
+ var _a, _b, _c, _d, _e, _f;
363
+ const sessionId = Number((_a = payload === null || payload === void 0 ? void 0 : payload.sessionId) !== null && _a !== void 0 ? _a : 0);
364
+ if (!Number.isFinite(sessionId) || sessionId <= 0) {
365
+ throw new common_1.BadRequestException('Provide a valid sessionId for heartbeat.');
366
+ }
367
+ const sessionRows = await this.prismaService.$queryRawUnsafe(`SELECT "id", "user_id"
368
+ FROM "lms_bulk_upload_session"
369
+ WHERE "id" = $1
370
+ LIMIT 1`, sessionId);
371
+ const session = (_b = sessionRows === null || sessionRows === void 0 ? void 0 : sessionRows[0]) !== null && _b !== void 0 ? _b : null;
372
+ if (!session) {
373
+ throw new common_1.NotFoundException(`Bulk upload session ${sessionId} was not found.`);
374
+ }
375
+ if (Number(session.user_id) !== userId) {
376
+ throw new common_1.BadRequestException('You can only update your own bulk upload session.');
377
+ }
378
+ const now = new Date();
379
+ const normalizedItems = ((_c = payload === null || payload === void 0 ? void 0 : payload.items) !== null && _c !== void 0 ? _c : []).map((item) => {
380
+ var _a, _b, _c;
381
+ const status = this.normalizeItemStatus(item === null || item === void 0 ? void 0 : item.status);
382
+ return {
383
+ uploadId: String((_a = item === null || item === void 0 ? void 0 : item.uploadId) !== null && _a !== void 0 ? _a : '').trim(),
384
+ status,
385
+ progressPercent: this.clampPercent(item === null || item === void 0 ? void 0 : item.progressPercent),
386
+ errorMessage: String((_b = item === null || item === void 0 ? void 0 : item.errorMessage) !== null && _b !== void 0 ? _b : '').trim() || null,
387
+ uploadedKey: String((_c = item === null || item === void 0 ? void 0 : item.uploadedKey) !== null && _c !== void 0 ? _c : '').trim() || null,
388
+ };
389
+ }).filter((item) => item.uploadId.length > 0);
390
+ for (const item of normalizedItems) {
391
+ await this.prismaService.$executeRawUnsafe(`UPDATE "lms_bulk_upload_item"
392
+ SET
393
+ "status" = $3,
394
+ "progress_percent" = $4,
395
+ "error_message" = $5,
396
+ "uploaded_key" = $6,
397
+ "last_heartbeat_at" = $7,
398
+ "completed_at" = CASE WHEN $3 IN ('received', 'done', 'error', 'cancelled') THEN COALESCE("completed_at", $7) ELSE "completed_at" END,
399
+ "updated_at" = $7
400
+ WHERE "session_id" = $1
401
+ AND "upload_id" = $2`, sessionId, item.uploadId, item.status, item.progressPercent, item.errorMessage, item.uploadedKey, now);
402
+ }
403
+ const summaryRows = await this.prismaService.$queryRawUnsafe(`SELECT
404
+ COUNT(*)::int AS total,
405
+ COUNT(*) FILTER (WHERE "status" = 'done')::int AS done_count,
406
+ COUNT(*) FILTER (WHERE "status" = 'error')::int AS error_count,
407
+ COUNT(*) FILTER (WHERE "status" = 'cancelled')::int AS cancelled_count,
408
+ COUNT(*) FILTER (WHERE "status" = 'uploading')::int AS uploading_count,
409
+ COUNT(*) FILTER (WHERE "status" = 'queued')::int AS queued_count,
410
+ COALESCE(AVG("progress_percent"), 0)::float AS average_progress
411
+ FROM "lms_bulk_upload_item"
412
+ WHERE "session_id" = $1`, sessionId);
413
+ const summary = (_d = summaryRows === null || summaryRows === void 0 ? void 0 : summaryRows[0]) !== null && _d !== void 0 ? _d : {
414
+ total: 0,
415
+ done_count: 0,
416
+ error_count: 0,
417
+ cancelled_count: 0,
418
+ uploading_count: 0,
419
+ queued_count: 0,
420
+ average_progress: 0,
421
+ };
422
+ const requestedStatus = this.normalizeSessionStatus(payload === null || payload === void 0 ? void 0 : payload.status);
423
+ const computedStatus = this.computeSessionStatus(summary);
424
+ const finalStatus = requestedStatus !== null && requestedStatus !== void 0 ? requestedStatus : computedStatus;
425
+ const progressPercent = this.clampPercent((_f = (_e = payload === null || payload === void 0 ? void 0 : payload.progressPercent) !== null && _e !== void 0 ? _e : summary.average_progress) !== null && _f !== void 0 ? _f : 0);
426
+ const isTerminal = ['completed', 'failed', 'cancelled'].includes(finalStatus);
427
+ await this.prismaService.$executeRawUnsafe(`UPDATE "lms_bulk_upload_session"
428
+ SET
429
+ "status" = $2,
430
+ "progress_percent" = $3,
431
+ "heartbeat_at" = $4,
432
+ "finished_at" = CASE WHEN $5 THEN COALESCE("finished_at", $4) ELSE "finished_at" END,
433
+ "updated_at" = $4
434
+ WHERE "id" = $1`, sessionId, finalStatus, progressPercent, now, isTerminal);
435
+ return {
436
+ sessionId,
437
+ status: finalStatus,
438
+ progressPercent,
439
+ totals: {
440
+ total: Number(summary.total || 0),
441
+ done: Number(summary.done_count || 0),
442
+ error: Number(summary.error_count || 0),
443
+ cancelled: Number(summary.cancelled_count || 0),
444
+ },
445
+ };
446
+ }
447
+ async listUploadItems(params) {
448
+ var _a, _b, _c;
449
+ const page = Math.max(Number(params.page) || 1, 1);
450
+ const pageSize = Math.min(Math.max(Number(params.pageSize) || 20, 1), 100);
451
+ const offset = (page - 1) * pageSize;
452
+ const search = String((_a = params.search) !== null && _a !== void 0 ? _a : '').trim();
453
+ const status = this.normalizeItemStatus(params.status, true);
454
+ const where = [];
455
+ const args = [];
456
+ if (status) {
457
+ args.push(status);
458
+ where.push(`i."status" = $${args.length}`);
459
+ }
460
+ if (search) {
461
+ args.push(`%${search}%`);
462
+ const searchParam = `$${args.length}`;
463
+ where.push(`(i."file_name" ILIKE ${searchParam} OR s."app_name" ILIKE ${searchParam} OR COALESCE(sender."name", '') ILIKE ${searchParam} OR CAST(s."user_id" AS TEXT) ILIKE ${searchParam} OR COALESCE(mc."title", '') ILIKE ${searchParam} OR COALESCE(ml."title", '') ILIKE ${searchParam})`);
464
+ }
465
+ const whereSql = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
466
+ const countRows = await this.prismaService.$queryRawUnsafe(`SELECT COUNT(*)::int AS total
467
+ FROM "lms_bulk_upload_item" i
468
+ JOIN "lms_bulk_upload_session" s ON s."id" = i."session_id"
469
+ LEFT JOIN LATERAL (
470
+ SELECT p."name"
471
+ FROM "person_user" pu
472
+ JOIN "person" p ON p."id" = pu."person_id"
473
+ WHERE pu."user_id" = s."user_id"
474
+ ORDER BY pu."id" DESC
475
+ LIMIT 1
476
+ ) sender ON TRUE
477
+ LEFT JOIN "course" mc ON mc."id" = i."matched_course_id"
478
+ LEFT JOIN "course_module" ms ON ms."id" = i."matched_session_id"
479
+ LEFT JOIN "course_lesson" ml ON ml."id" = i."matched_lesson_id"
480
+ ${whereSql}`, ...args);
481
+ const total = Number((_c = (_b = countRows === null || countRows === void 0 ? void 0 : countRows[0]) === null || _b === void 0 ? void 0 : _b.total) !== null && _c !== void 0 ? _c : 0);
482
+ const dataRows = await this.prismaService.$queryRawUnsafe(`SELECT
483
+ i."id",
484
+ i."session_id",
485
+ i."upload_id",
486
+ i."file_name",
487
+ i."size_bytes",
488
+ i."status",
489
+ i."progress_percent",
490
+ i."error_message",
491
+ i."uploaded_key",
492
+ i."received_at",
493
+ i."updated_at",
494
+ i."completed_at",
495
+ s."app_name",
496
+ s."user_id",
497
+ u."photo_id" AS user_photo_id,
498
+ sender."name" AS user_name,
499
+ s."status" AS session_status,
500
+ s."started_at" AS session_started_at,
501
+ mc."id" AS matched_course_id,
502
+ mc."title" AS matched_course_title,
503
+ mc."slug" AS matched_course_slug,
504
+ logo."file_id" AS matched_course_logo_file_id,
505
+ ms."id" AS matched_session_id,
506
+ ms."title" AS matched_session_title,
507
+ ml."id" AS matched_lesson_id,
508
+ ml."title" AS matched_lesson_title
509
+ FROM "lms_bulk_upload_item" i
510
+ JOIN "lms_bulk_upload_session" s ON s."id" = i."session_id"
511
+ LEFT JOIN "user" u ON u."id" = s."user_id"
512
+ LEFT JOIN LATERAL (
513
+ SELECT p."name"
514
+ FROM "person_user" pu
515
+ JOIN "person" p ON p."id" = pu."person_id"
516
+ WHERE pu."user_id" = s."user_id"
517
+ ORDER BY pu."id" DESC
518
+ LIMIT 1
519
+ ) sender ON TRUE
520
+ LEFT JOIN "course" mc ON mc."id" = i."matched_course_id"
521
+ LEFT JOIN "course_module" ms ON ms."id" = i."matched_session_id"
522
+ LEFT JOIN "course_lesson" ml ON ml."id" = i."matched_lesson_id"
523
+ LEFT JOIN LATERAL (
524
+ SELECT ci."file_id"
525
+ FROM "course_image" ci
526
+ INNER JOIN "image_type" it ON it."id" = ci."image_type_id"
527
+ WHERE ci."course_id" = mc."id"
528
+ AND it."slug" = 'course-logo'
529
+ ORDER BY ci."id" DESC
530
+ LIMIT 1
531
+ ) logo ON TRUE
532
+ ${whereSql}
533
+ ORDER BY i."updated_at" DESC
534
+ LIMIT $${args.length + 1}
535
+ OFFSET $${args.length + 2}`, ...args, pageSize, offset);
536
+ return {
537
+ data: dataRows.map((row) => ({
538
+ id: row.id,
539
+ sessionId: row.session_id,
540
+ uploadId: row.upload_id,
541
+ fileName: row.file_name,
542
+ sizeBytes: Number(row.size_bytes || 0),
543
+ status: row.status,
544
+ progressPercent: Number(row.progress_percent || 0),
545
+ errorMessage: row.error_message,
546
+ uploadedKey: row.uploaded_key,
547
+ receivedAt: row.received_at,
548
+ updatedAt: row.updated_at,
549
+ completedAt: row.completed_at,
550
+ appName: row.app_name,
551
+ userId: row.user_id,
552
+ userPhotoId: row.user_photo_id ? Number(row.user_photo_id) : null,
553
+ userName: row.user_name,
554
+ sessionStatus: row.session_status,
555
+ startedAt: row.session_started_at,
556
+ matchedCourseId: row.matched_course_id ? Number(row.matched_course_id) : null,
557
+ matchedCourseTitle: row.matched_course_title,
558
+ matchedCourseSlug: row.matched_course_slug,
559
+ matchedCourseLogoFileId: row.matched_course_logo_file_id ? Number(row.matched_course_logo_file_id) : null,
560
+ matchedSessionId: row.matched_session_id ? Number(row.matched_session_id) : null,
561
+ matchedSessionTitle: row.matched_session_title,
562
+ matchedLessonId: row.matched_lesson_id ? Number(row.matched_lesson_id) : null,
563
+ matchedLessonTitle: row.matched_lesson_title,
564
+ })),
565
+ total,
566
+ page,
567
+ pageSize,
568
+ lastPage: Math.max(1, Math.ceil(total / pageSize)),
569
+ };
570
+ }
571
+ async cleanupUploadHistory(payload) {
572
+ var _a, _b;
573
+ const statusSet = new Set();
574
+ for (const rawStatus of (_a = payload === null || payload === void 0 ? void 0 : payload.statuses) !== null && _a !== void 0 ? _a : []) {
575
+ const normalized = String(rawStatus !== null && rawStatus !== void 0 ? rawStatus : '').trim().toLowerCase();
576
+ if (cleanup_upload_history_dto_1.BULK_UPLOAD_CLEANUP_STATUSES.includes(normalized)) {
577
+ statusSet.add(normalized);
578
+ }
579
+ }
580
+ if (statusSet.size === 0) {
581
+ statusSet.add('done');
582
+ }
583
+ const statuses = Array.from(statusSet);
584
+ const allowedWindows = new Set(cleanup_upload_history_dto_1.BULK_UPLOAD_CLEANUP_WINDOWS);
585
+ const rawWindow = String((_b = payload === null || payload === void 0 ? void 0 : payload.timeWindow) !== null && _b !== void 0 ? _b : '').trim().toLowerCase();
586
+ const timeWindow = allowedWindows.has(rawWindow) ? rawWindow : 'last_day';
587
+ let cutoffDate = null;
588
+ if (timeWindow === 'last_hour') {
589
+ cutoffDate = new Date(Date.now() - 60 * 60 * 1000);
590
+ }
591
+ else if (timeWindow === 'last_day') {
592
+ cutoffDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
593
+ }
594
+ else if (timeWindow === 'last_week') {
595
+ cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
596
+ }
597
+ const statusPlaceholders = statuses.map((_, index) => `$${index + 1}`).join(', ');
598
+ const args = [...statuses];
599
+ let timeFilterSql = '';
600
+ if (cutoffDate) {
601
+ args.push(cutoffDate);
602
+ timeFilterSql = ` AND i."updated_at" >= $${args.length}`;
603
+ }
604
+ const deletedRows = await this.prismaService.$queryRawUnsafe(`DELETE FROM "lms_bulk_upload_item" i
605
+ USING "lms_bulk_upload_session" s
606
+ WHERE i."session_id" = s."id"
607
+ AND i."status" IN (${statusPlaceholders})
608
+ ${timeFilterSql}
609
+ RETURNING i."session_id"`, ...args);
610
+ const deletedItems = deletedRows.length;
611
+ const sessionIds = Array.from(new Set(deletedRows
612
+ .map((row) => Number(row.session_id || 0))
613
+ .filter((sessionId) => Number.isFinite(sessionId) && sessionId > 0)));
614
+ let deletedSessions = 0;
615
+ if (sessionIds.length > 0) {
616
+ const sessionPlaceholders = sessionIds
617
+ .map((_, index) => `$${index + 1}`)
618
+ .join(', ');
619
+ const deletedSessionRows = await this.prismaService.$queryRawUnsafe(`DELETE FROM "lms_bulk_upload_session" s
620
+ WHERE s."id" IN (${sessionPlaceholders})
621
+ AND NOT EXISTS (
622
+ SELECT 1
623
+ FROM "lms_bulk_upload_item" i
624
+ WHERE i."session_id" = s."id"
625
+ )
626
+ RETURNING s."id"`, ...sessionIds);
627
+ deletedSessions = deletedSessionRows.length;
628
+ }
629
+ return {
630
+ success: true,
631
+ deletedItems,
632
+ deletedSessions,
633
+ filtersApplied: {
634
+ statuses,
635
+ timeWindow,
636
+ },
637
+ };
638
+ }
639
+ clampPercent(value) {
640
+ const parsed = Number(value);
641
+ if (!Number.isFinite(parsed))
642
+ return 0;
643
+ return Math.max(0, Math.min(100, Math.round(parsed)));
644
+ }
645
+ normalizeSessionStatus(value) {
646
+ const normalized = String(value !== null && value !== void 0 ? value : '').trim().toLowerCase();
647
+ if (!normalized)
648
+ return null;
649
+ if (normalized === 'started' ||
650
+ normalized === 'uploading' ||
651
+ normalized === 'paused' ||
652
+ normalized === 'cancelling' ||
653
+ normalized === 'completed' ||
654
+ normalized === 'failed' ||
655
+ normalized === 'cancelled') {
656
+ return normalized;
657
+ }
658
+ return null;
659
+ }
660
+ normalizeItemStatus(value, allowEmpty = false) {
661
+ const normalized = String(value !== null && value !== void 0 ? value : '').trim().toLowerCase();
662
+ if (!normalized && allowEmpty)
663
+ return null;
664
+ if (normalized === 'queued' ||
665
+ normalized === 'uploading' ||
666
+ normalized === 'cancelling' ||
667
+ normalized === 'received' ||
668
+ normalized === 'done' ||
669
+ normalized === 'error' ||
670
+ normalized === 'cancelled') {
671
+ return normalized;
672
+ }
673
+ return allowEmpty ? null : 'queued';
674
+ }
675
+ computeSessionStatus(summary) {
676
+ const total = Number(summary.total || 0);
677
+ const done = Number(summary.done_count || 0);
678
+ const error = Number(summary.error_count || 0);
679
+ const cancelled = Number(summary.cancelled_count || 0);
680
+ const uploading = Number(summary.uploading_count || 0);
681
+ const queued = Number(summary.queued_count || 0);
682
+ if (total === 0)
683
+ return 'started';
684
+ if (done === total)
685
+ return 'completed';
686
+ if (done + error + cancelled === total && error > 0)
687
+ return 'failed';
688
+ if (done + error + cancelled === total && cancelled > 0 && error === 0)
689
+ return 'cancelled';
690
+ if (uploading > 0)
691
+ return 'uploading';
692
+ if (queued === total)
693
+ return 'started';
694
+ return 'uploading';
695
+ }
696
+ async resolveStorageProfileAndBucket(userId, overrides) {
697
+ var _a, _b, _c, _d, _e, _f;
114
698
  const settings = await this.settingService.getSettingValues([
115
699
  'lms-bulk-upload-storage-profile-id',
116
- 'lms-bulk-upload-s3-bucket',
117
700
  'lms-bulk-upload-sts-duration-seconds',
118
701
  ]);
119
- const profileId = Number(settings['lms-bulk-upload-storage-profile-id'] || 0);
702
+ const profileId = Number((_b = (_a = overrides === null || overrides === void 0 ? void 0 : overrides.storageProfileId) !== null && _a !== void 0 ? _a : settings['lms-bulk-upload-storage-profile-id']) !== null && _b !== void 0 ? _b : 0);
120
703
  if (!Number.isFinite(profileId) || profileId <= 0) {
121
704
  throw new common_1.BadRequestException('LMS bulk upload storage profile is not configured (lms-bulk-upload-storage-profile-id).');
122
705
  }
@@ -133,17 +716,15 @@ let LmsBulkUploadService = class LmsBulkUploadService {
133
716
  if (profile.integration_type.slug !== 'storage') {
134
717
  throw new common_1.BadRequestException(`Integration profile ${profileId} is not a storage profile.`);
135
718
  }
136
- const providerSlug = String((_a = profile.integration_provider.slug) !== null && _a !== void 0 ? _a : '').toLowerCase();
719
+ const providerSlug = String((_c = profile.integration_provider.slug) !== null && _c !== void 0 ? _c : '').toLowerCase();
137
720
  if (providerSlug !== 's3') {
138
721
  throw new common_1.BadRequestException(`Integration provider ${providerSlug} is not supported for desktop bulk upload. Use AWS S3 profile.`);
139
722
  }
140
- const config = (_b = profile.config) !== null && _b !== void 0 ? _b : {};
141
- const region = String((_c = config.region) !== null && _c !== void 0 ? _c : '').trim() || 'us-east-1';
142
- const bucketFromSettings = String((_d = settings['lms-bulk-upload-s3-bucket']) !== null && _d !== void 0 ? _d : '').trim();
143
- const bucketFromProfile = String((_e = config.bucket) !== null && _e !== void 0 ? _e : '').trim();
144
- const bucket = bucketFromSettings || bucketFromProfile;
723
+ const config = (_d = profile.config) !== null && _d !== void 0 ? _d : {};
724
+ const region = String((_e = config.region) !== null && _e !== void 0 ? _e : '').trim() || 'us-east-1';
725
+ const bucket = String((_f = config.bucket) !== null && _f !== void 0 ? _f : '').trim();
145
726
  if (!bucket) {
146
- throw new common_1.BadRequestException('LMS bulk upload bucket is not configured. Set lms-bulk-upload-s3-bucket or profile bucket.');
727
+ throw new common_1.BadRequestException(`Storage integration profile ${profileId} does not define a bucket in config.bucket.`);
147
728
  }
148
729
  const durationSeconds = Math.max(900, Math.min(43200, Number(settings['lms-bulk-upload-sts-duration-seconds'] || 3600) || 3600));
149
730
  const prefix = `desktop/lms/u${userId}/`;
@@ -159,11 +740,15 @@ let LmsBulkUploadService = class LmsBulkUploadService {
159
740
  exports.LmsBulkUploadService = LmsBulkUploadService;
160
741
  exports.LmsBulkUploadService = LmsBulkUploadService = __decorate([
161
742
  (0, common_1.Injectable)(),
162
- __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_2.SettingService))),
743
+ __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.SettingService))),
163
744
  __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_prisma_1.PrismaService))),
164
745
  __param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.FileService))),
165
- __metadata("design:paramtypes", [core_2.SettingService,
746
+ __param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => core_1.WebhookIntegrationService))),
747
+ __param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => lms_bulk_upload_infra_service_1.LmsBulkUploadInfraService))),
748
+ __metadata("design:paramtypes", [core_1.SettingService,
166
749
  api_prisma_1.PrismaService,
167
- core_1.FileService])
750
+ core_1.FileService,
751
+ core_1.WebhookIntegrationService,
752
+ lms_bulk_upload_infra_service_1.LmsBulkUploadInfraService])
168
753
  ], LmsBulkUploadService);
169
754
  //# sourceMappingURL=lms-bulk-upload.service.js.map