@hed-hog/lms 0.0.349 → 0.0.350

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 (409) hide show
  1. package/dist/achievement/achievement.controller.d.ts +62 -0
  2. package/dist/achievement/achievement.controller.d.ts.map +1 -0
  3. package/dist/achievement/achievement.controller.js +90 -0
  4. package/dist/achievement/achievement.controller.js.map +1 -0
  5. package/dist/achievement/achievement.mcp-tools.d.ts +19 -0
  6. package/dist/achievement/achievement.mcp-tools.d.ts.map +1 -0
  7. package/dist/achievement/achievement.mcp-tools.js +157 -0
  8. package/dist/achievement/achievement.mcp-tools.js.map +1 -0
  9. package/dist/achievement/achievement.module.d.ts +3 -0
  10. package/dist/achievement/achievement.module.d.ts.map +1 -0
  11. package/dist/achievement/achievement.module.js +26 -0
  12. package/dist/achievement/achievement.module.js.map +1 -0
  13. package/dist/achievement/achievement.service.d.ts +72 -0
  14. package/dist/achievement/achievement.service.d.ts.map +1 -0
  15. package/dist/achievement/achievement.service.js +200 -0
  16. package/dist/achievement/achievement.service.js.map +1 -0
  17. package/dist/achievement/dto/create-achievement.dto.d.ts +12 -0
  18. package/dist/achievement/dto/create-achievement.dto.d.ts.map +1 -0
  19. package/dist/achievement/dto/create-achievement.dto.js +60 -0
  20. package/dist/achievement/dto/create-achievement.dto.js.map +1 -0
  21. package/dist/achievement/dto/update-achievement.dto.d.ts +11 -0
  22. package/dist/achievement/dto/update-achievement.dto.d.ts.map +1 -0
  23. package/dist/achievement/dto/update-achievement.dto.js +57 -0
  24. package/dist/achievement/dto/update-achievement.dto.js.map +1 -0
  25. package/dist/bitcode-wallet/bitcode-wallet.controller.d.ts +114 -0
  26. package/dist/bitcode-wallet/bitcode-wallet.controller.d.ts.map +1 -0
  27. package/dist/bitcode-wallet/bitcode-wallet.controller.js +102 -0
  28. package/dist/bitcode-wallet/bitcode-wallet.controller.js.map +1 -0
  29. package/dist/bitcode-wallet/bitcode-wallet.mcp-tools.d.ts +25 -0
  30. package/dist/bitcode-wallet/bitcode-wallet.mcp-tools.d.ts.map +1 -0
  31. package/dist/bitcode-wallet/bitcode-wallet.mcp-tools.js +160 -0
  32. package/dist/bitcode-wallet/bitcode-wallet.mcp-tools.js.map +1 -0
  33. package/dist/bitcode-wallet/bitcode-wallet.module.d.ts +3 -0
  34. package/dist/bitcode-wallet/bitcode-wallet.module.d.ts.map +1 -0
  35. package/dist/bitcode-wallet/bitcode-wallet.module.js +26 -0
  36. package/dist/bitcode-wallet/bitcode-wallet.module.js.map +1 -0
  37. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +127 -0
  38. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -0
  39. package/dist/bitcode-wallet/bitcode-wallet.service.js +264 -0
  40. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -0
  41. package/dist/bitcode-wallet/dto/create-bitcode-wallet-transaction.dto.d.ts +8 -0
  42. package/dist/bitcode-wallet/dto/create-bitcode-wallet-transaction.dto.d.ts.map +1 -0
  43. package/dist/bitcode-wallet/dto/create-bitcode-wallet-transaction.dto.js +33 -0
  44. package/dist/bitcode-wallet/dto/create-bitcode-wallet-transaction.dto.js.map +1 -0
  45. package/dist/bitcode-wallet/dto/create-bitcode-wallet.dto.d.ts +4 -0
  46. package/dist/bitcode-wallet/dto/create-bitcode-wallet.dto.d.ts.map +1 -0
  47. package/dist/bitcode-wallet/dto/create-bitcode-wallet.dto.js +22 -0
  48. package/dist/bitcode-wallet/dto/create-bitcode-wallet.dto.js.map +1 -0
  49. package/dist/bitcode-wallet/dto/update-bitcode-wallet-transaction.dto.d.ts +7 -0
  50. package/dist/bitcode-wallet/dto/update-bitcode-wallet-transaction.dto.d.ts.map +1 -0
  51. package/dist/bitcode-wallet/dto/update-bitcode-wallet-transaction.dto.js +35 -0
  52. package/dist/bitcode-wallet/dto/update-bitcode-wallet-transaction.dto.js.map +1 -0
  53. package/dist/bitcode-wallet/dto/update-bitcode-wallet.dto.d.ts +4 -0
  54. package/dist/bitcode-wallet/dto/update-bitcode-wallet.dto.d.ts.map +1 -0
  55. package/dist/bitcode-wallet/dto/update-bitcode-wallet.dto.js +23 -0
  56. package/dist/bitcode-wallet/dto/update-bitcode-wallet.dto.js.map +1 -0
  57. package/dist/certificate/certificate.controller.d.ts +22 -0
  58. package/dist/certificate/certificate.controller.d.ts.map +1 -1
  59. package/dist/certificate/certificate.controller.js +12 -0
  60. package/dist/certificate/certificate.controller.js.map +1 -1
  61. package/dist/certificate/certificate.mcp-tools.d.ts +24 -0
  62. package/dist/certificate/certificate.mcp-tools.d.ts.map +1 -0
  63. package/dist/certificate/certificate.mcp-tools.js +188 -0
  64. package/dist/certificate/certificate.mcp-tools.js.map +1 -0
  65. package/dist/certificate/certificate.module.d.ts.map +1 -1
  66. package/dist/certificate/certificate.module.js +2 -1
  67. package/dist/certificate/certificate.module.js.map +1 -1
  68. package/dist/certificate/certificate.service.d.ts +25 -2
  69. package/dist/certificate/certificate.service.d.ts.map +1 -1
  70. package/dist/certificate/certificate.service.js +87 -2
  71. package/dist/certificate/certificate.service.js.map +1 -1
  72. package/dist/certificate/dto/update-certificate-public-access.dto.d.ts +4 -0
  73. package/dist/certificate/dto/update-certificate-public-access.dto.d.ts.map +1 -0
  74. package/dist/certificate/dto/update-certificate-public-access.dto.js +21 -0
  75. package/dist/certificate/dto/update-certificate-public-access.dto.js.map +1 -0
  76. package/dist/class-group/class-group.mcp-tools.d.ts +87 -0
  77. package/dist/class-group/class-group.mcp-tools.d.ts.map +1 -0
  78. package/dist/class-group/class-group.mcp-tools.js +553 -0
  79. package/dist/class-group/class-group.mcp-tools.js.map +1 -0
  80. package/dist/class-group/class-group.module.d.ts.map +1 -1
  81. package/dist/class-group/class-group.module.js +2 -1
  82. package/dist/class-group/class-group.module.js.map +1 -1
  83. package/dist/class-group/class-group.service.d.ts +3 -1
  84. package/dist/class-group/class-group.service.d.ts.map +1 -1
  85. package/dist/class-group/class-group.service.js +45 -2
  86. package/dist/class-group/class-group.service.js.map +1 -1
  87. package/dist/course/course-operations-integration.service.d.ts +40 -0
  88. package/dist/course/course-operations-integration.service.d.ts.map +1 -0
  89. package/dist/course/course-operations-integration.service.js +372 -0
  90. package/dist/course/course-operations-integration.service.js.map +1 -0
  91. package/dist/course/course-structure.controller.d.ts +43 -4
  92. package/dist/course/course-structure.controller.d.ts.map +1 -1
  93. package/dist/course/course-structure.controller.js +22 -0
  94. package/dist/course/course-structure.controller.js.map +1 -1
  95. package/dist/course/course-structure.service.d.ts +42 -1
  96. package/dist/course/course-structure.service.d.ts.map +1 -1
  97. package/dist/course/course-structure.service.js +199 -32
  98. package/dist/course/course-structure.service.js.map +1 -1
  99. package/dist/course/course.controller.d.ts +12 -0
  100. package/dist/course/course.controller.d.ts.map +1 -1
  101. package/dist/course/course.mcp-tools.d.ts +90 -0
  102. package/dist/course/course.mcp-tools.d.ts.map +1 -0
  103. package/dist/course/course.mcp-tools.js +520 -0
  104. package/dist/course/course.mcp-tools.js.map +1 -0
  105. package/dist/course/course.module.d.ts.map +1 -1
  106. package/dist/course/course.module.js +8 -1
  107. package/dist/course/course.module.js.map +1 -1
  108. package/dist/course/course.service.d.ts +15 -1
  109. package/dist/course/course.service.d.ts.map +1 -1
  110. package/dist/course/course.service.js +70 -35
  111. package/dist/course/course.service.js.map +1 -1
  112. package/dist/course/dto/create-course.dto.d.ts +1 -0
  113. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  114. package/dist/course/dto/create-course.dto.js +7 -0
  115. package/dist/course/dto/create-course.dto.js.map +1 -1
  116. package/dist/course/dto/update-course-resources.dto.d.ts +11 -0
  117. package/dist/course/dto/update-course-resources.dto.d.ts.map +1 -0
  118. package/dist/course/dto/update-course-resources.dto.js +51 -0
  119. package/dist/course/dto/update-course-resources.dto.js.map +1 -0
  120. package/dist/course-lesson-discussion/course-lesson-discussion.controller.d.ts +23 -0
  121. package/dist/course-lesson-discussion/course-lesson-discussion.controller.d.ts.map +1 -0
  122. package/dist/course-lesson-discussion/course-lesson-discussion.controller.js +78 -0
  123. package/dist/course-lesson-discussion/course-lesson-discussion.controller.js.map +1 -0
  124. package/dist/course-lesson-discussion/course-lesson-discussion.mcp-tools.d.ts +22 -0
  125. package/dist/course-lesson-discussion/course-lesson-discussion.mcp-tools.d.ts.map +1 -0
  126. package/dist/course-lesson-discussion/course-lesson-discussion.mcp-tools.js +120 -0
  127. package/dist/course-lesson-discussion/course-lesson-discussion.mcp-tools.js.map +1 -0
  128. package/dist/course-lesson-discussion/course-lesson-discussion.module.d.ts +3 -0
  129. package/dist/course-lesson-discussion/course-lesson-discussion.module.d.ts.map +1 -0
  130. package/dist/course-lesson-discussion/course-lesson-discussion.module.js +26 -0
  131. package/dist/course-lesson-discussion/course-lesson-discussion.module.js.map +1 -0
  132. package/dist/course-lesson-discussion/course-lesson-discussion.service.d.ts +49 -0
  133. package/dist/course-lesson-discussion/course-lesson-discussion.service.d.ts.map +1 -0
  134. package/dist/course-lesson-discussion/course-lesson-discussion.service.js +272 -0
  135. package/dist/course-lesson-discussion/course-lesson-discussion.service.js.map +1 -0
  136. package/dist/course-lesson-discussion/dto/create-course-lesson-discussion-topic.dto.d.ts +6 -0
  137. package/dist/course-lesson-discussion/dto/create-course-lesson-discussion-topic.dto.d.ts.map +1 -0
  138. package/dist/course-lesson-discussion/dto/create-course-lesson-discussion-topic.dto.js +33 -0
  139. package/dist/course-lesson-discussion/dto/create-course-lesson-discussion-topic.dto.js.map +1 -0
  140. package/dist/course-lesson-note/course-lesson-note.controller.d.ts +53 -0
  141. package/dist/course-lesson-note/course-lesson-note.controller.d.ts.map +1 -0
  142. package/dist/course-lesson-note/course-lesson-note.controller.js +93 -0
  143. package/dist/course-lesson-note/course-lesson-note.controller.js.map +1 -0
  144. package/dist/course-lesson-note/course-lesson-note.mcp-tools.d.ts +27 -0
  145. package/dist/course-lesson-note/course-lesson-note.mcp-tools.d.ts.map +1 -0
  146. package/dist/course-lesson-note/course-lesson-note.mcp-tools.js +145 -0
  147. package/dist/course-lesson-note/course-lesson-note.mcp-tools.js.map +1 -0
  148. package/dist/course-lesson-note/course-lesson-note.module.d.ts +3 -0
  149. package/dist/course-lesson-note/course-lesson-note.module.d.ts.map +1 -0
  150. package/dist/course-lesson-note/course-lesson-note.module.js +26 -0
  151. package/dist/course-lesson-note/course-lesson-note.module.js.map +1 -0
  152. package/dist/course-lesson-note/course-lesson-note.service.d.ts +59 -0
  153. package/dist/course-lesson-note/course-lesson-note.service.d.ts.map +1 -0
  154. package/dist/course-lesson-note/course-lesson-note.service.js +195 -0
  155. package/dist/course-lesson-note/course-lesson-note.service.js.map +1 -0
  156. package/dist/course-lesson-note/dto/create-course-lesson-note.dto.d.ts +6 -0
  157. package/dist/course-lesson-note/dto/create-course-lesson-note.dto.d.ts.map +1 -0
  158. package/dist/course-lesson-note/dto/create-course-lesson-note.dto.js +33 -0
  159. package/dist/course-lesson-note/dto/create-course-lesson-note.dto.js.map +1 -0
  160. package/dist/course-lesson-note/dto/update-course-lesson-note.dto.d.ts +6 -0
  161. package/dist/course-lesson-note/dto/update-course-lesson-note.dto.d.ts.map +1 -0
  162. package/dist/course-lesson-note/dto/update-course-lesson-note.dto.js +35 -0
  163. package/dist/course-lesson-note/dto/update-course-lesson-note.dto.js.map +1 -0
  164. package/dist/dashboard/dashboard.mcp-tools.d.ts +10 -0
  165. package/dist/dashboard/dashboard.mcp-tools.d.ts.map +1 -0
  166. package/dist/dashboard/dashboard.mcp-tools.js +46 -0
  167. package/dist/dashboard/dashboard.mcp-tools.js.map +1 -0
  168. package/dist/dashboard/dashboard.module.d.ts.map +1 -1
  169. package/dist/dashboard/dashboard.module.js +2 -1
  170. package/dist/dashboard/dashboard.module.js.map +1 -1
  171. package/dist/enterprise/enterprise.mcp-tools.d.ts +82 -0
  172. package/dist/enterprise/enterprise.mcp-tools.d.ts.map +1 -0
  173. package/dist/enterprise/enterprise.mcp-tools.js +516 -0
  174. package/dist/enterprise/enterprise.mcp-tools.js.map +1 -0
  175. package/dist/enterprise/enterprise.module.d.ts.map +1 -1
  176. package/dist/enterprise/enterprise.module.js +2 -1
  177. package/dist/enterprise/enterprise.module.js.map +1 -1
  178. package/dist/enterprise/training/enterprise-training.module.d.ts.map +1 -1
  179. package/dist/enterprise/training/enterprise-training.module.js +11 -1
  180. package/dist/enterprise/training/enterprise-training.module.js.map +1 -1
  181. package/dist/enterprise/training/training-admin.mcp-tools.d.ts +79 -0
  182. package/dist/enterprise/training/training-admin.mcp-tools.d.ts.map +1 -0
  183. package/dist/enterprise/training/training-admin.mcp-tools.js +620 -0
  184. package/dist/enterprise/training/training-admin.mcp-tools.js.map +1 -0
  185. package/dist/enterprise/training/training-instructor.mcp-tools.d.ts +47 -0
  186. package/dist/enterprise/training/training-instructor.mcp-tools.d.ts.map +1 -0
  187. package/dist/enterprise/training/training-instructor.mcp-tools.js +275 -0
  188. package/dist/enterprise/training/training-instructor.mcp-tools.js.map +1 -0
  189. package/dist/enterprise/training/training-student.controller.d.ts +24 -0
  190. package/dist/enterprise/training/training-student.controller.d.ts.map +1 -1
  191. package/dist/enterprise/training/training-student.controller.js +22 -0
  192. package/dist/enterprise/training/training-student.controller.js.map +1 -1
  193. package/dist/enterprise/training/training-student.mcp-tools.d.ts +27 -0
  194. package/dist/enterprise/training/training-student.mcp-tools.d.ts.map +1 -0
  195. package/dist/enterprise/training/training-student.mcp-tools.js +186 -0
  196. package/dist/enterprise/training/training-student.mcp-tools.js.map +1 -0
  197. package/dist/enterprise/training/training-student.service.d.ts +32 -0
  198. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  199. package/dist/enterprise/training/training-student.service.js +138 -0
  200. package/dist/enterprise/training/training-student.service.js.map +1 -1
  201. package/dist/evaluation/evaluation.mcp-tools.d.ts +25 -0
  202. package/dist/evaluation/evaluation.mcp-tools.d.ts.map +1 -0
  203. package/dist/evaluation/evaluation.mcp-tools.js +220 -0
  204. package/dist/evaluation/evaluation.mcp-tools.js.map +1 -0
  205. package/dist/evaluation/evaluation.module.d.ts.map +1 -1
  206. package/dist/evaluation/evaluation.module.js +2 -1
  207. package/dist/evaluation/evaluation.module.js.map +1 -1
  208. package/dist/exam/dto/create-exam-question.dto.d.ts +2 -0
  209. package/dist/exam/dto/create-exam-question.dto.d.ts.map +1 -1
  210. package/dist/exam/dto/create-exam-question.dto.js +10 -0
  211. package/dist/exam/dto/create-exam-question.dto.js.map +1 -1
  212. package/dist/exam/dto/create-exam.dto.d.ts +2 -0
  213. package/dist/exam/dto/create-exam.dto.d.ts.map +1 -1
  214. package/dist/exam/dto/create-exam.dto.js +10 -0
  215. package/dist/exam/dto/create-exam.dto.js.map +1 -1
  216. package/dist/exam/dto/create-question-subject.dto.d.ts +5 -0
  217. package/dist/exam/dto/create-question-subject.dto.d.ts.map +1 -0
  218. package/dist/exam/dto/create-question-subject.dto.js +28 -0
  219. package/dist/exam/dto/create-question-subject.dto.js.map +1 -0
  220. package/dist/exam/exam-attempt.controller.d.ts +4 -0
  221. package/dist/exam/exam-attempt.controller.d.ts.map +1 -1
  222. package/dist/exam/exam-attempt.service.d.ts +7 -1
  223. package/dist/exam/exam-attempt.service.d.ts.map +1 -1
  224. package/dist/exam/exam-attempt.service.js +47 -17
  225. package/dist/exam/exam-attempt.service.js.map +1 -1
  226. package/dist/exam/exam.controller.d.ts +34 -0
  227. package/dist/exam/exam.controller.d.ts.map +1 -1
  228. package/dist/exam/exam.controller.js +27 -0
  229. package/dist/exam/exam.controller.js.map +1 -1
  230. package/dist/exam/exam.mcp-tools.d.ts +62 -0
  231. package/dist/exam/exam.mcp-tools.d.ts.map +1 -0
  232. package/dist/exam/exam.mcp-tools.js +430 -0
  233. package/dist/exam/exam.mcp-tools.js.map +1 -0
  234. package/dist/exam/exam.module.d.ts.map +1 -1
  235. package/dist/exam/exam.module.js +2 -1
  236. package/dist/exam/exam.module.js.map +1 -1
  237. package/dist/exam/exam.service.d.ts +38 -0
  238. package/dist/exam/exam.service.d.ts.map +1 -1
  239. package/dist/exam/exam.service.js +114 -17
  240. package/dist/exam/exam.service.js.map +1 -1
  241. package/dist/index.d.ts +9 -0
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js +9 -0
  244. package/dist/index.js.map +1 -1
  245. package/dist/instructor/instructor.mcp-tools.d.ts +41 -0
  246. package/dist/instructor/instructor.mcp-tools.d.ts.map +1 -0
  247. package/dist/instructor/instructor.mcp-tools.js +326 -0
  248. package/dist/instructor/instructor.mcp-tools.js.map +1 -0
  249. package/dist/instructor/instructor.module.d.ts.map +1 -1
  250. package/dist/instructor/instructor.module.js +2 -1
  251. package/dist/instructor/instructor.module.js.map +1 -1
  252. package/dist/lms.module.d.ts.map +1 -1
  253. package/dist/lms.module.js +15 -0
  254. package/dist/lms.module.js.map +1 -1
  255. package/dist/realtime/lms-realtime.controller.d.ts +7 -0
  256. package/dist/realtime/lms-realtime.controller.d.ts.map +1 -0
  257. package/dist/realtime/lms-realtime.controller.js +34 -0
  258. package/dist/realtime/lms-realtime.controller.js.map +1 -0
  259. package/dist/realtime/lms-realtime.module.d.ts +3 -0
  260. package/dist/realtime/lms-realtime.module.d.ts.map +1 -0
  261. package/dist/realtime/lms-realtime.module.js +25 -0
  262. package/dist/realtime/lms-realtime.module.js.map +1 -0
  263. package/dist/realtime/lms-realtime.service.d.ts +36 -0
  264. package/dist/realtime/lms-realtime.service.d.ts.map +1 -0
  265. package/dist/realtime/lms-realtime.service.js +59 -0
  266. package/dist/realtime/lms-realtime.service.js.map +1 -0
  267. package/dist/realtime/lms-realtime.subscriber.d.ts +10 -0
  268. package/dist/realtime/lms-realtime.subscriber.d.ts.map +1 -0
  269. package/dist/realtime/lms-realtime.subscriber.js +70 -0
  270. package/dist/realtime/lms-realtime.subscriber.js.map +1 -0
  271. package/dist/reports/reports.mcp-tools.d.ts +10 -0
  272. package/dist/reports/reports.mcp-tools.d.ts.map +1 -0
  273. package/dist/reports/reports.mcp-tools.js +50 -0
  274. package/dist/reports/reports.mcp-tools.js.map +1 -0
  275. package/dist/reports/reports.module.d.ts.map +1 -1
  276. package/dist/reports/reports.module.js +2 -1
  277. package/dist/reports/reports.module.js.map +1 -1
  278. package/dist/training/training.mcp-tools.d.ts +20 -0
  279. package/dist/training/training.mcp-tools.d.ts.map +1 -0
  280. package/dist/training/training.mcp-tools.js +181 -0
  281. package/dist/training/training.mcp-tools.js.map +1 -0
  282. package/dist/training/training.module.d.ts.map +1 -1
  283. package/dist/training/training.module.js +2 -1
  284. package/dist/training/training.module.js.map +1 -1
  285. package/hedhog/data/integration_event_catalog.yaml +69 -0
  286. package/hedhog/data/menu.yaml +34 -0
  287. package/hedhog/data/route.yaml +2351 -0
  288. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +168 -103
  289. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +80 -1
  290. package/hedhog/frontend/app/_components/course-picker.tsx.ejs +228 -0
  291. package/hedhog/frontend/app/_lib/hooks/use-lms-realtime-refresh.ts.ejs +58 -0
  292. package/hedhog/frontend/app/achievements/page.tsx.ejs +844 -0
  293. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +1010 -0
  294. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +61 -2
  295. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +7 -0
  296. package/hedhog/frontend/app/classes/page.tsx.ejs +55 -2060
  297. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +28 -0
  298. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -0
  299. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +0 -9
  300. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +80 -66
  301. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +583 -8
  302. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +527 -57
  303. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -1
  304. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +106 -4
  305. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +2 -1
  306. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +11 -1
  307. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +53 -6
  308. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +13 -2
  309. package/hedhog/frontend/app/courses/page.tsx.ejs +175 -29
  310. package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +3 -0
  311. package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +7 -0
  312. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +169 -2
  313. package/hedhog/frontend/app/exams/page.tsx.ejs +77 -22
  314. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +1 -0
  315. package/hedhog/frontend/app/instructors/page.tsx.ejs +1 -0
  316. package/hedhog/frontend/app/paths/page.tsx.ejs +6 -24
  317. package/hedhog/frontend/app/training/page.tsx.ejs +6 -24
  318. package/hedhog/frontend/messages/en.json +314 -12
  319. package/hedhog/frontend/messages/pt.json +314 -12
  320. package/hedhog/query/triggers.sql +53 -0
  321. package/hedhog/table/achievement.yaml +46 -0
  322. package/hedhog/table/bitcode_wallet.yaml +18 -0
  323. package/hedhog/table/bitcode_wallet_transaction.yaml +22 -0
  324. package/hedhog/table/certificate.yaml +3 -0
  325. package/hedhog/table/course.yaml +4 -0
  326. package/hedhog/table/course_file.yaml +23 -0
  327. package/hedhog/table/course_lesson.yaml +5 -0
  328. package/hedhog/table/course_lesson_discussion_like.yaml +21 -0
  329. package/hedhog/table/course_lesson_discussion_topic.yaml +35 -0
  330. package/hedhog/table/course_lesson_note.yaml +34 -0
  331. package/hedhog/table/exam.yaml +5 -0
  332. package/hedhog/table/learning_path_enrollment.yaml +6 -0
  333. package/hedhog/table/question.yaml +10 -0
  334. package/hedhog/table/question_subject.yaml +17 -0
  335. package/hedhog/table/student_activity_streak.yaml +25 -0
  336. package/package.json +6 -6
  337. package/src/achievement/achievement.controller.ts +60 -0
  338. package/src/achievement/achievement.mcp-tools.ts +108 -0
  339. package/src/achievement/achievement.module.ts +13 -0
  340. package/src/achievement/achievement.service.ts +252 -0
  341. package/src/achievement/dto/create-achievement.dto.ts +50 -0
  342. package/src/achievement/dto/update-achievement.dto.ts +47 -0
  343. package/src/bitcode-wallet/bitcode-wallet.controller.ts +69 -0
  344. package/src/bitcode-wallet/bitcode-wallet.mcp-tools.ts +107 -0
  345. package/src/bitcode-wallet/bitcode-wallet.module.ts +13 -0
  346. package/src/bitcode-wallet/bitcode-wallet.service.ts +361 -0
  347. package/src/bitcode-wallet/dto/create-bitcode-wallet-transaction.dto.ts +27 -0
  348. package/src/bitcode-wallet/dto/create-bitcode-wallet.dto.ts +7 -0
  349. package/src/bitcode-wallet/dto/update-bitcode-wallet-transaction.dto.ts +28 -0
  350. package/src/bitcode-wallet/dto/update-bitcode-wallet.dto.ts +8 -0
  351. package/src/certificate/certificate.controller.ts +20 -11
  352. package/src/certificate/certificate.mcp-tools.ts +131 -0
  353. package/src/certificate/certificate.module.ts +2 -1
  354. package/src/certificate/certificate.service.ts +95 -4
  355. package/src/certificate/dto/update-certificate-public-access.dto.ts +6 -0
  356. package/src/class-group/class-group.mcp-tools.ts +435 -0
  357. package/src/class-group/class-group.module.ts +2 -1
  358. package/src/class-group/class-group.service.ts +51 -1
  359. package/src/course/course-operations-integration.service.ts +520 -0
  360. package/src/course/course-structure.controller.ts +22 -8
  361. package/src/course/course-structure.service.ts +215 -23
  362. package/src/course/course.mcp-tools.ts +409 -0
  363. package/src/course/course.module.ts +8 -1
  364. package/src/course/course.service.ts +106 -27
  365. package/src/course/dto/create-course.dto.ts +8 -0
  366. package/src/course/dto/update-course-resources.dto.ts +39 -0
  367. package/src/course-lesson-discussion/course-lesson-discussion.controller.ts +55 -0
  368. package/src/course-lesson-discussion/course-lesson-discussion.mcp-tools.ts +75 -0
  369. package/src/course-lesson-discussion/course-lesson-discussion.module.ts +13 -0
  370. package/src/course-lesson-discussion/course-lesson-discussion.service.ts +354 -0
  371. package/src/course-lesson-discussion/dto/create-course-lesson-discussion-topic.dto.ts +16 -0
  372. package/src/course-lesson-note/course-lesson-note.controller.ts +68 -0
  373. package/src/course-lesson-note/course-lesson-note.mcp-tools.ts +96 -0
  374. package/src/course-lesson-note/course-lesson-note.module.ts +13 -0
  375. package/src/course-lesson-note/course-lesson-note.service.ts +248 -0
  376. package/src/course-lesson-note/dto/create-course-lesson-note.dto.ts +16 -0
  377. package/src/course-lesson-note/dto/update-course-lesson-note.dto.ts +18 -0
  378. package/src/dashboard/dashboard.mcp-tools.ts +23 -0
  379. package/src/dashboard/dashboard.module.ts +2 -1
  380. package/src/enterprise/enterprise.mcp-tools.ts +403 -0
  381. package/src/enterprise/enterprise.module.ts +2 -1
  382. package/src/enterprise/training/enterprise-training.module.ts +11 -1
  383. package/src/enterprise/training/training-admin.mcp-tools.ts +479 -0
  384. package/src/enterprise/training/training-instructor.mcp-tools.ts +210 -0
  385. package/src/enterprise/training/training-student.controller.ts +17 -1
  386. package/src/enterprise/training/training-student.mcp-tools.ts +136 -0
  387. package/src/enterprise/training/training-student.service.ts +167 -1
  388. package/src/evaluation/evaluation.mcp-tools.ts +155 -0
  389. package/src/evaluation/evaluation.module.ts +2 -1
  390. package/src/exam/dto/create-exam-question.dto.ts +8 -0
  391. package/src/exam/dto/create-exam.dto.ts +8 -0
  392. package/src/exam/dto/create-question-subject.dto.ts +12 -0
  393. package/src/exam/exam-attempt.service.ts +46 -14
  394. package/src/exam/exam.controller.ts +19 -0
  395. package/src/exam/exam.mcp-tools.ts +337 -0
  396. package/src/exam/exam.module.ts +2 -1
  397. package/src/exam/exam.service.ts +121 -0
  398. package/src/index.ts +9 -0
  399. package/src/instructor/instructor.mcp-tools.ts +243 -0
  400. package/src/instructor/instructor.module.ts +2 -1
  401. package/src/lms.module.ts +15 -1
  402. package/src/realtime/lms-realtime.controller.ts +12 -0
  403. package/src/realtime/lms-realtime.module.ts +12 -0
  404. package/src/realtime/lms-realtime.service.ts +98 -0
  405. package/src/realtime/lms-realtime.subscriber.ts +61 -0
  406. package/src/reports/reports.mcp-tools.ts +27 -0
  407. package/src/reports/reports.module.ts +2 -1
  408. package/src/training/training.mcp-tools.ts +128 -0
  409. package/src/training/training.module.ts +2 -1
@@ -36,6 +36,7 @@ type ApiCourse = {
36
36
  primaryColor?: string | null;
37
37
  secondaryColor?: string | null;
38
38
  logoFileId?: number | null;
39
+ operationsProjectId?: number | null;
39
40
  };
40
41
 
41
42
  export interface EnterpriseCourseEditSheetProps {
@@ -128,6 +129,9 @@ export function EnterpriseCourseEditSheet({
128
129
  nivel: toPtLevel(course.level),
129
130
  status: toPtStatus(course.status),
130
131
  categorias: course.categories ?? [],
132
+ operationsProjectId: course.operationsProjectId
133
+ ? String(course.operationsProjectId)
134
+ : '',
131
135
  primaryColor: course.primaryColor ?? '#1D4ED8',
132
136
  secondaryColor: course.secondaryColor ?? '#111827',
133
137
  logoFileId: course.logoFileId ?? null,
@@ -171,6 +175,9 @@ export function EnterpriseCourseEditSheet({
171
175
  primaryContrastColor: getContrastColor(values.primaryColor),
172
176
  secondaryColor: values.secondaryColor,
173
177
  secondaryContrastColor: getContrastColor(values.secondaryColor),
178
+ operationsProjectId: values.operationsProjectId
179
+ ? Number(values.operationsProjectId)
180
+ : null,
174
181
  logoFileId: values.logoFileId ?? null,
175
182
  },
176
183
  });
@@ -8,6 +8,7 @@ import {
8
8
  SearchBar,
9
9
  ViewModeToggle,
10
10
  } from '@/components/entity-list';
11
+ import { EntityPicker } from '@/components/ui/entity-picker';
11
12
  import { RichTextEditor } from '@/components/rich-text-editor';
12
13
  import { Badge } from '@/components/ui/badge';
13
14
  import { Button } from '@/components/ui/button';
@@ -132,13 +133,22 @@ interface Questao {
132
133
  examQuestionId: number;
133
134
  order: number;
134
135
  tipo: ExamQuestionType;
136
+ assuntoId: number | null;
137
+ assunto: string | null;
135
138
  enunciado: string;
139
+ explicacao: string;
136
140
  pontuacao: number;
137
141
  alternativas: Alternativa[];
138
142
  fillBlankAnswers: FillBlankAnswer[];
139
143
  matchingPairs: MatchingPair[];
140
144
  }
141
145
 
146
+ type AssuntoQuestaoOption = {
147
+ id: number;
148
+ name: string;
149
+ description?: string | null;
150
+ };
151
+
142
152
  type ExamQuestionType =
143
153
  | 'multiple_choice'
144
154
  | 'true_false'
@@ -164,7 +174,10 @@ type ApiExamQuestion = {
164
174
  examQuestionId: number;
165
175
  order: number;
166
176
  questionType: ExamQuestionType;
177
+ subjectId: number | null;
178
+ subjectName: string | null;
167
179
  statement: string;
180
+ explanation?: string | null;
168
181
  points: number;
169
182
  alternatives: ApiExamQuestionAlternative[];
170
183
  fillBlankAnswers?: Array<{
@@ -188,8 +201,18 @@ type ApiExamQuestionsResponse = {
188
201
  data: ApiExamQuestion[];
189
202
  };
190
203
 
204
+ type ApiQuestionSubjectListResponse = {
205
+ data: AssuntoQuestaoOption[];
206
+ total: number;
207
+ page: number;
208
+ pageSize: number;
209
+ lastPage: number;
210
+ };
211
+
191
212
  type QuestaoForm = {
213
+ subjectId: number;
192
214
  enunciado: string;
215
+ explicacao?: string;
193
216
  questionType: ExamQuestionType;
194
217
  pontuacao: number;
195
218
  };
@@ -204,7 +227,10 @@ function mapApiQuestion(question: ApiExamQuestion): Questao {
204
227
  examQuestionId: question.examQuestionId,
205
228
  order: question.order,
206
229
  tipo: question.questionType,
230
+ assuntoId: question.subjectId,
231
+ assunto: question.subjectName,
207
232
  enunciado: question.statement,
233
+ explicacao: question.explanation ?? '',
208
234
  pontuacao: question.points,
209
235
  alternativas: question.alternatives.map((alternative) => ({
210
236
  id: alternative.id,
@@ -440,6 +466,11 @@ function SortableQuestao({
440
466
 
441
467
  <div className="flex items-start justify-between gap-3">
442
468
  <div className="mt-2 flex flex-wrap items-center gap-2">
469
+ {questao.assunto && (
470
+ <Badge variant="secondary" className="text-xs">
471
+ {questao.assunto}
472
+ </Badge>
473
+ )}
443
474
  <Badge variant="outline" className="text-xs">
444
475
  {t(`question.types.${questao.tipo}`)}
445
476
  </Badge>
@@ -588,6 +619,9 @@ export default function QuestoesPage() {
588
619
  );
589
620
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
590
621
  const [questaoToDelete, setQuestaoToDelete] = useState<Questao | null>(null);
622
+ const [assuntosLocais, setAssuntosLocais] = useState<AssuntoQuestaoOption[]>(
623
+ []
624
+ );
591
625
  const [hasChanges, setHasChanges] = useState(false);
592
626
  const [saving, setSaving] = useState(false);
593
627
  const [buscaInput, setBuscaInput] = useState('');
@@ -608,7 +642,9 @@ export default function QuestoesPage() {
608
642
  });
609
643
 
610
644
  const questaoSchema = z.object({
645
+ subjectId: z.coerce.number().int().min(1, t('sheet.validation.subjectRequired')),
611
646
  enunciado: z.string().min(5, t('sheet.validation.statementMin')),
647
+ explicacao: z.string().optional(),
612
648
  questionType: z.enum([
613
649
  'multiple_choice',
614
650
  'true_false',
@@ -622,7 +658,9 @@ export default function QuestoesPage() {
622
658
  const form = useForm<QuestaoForm>({
623
659
  resolver: zodResolver(questaoSchema),
624
660
  defaultValues: {
661
+ subjectId: 0,
625
662
  enunciado: '',
663
+ explicacao: '',
626
664
  questionType: 'multiple_choice',
627
665
  pontuacao: 1,
628
666
  },
@@ -701,6 +739,65 @@ export default function QuestoesPage() {
701
739
  setCurrentPage(1);
702
740
  }
703
741
 
742
+
743
+ async function loadQuestionSubjectOptions(args: {
744
+ page: number;
745
+ pageSize: number;
746
+ search: string;
747
+ }) {
748
+ const response = await request<ApiQuestionSubjectListResponse>({
749
+ url: '/lms/exams/question-subjects',
750
+ method: 'GET',
751
+ params: {
752
+ page: args.page,
753
+ pageSize: args.pageSize,
754
+ ...(args.search ? { search: args.search } : {}),
755
+ },
756
+ });
757
+
758
+ const items = response.data?.data ?? [];
759
+
760
+ setAssuntosLocais((prev) => {
761
+ const merged = [...prev, ...items];
762
+ return merged.filter(
763
+ (item, index, arr) =>
764
+ arr.findIndex((candidate) => candidate.id === item.id) === index
765
+ );
766
+ });
767
+
768
+ return {
769
+ items,
770
+ hasMore: (response.data?.page ?? 1) < (response.data?.lastPage ?? 1),
771
+ };
772
+ }
773
+
774
+ async function createQuestionSubject(values: Record<string, string>) {
775
+ const name = values.name?.trim() ?? '';
776
+
777
+ if (!name) {
778
+ toast.error(t('toasts.subjectNameRequired'));
779
+ return null;
780
+ }
781
+
782
+ const response = await request<AssuntoQuestaoOption>({
783
+ url: '/lms/exams/question-subjects',
784
+ method: 'POST',
785
+ data: { name },
786
+ });
787
+
788
+ const created = response.data;
789
+
790
+ if (created?.id) {
791
+ setAssuntosLocais((prev) =>
792
+ prev.some((item) => item.id === created.id) ? prev : [...prev, created]
793
+ );
794
+ toast.success(t('toasts.subjectCreated'));
795
+ return created;
796
+ }
797
+
798
+ return null;
799
+ }
800
+
704
801
  useEffect(() => {
705
802
  if (currentPage > totalPages) {
706
803
  setCurrentPage(totalPages);
@@ -786,7 +883,9 @@ export default function QuestoesPage() {
786
883
  function openCreateSheet() {
787
884
  setEditingQuestao(null);
788
885
  form.reset({
886
+ subjectId: 0,
789
887
  enunciado: '',
888
+ explicacao: '',
790
889
  questionType: 'multiple_choice',
791
890
  pontuacao: 1,
792
891
  });
@@ -802,7 +901,9 @@ export default function QuestoesPage() {
802
901
  function openEditSheet(questao: Questao) {
803
902
  setEditingQuestao(questao);
804
903
  form.reset({
904
+ subjectId: questao.assuntoId ?? 0,
805
905
  enunciado: questao.enunciado,
906
+ explicacao: questao.explicacao,
806
907
  questionType: questao.tipo,
807
908
  pontuacao: questao.pontuacao,
808
909
  });
@@ -967,7 +1068,9 @@ export default function QuestoesPage() {
967
1068
  }
968
1069
 
969
1070
  const payload = {
1071
+ subjectId: data.subjectId,
970
1072
  statement: data.enunciado,
1073
+ explanation: data.explicacao?.trim() || null,
971
1074
  questionType: data.questionType,
972
1075
  points: Math.max(1, Math.round(data.pontuacao)),
973
1076
  ...(requiresAlternatives && {
@@ -1033,11 +1136,18 @@ export default function QuestoesPage() {
1033
1136
  }
1034
1137
 
1035
1138
  async function duplicateQuestao(questao: Questao) {
1139
+ if (!questao.assuntoId) {
1140
+ toast.error(t('toasts.subjectRequiredToDuplicate'));
1141
+ return;
1142
+ }
1143
+
1036
1144
  await request({
1037
- url: `/lms/exams/${examId}/questions`,
1145
+ url: '/lms/exams/' + examId + '/questions',
1038
1146
  method: 'POST',
1039
1147
  data: {
1040
- statement: `${questao.enunciado}${t('copySuffix')}`,
1148
+ subjectId: questao.assuntoId,
1149
+ statement: questao.enunciado + t('copySuffix'),
1150
+ explanation: questao.explicacao?.trim() || null,
1041
1151
  questionType: questao.tipo,
1042
1152
  points: Math.max(1, Math.round(questao.pontuacao)),
1043
1153
  ...(OBJECTIVE_QUESTION_TYPES.includes(questao.tipo) && {
@@ -1346,6 +1456,7 @@ export default function QuestoesPage() {
1346
1456
  <TableHeader>
1347
1457
  <TableRow>
1348
1458
  <TableHead>{t('table.headers.question')}</TableHead>
1459
+ <TableHead>{t('table.headers.subject')}</TableHead>
1349
1460
  <TableHead>{t('table.headers.type')}</TableHead>
1350
1461
  <TableHead className="text-right">
1351
1462
  {t('table.headers.points')}
@@ -1366,6 +1477,7 @@ export default function QuestoesPage() {
1366
1477
  </p>
1367
1478
  </div>
1368
1479
  </TableCell>
1480
+ <TableCell>{questao.assunto ?? '-'}</TableCell>
1369
1481
  <TableCell>
1370
1482
  <Badge variant="outline" className="text-xs">
1371
1483
  {t(`question.types.${questao.tipo}`)}
@@ -1493,6 +1605,43 @@ export default function QuestoesPage() {
1493
1605
  onSubmit={form.handleSubmit(onSubmit)}
1494
1606
  className="flex flex-1 flex-col gap-5 px-4 pb-4"
1495
1607
  >
1608
+ <FormField
1609
+ control={form.control}
1610
+ name="subjectId"
1611
+ render={({ field }) => (
1612
+ <FormItem>
1613
+ <FormLabel>{t('sheet.fields.subject')}</FormLabel>
1614
+ <FormControl>
1615
+ <EntityPicker<AssuntoQuestaoOption>
1616
+ value={field.value || null}
1617
+ valueType="number"
1618
+ placeholder={t('sheet.fields.subjectPlaceholder')}
1619
+ searchPlaceholder={t('sheet.fields.subjectSearchPlaceholder')}
1620
+ entityLabel={t('sheet.fields.subjectEntityLabel')}
1621
+ noResultsLabel={t('sheet.fields.subjectNoResults')}
1622
+ options={assuntosLocais}
1623
+ loadOptions={loadQuestionSubjectOptions}
1624
+ getOptionLabel={(option) => option.name}
1625
+ getOptionDescription={(option) => option.description ?? undefined}
1626
+ createTitle={t('sheet.fields.subjectCreateTitle')}
1627
+ createDescription={t('sheet.fields.subjectCreateDescription')}
1628
+ createFields={[
1629
+ {
1630
+ name: 'name',
1631
+ label: t('sheet.fields.subjectCreateNameLabel'),
1632
+ placeholder: t('sheet.fields.subjectCreateNamePlaceholder'),
1633
+ required: true,
1634
+ },
1635
+ ]}
1636
+ onCreate={createQuestionSubject}
1637
+ onChange={(value) => field.onChange(Number(value ?? 0))}
1638
+ />
1639
+ </FormControl>
1640
+ <FormMessage />
1641
+ </FormItem>
1642
+ )}
1643
+ />
1644
+
1496
1645
  <FormField
1497
1646
  control={form.control}
1498
1647
  name="enunciado"
@@ -1554,6 +1703,24 @@ export default function QuestoesPage() {
1554
1703
  )}
1555
1704
  />
1556
1705
 
1706
+ <FormField
1707
+ control={form.control}
1708
+ name="explicacao"
1709
+ render={({ field }) => (
1710
+ <FormItem>
1711
+ <FormLabel>{t('sheet.fields.explanation')}</FormLabel>
1712
+ <FormControl>
1713
+ <RichTextEditor
1714
+ className="w-full max-w-full h-40"
1715
+ value={field.value ?? ''}
1716
+ onChange={field.onChange}
1717
+ />
1718
+ </FormControl>
1719
+ <FormMessage />
1720
+ </FormItem>
1721
+ )}
1722
+ />
1723
+
1557
1724
  <FormField
1558
1725
  control={form.control}
1559
1726
  name="pontuacao"
@@ -82,7 +82,7 @@ import {
82
82
  } from 'lucide-react';
83
83
  import { useTranslations } from 'next-intl';
84
84
  import { useRouter } from 'next/navigation';
85
- import { useEffect, useMemo, useRef, useState } from 'react';
85
+ import { useEffect, useMemo, useState } from 'react';
86
86
  import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
87
87
  import { Controller, useForm, useWatch } from 'react-hook-form';
88
88
  import { toast } from 'sonner';
@@ -94,9 +94,11 @@ interface Exame {
94
94
  id: number;
95
95
  codigo: string;
96
96
  titulo: string;
97
+ nivel: 'beginner' | 'intermediate' | 'advanced';
97
98
  notaMinima: number;
98
99
  limiteTempo: number;
99
100
  shuffle: boolean;
101
+ mostrarGabaritoAposConclusao: boolean;
100
102
  status: 'publicado' | 'rascunho' | 'encerrado';
101
103
  questoes: number;
102
104
  realizacoes: number;
@@ -110,9 +112,11 @@ type ApiExame = {
110
112
  id: number;
111
113
  code: string;
112
114
  title: string;
115
+ level: 'beginner' | 'intermediate' | 'advanced';
113
116
  minScore: number;
114
117
  timeLimit: number;
115
118
  shuffle: boolean;
119
+ showAnswerKeyAfterFinish: boolean;
116
120
  status: 'published' | 'draft' | 'closed' | 'archived';
117
121
  questions: number;
118
122
  attempts: number;
@@ -153,12 +157,14 @@ const API_EXAMS_CACHE_KEY = 'lms:exams:api-cache';
153
157
  const createExameSchema = (t: (key: string) => string) =>
154
158
  z.object({
155
159
  titulo: z.string().min(3, t('form.validation.titleMinLength')),
160
+ nivel: z.enum(['beginner', 'intermediate', 'advanced']),
156
161
  notaMinima: z.coerce
157
162
  .number()
158
163
  .min(0, t('form.validation.minScoreMin'))
159
164
  .max(10, t('form.validation.minScoreMax')),
160
165
  limiteTempo: z.coerce.number().min(1, t('form.validation.timeLimitMin')),
161
166
  shuffle: z.boolean().default(false),
167
+ mostrarGabaritoAposConclusao: z.boolean().default(false),
162
168
  status: z.string().min(1, t('form.validation.statusRequired')),
163
169
  primaryColor: z
164
170
  .string()
@@ -190,9 +196,11 @@ function mapApiExame(exame: ApiExame): Exame {
190
196
  id: exame.id,
191
197
  codigo: exame.code,
192
198
  titulo: exame.title,
199
+ nivel: exame.level,
193
200
  notaMinima: exame.minScore,
194
201
  limiteTempo: exame.timeLimit,
195
202
  shuffle: exame.shuffle,
203
+ mostrarGabaritoAposConclusao: Boolean(exame.showAnswerKeyAfterFinish),
196
204
  status: toPtStatus(exame.status),
197
205
  questoes: exame.questions,
198
206
  realizacoes: exame.attempts,
@@ -268,18 +276,15 @@ export default function ExamesPage() {
268
276
  allowedValues: PAGE_SIZES,
269
277
  });
270
278
 
271
- // Double-click tracking
272
- const clickTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(
273
- new Map()
274
- );
275
-
276
279
  const form = useForm<ExameForm>({
277
280
  resolver: zodResolver(exameSchema),
278
281
  defaultValues: {
279
282
  titulo: '',
283
+ nivel: 'beginner',
280
284
  notaMinima: 7,
281
285
  limiteTempo: 60,
282
286
  shuffle: true,
287
+ mostrarGabaritoAposConclusao: false,
283
288
  status: 'rascunho',
284
289
  primaryColor: '#1D4ED8',
285
290
  secondaryColor: '#111827',
@@ -365,11 +370,18 @@ export default function ExamesPage() {
365
370
  watchedFormValues.notaMinima,
366
371
  editingExame.notaMinima
367
372
  ),
373
+ nivel:
374
+ watchedFormValues.nivel === undefined
375
+ ? editingExame.nivel
376
+ : watchedFormValues.nivel,
368
377
  limiteTempo: toNumberOrFallback(
369
378
  watchedFormValues.limiteTempo,
370
379
  editingExame.limiteTempo
371
380
  ),
372
381
  shuffle: watchedFormValues.shuffle ?? editingExame.shuffle,
382
+ mostrarGabaritoAposConclusao:
383
+ watchedFormValues.mostrarGabaritoAposConclusao ??
384
+ editingExame.mostrarGabaritoAposConclusao,
373
385
  status:
374
386
  (watchedFormValues.status as Exame['status'] | undefined) ??
375
387
  editingExame.status,
@@ -421,29 +433,17 @@ export default function ExamesPage() {
421
433
  setDeleteDialogOpen(true);
422
434
  }
423
435
 
424
- // ── Double-click ──────────────────────────────────────────────────────────
425
-
426
- function handleCardClick(exame: Exame) {
427
- const existing = clickTimers.current.get(exame.id);
428
- if (existing) {
429
- clearTimeout(existing);
430
- clickTimers.current.delete(exame.id);
431
- router.push(`/lms/exams/${exame.id}/questions`);
432
- } else {
433
- const t = setTimeout(() => clickTimers.current.delete(exame.id), 300);
434
- clickTimers.current.set(exame.id, t);
435
- }
436
- }
437
-
438
436
  // ── CRUD ──────────────────────────────────────────────────────────────────
439
437
 
440
438
  function openCreateSheet() {
441
439
  setEditingExame(null);
442
440
  form.reset({
443
441
  titulo: '',
442
+ nivel: 'beginner',
444
443
  notaMinima: 7,
445
444
  limiteTempo: 60,
446
445
  shuffle: true,
446
+ mostrarGabaritoAposConclusao: false,
447
447
  status: 'rascunho',
448
448
  primaryColor: '#1D4ED8',
449
449
  secondaryColor: '#111827',
@@ -456,9 +456,11 @@ export default function ExamesPage() {
456
456
  setEditingExame(exame);
457
457
  form.reset({
458
458
  titulo: exame.titulo,
459
+ nivel: exame.nivel,
459
460
  notaMinima: exame.notaMinima,
460
461
  limiteTempo: exame.limiteTempo,
461
462
  shuffle: exame.shuffle,
463
+ mostrarGabaritoAposConclusao: exame.mostrarGabaritoAposConclusao,
462
464
  status: exame.status,
463
465
  primaryColor: exame.primaryColor ?? '#1D4ED8',
464
466
  secondaryColor: exame.secondaryColor ?? '#111827',
@@ -471,9 +473,11 @@ export default function ExamesPage() {
471
473
  try {
472
474
  const payload = {
473
475
  title: data.titulo,
476
+ level: data.nivel,
474
477
  minScore: data.notaMinima,
475
478
  timeLimit: data.limiteTempo,
476
479
  shuffle: data.shuffle,
480
+ showAnswerKeyAfterFinish: data.mostrarGabaritoAposConclusao,
477
481
  status: toApiStatus(data.status),
478
482
  primaryColor: data.primaryColor,
479
483
  secondaryColor: data.secondaryColor,
@@ -783,7 +787,7 @@ export default function ExamesPage() {
783
787
  <motion.div key={exame.id} variants={fadeUp}>
784
788
  <Card
785
789
  className="group relative h-95 cursor-pointer overflow-hidden border-border/70 shadow-sm transition-all duration-200 hover:border-border hover:shadow-md"
786
- onClick={() => handleCardClick(exame)}
790
+ onDoubleClick={() => openEditSheet(exame)}
787
791
  title={t('cards.tooltip')}
788
792
  >
789
793
  <div
@@ -962,7 +966,7 @@ export default function ExamesPage() {
962
966
  <TableRow
963
967
  key={exame.id}
964
968
  className="cursor-pointer"
965
- onClick={() => handleCardClick(exame)}
969
+ onDoubleClick={() => openEditSheet(exame)}
966
970
  title={t('cards.tooltip')}
967
971
  >
968
972
  <TableCell>
@@ -1114,6 +1118,37 @@ export default function ExamesPage() {
1114
1118
  <FieldError>{form.formState.errors.titulo?.message}</FieldError>
1115
1119
  </Field>
1116
1120
  <div className="grid grid-cols-2 gap-4">
1121
+ <Field>
1122
+ <FieldLabel>
1123
+ {t('form.fields.level.label')}{' '}
1124
+ <span className="text-destructive">*</span>
1125
+ </FieldLabel>
1126
+ <Controller
1127
+ name="nivel"
1128
+ control={form.control}
1129
+ render={({ field }) => (
1130
+ <Select onValueChange={field.onChange} value={field.value}>
1131
+ <SelectTrigger>
1132
+ <SelectValue
1133
+ placeholder={t('form.fields.level.placeholder')}
1134
+ />
1135
+ </SelectTrigger>
1136
+ <SelectContent>
1137
+ <SelectItem value="beginner">
1138
+ {t('form.fields.level.options.beginner')}
1139
+ </SelectItem>
1140
+ <SelectItem value="intermediate">
1141
+ {t('form.fields.level.options.intermediate')}
1142
+ </SelectItem>
1143
+ <SelectItem value="advanced">
1144
+ {t('form.fields.level.options.advanced')}
1145
+ </SelectItem>
1146
+ </SelectContent>
1147
+ </Select>
1148
+ )}
1149
+ />
1150
+ <FieldError>{form.formState.errors.nivel?.message}</FieldError>
1151
+ </Field>
1117
1152
  <Field>
1118
1153
  <FieldLabel htmlFor="notaMinima">
1119
1154
  {t('form.fields.minScore.label')}{' '}
@@ -1261,6 +1296,26 @@ export default function ExamesPage() {
1261
1296
  )}
1262
1297
  />
1263
1298
  </label>
1299
+ <label className="flex cursor-pointer items-center justify-between rounded-lg border p-3 hover:bg-muted">
1300
+ <div>
1301
+ <p className="text-sm font-medium">
1302
+ {t('form.fields.showAnswerKeyAfterFinish.label')}
1303
+ </p>
1304
+ <p className="text-xs text-muted-foreground">
1305
+ {t('form.fields.showAnswerKeyAfterFinish.description')}
1306
+ </p>
1307
+ </div>
1308
+ <Controller
1309
+ name="mostrarGabaritoAposConclusao"
1310
+ control={form.control}
1311
+ render={({ field }) => (
1312
+ <Switch
1313
+ checked={field.value}
1314
+ onCheckedChange={field.onChange}
1315
+ />
1316
+ )}
1317
+ />
1318
+ </label>
1264
1319
  <SheetFooter className="mt-auto shrink-0 gap-2 pt-4 px-0">
1265
1320
  <Button type="submit" disabled={saving} className="gap-2">
1266
1321
  {saving && <Loader2 className="size-4 animate-spin" />}
@@ -476,6 +476,7 @@ export default function InstructorSkillsPage() {
476
476
  setPageSize(nextPageSize);
477
477
  setPage(1);
478
478
  }}
479
+ pageSizeOptions={[6, 12, 24]}
479
480
  />
480
481
 
481
482
  <SkillFormSheet
@@ -735,6 +735,7 @@ export default function InstructorsPage() {
735
735
  setPageSize(nextPageSize);
736
736
  setPage(1);
737
737
  }}
738
+ pageSizeOptions={[6, 12, 24]}
738
739
  />
739
740
 
740
741
  <InstructorFormSheet
@@ -591,11 +591,6 @@ export default function TrainingPage() {
591
591
  allowedValues: PAGE_SIZES,
592
592
  });
593
593
 
594
- // Double-click tracking
595
- const clickTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(
596
- new Map()
597
- );
598
-
599
594
  const sensors = useSensors(
600
595
  useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
601
596
  useSensor(KeyboardSensor, {
@@ -961,23 +956,6 @@ export default function TrainingPage() {
961
956
  setDeleteDialogOpen(true);
962
957
  }
963
958
 
964
- // ── Double-click ──────────────────────────────────────────────────────────
965
-
966
- function handleCardClick(formacao: Formacao) {
967
- const existing = clickTimers.current.get(formacao.id);
968
- if (existing) {
969
- clearTimeout(existing);
970
- clickTimers.current.delete(formacao.id);
971
- // Toast message for opening would go here if integrated
972
- } else {
973
- const timer = setTimeout(
974
- () => clickTimers.current.delete(formacao.id),
975
- 300
976
- );
977
- clickTimers.current.set(formacao.id, timer);
978
- }
979
- }
980
-
981
959
  // ── CRUD ──────────────────────────────────────────────────────────────────
982
960
 
983
961
  function openCreateSheet() {
@@ -1318,11 +1296,15 @@ export default function TrainingPage() {
1318
1296
  data.tituloComercial || data.nomeInterno
1319
1297
  ),
1320
1298
  slug,
1299
+ name: data.nomeInterno.trim() || undefined,
1321
1300
  title: data.tituloComercial,
1322
1301
  description: data.descricao,
1323
1302
  level: ptLevelToApi(data.nivel),
1324
1303
  status: courseStatusToApi(data.status),
1325
1304
  categorySlugs,
1305
+ operationsProjectId: data.operationsProjectId
1306
+ ? Number(data.operationsProjectId)
1307
+ : null,
1326
1308
  },
1327
1309
  });
1328
1310
 
@@ -1664,7 +1646,7 @@ export default function TrainingPage() {
1664
1646
  <motion.div key={formacao.id} variants={fadeUp}>
1665
1647
  <Card
1666
1648
  className="group relative flex min-h-60 max-h-67.5 cursor-pointer flex-col overflow-hidden border-border/70 py-0 shadow-sm transition-all duration-200 hover:border-border hover:shadow-md"
1667
- onClick={() => handleCardClick(formacao)}
1649
+ onDoubleClick={() => openEditSheet(formacao)}
1668
1650
  title={t('cards.tooltip')}
1669
1651
  >
1670
1652
  <div
@@ -1806,7 +1788,7 @@ export default function TrainingPage() {
1806
1788
  <TableRow
1807
1789
  key={formacao.id}
1808
1790
  className="cursor-pointer"
1809
- onClick={() => handleCardClick(formacao)}
1791
+ onDoubleClick={() => openEditSheet(formacao)}
1810
1792
  title={t('cards.tooltip')}
1811
1793
  >
1812
1794
  <TableCell>