@hed-hog/lms 0.0.304 → 0.0.306

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 (458) hide show
  1. package/README.md +413 -401
  2. package/dist/certificate/certificate.controller.d.ts +90 -0
  3. package/dist/certificate/certificate.controller.d.ts.map +1 -0
  4. package/dist/certificate/certificate.controller.js +121 -0
  5. package/dist/certificate/certificate.controller.js.map +1 -0
  6. package/dist/certificate/certificate.module.d.ts +3 -0
  7. package/dist/certificate/certificate.module.d.ts.map +1 -0
  8. package/dist/certificate/certificate.module.js +26 -0
  9. package/dist/certificate/certificate.module.js.map +1 -0
  10. package/dist/certificate/certificate.service.d.ts +115 -0
  11. package/dist/certificate/certificate.service.d.ts.map +1 -0
  12. package/dist/certificate/certificate.service.js +343 -0
  13. package/dist/certificate/certificate.service.js.map +1 -0
  14. package/dist/certificate/dto/create-certificate-template.dto.d.ts +8 -0
  15. package/dist/certificate/dto/create-certificate-template.dto.d.ts.map +1 -0
  16. package/dist/certificate/dto/create-certificate-template.dto.js +44 -0
  17. package/dist/certificate/dto/create-certificate-template.dto.js.map +1 -0
  18. package/dist/certificate/dto/update-certificate-template.dto.d.ts +6 -0
  19. package/dist/certificate/dto/update-certificate-template.dto.d.ts.map +1 -0
  20. package/dist/certificate/dto/update-certificate-template.dto.js +9 -0
  21. package/dist/certificate/dto/update-certificate-template.dto.js.map +1 -0
  22. package/dist/class-group/class-group.controller.d.ts +305 -0
  23. package/dist/class-group/class-group.controller.d.ts.map +1 -0
  24. package/dist/class-group/class-group.controller.js +257 -0
  25. package/dist/class-group/class-group.controller.js.map +1 -0
  26. package/dist/class-group/class-group.module.d.ts +3 -0
  27. package/dist/class-group/class-group.module.d.ts.map +1 -0
  28. package/dist/class-group/class-group.module.js +25 -0
  29. package/dist/class-group/class-group.module.js.map +1 -0
  30. package/dist/class-group/class-group.service.d.ts +354 -0
  31. package/dist/class-group/class-group.service.d.ts.map +1 -0
  32. package/dist/class-group/class-group.service.js +1356 -0
  33. package/dist/class-group/class-group.service.js.map +1 -0
  34. package/dist/class-group/dto/create-class-group.dto.d.ts +33 -0
  35. package/dist/class-group/dto/create-class-group.dto.d.ts.map +1 -0
  36. package/dist/class-group/dto/create-class-group.dto.js +165 -0
  37. package/dist/class-group/dto/create-class-group.dto.js.map +1 -0
  38. package/dist/class-group/dto/create-session.dto.d.ts +22 -0
  39. package/dist/class-group/dto/create-session.dto.d.ts.map +1 -0
  40. package/dist/class-group/dto/create-session.dto.js +117 -0
  41. package/dist/class-group/dto/create-session.dto.js.map +1 -0
  42. package/dist/class-group/dto/enrollment.dto.d.ts +22 -0
  43. package/dist/class-group/dto/enrollment.dto.d.ts.map +1 -0
  44. package/dist/class-group/dto/enrollment.dto.js +89 -0
  45. package/dist/class-group/dto/enrollment.dto.js.map +1 -0
  46. package/dist/class-group/dto/update-class-group.dto.d.ts +6 -0
  47. package/dist/class-group/dto/update-class-group.dto.d.ts.map +1 -0
  48. package/dist/class-group/dto/update-class-group.dto.js +9 -0
  49. package/dist/class-group/dto/update-class-group.dto.js.map +1 -0
  50. package/dist/class-group/dto/update-session.dto.d.ts +7 -0
  51. package/dist/class-group/dto/update-session.dto.d.ts.map +1 -0
  52. package/dist/class-group/dto/update-session.dto.js +24 -0
  53. package/dist/class-group/dto/update-session.dto.js.map +1 -0
  54. package/dist/course/course-structure.controller.d.ts +127 -0
  55. package/dist/course/course-structure.controller.d.ts.map +1 -0
  56. package/dist/course/course-structure.controller.js +115 -0
  57. package/dist/course/course-structure.controller.js.map +1 -0
  58. package/dist/course/course-structure.service.d.ts +142 -0
  59. package/dist/course/course-structure.service.d.ts.map +1 -0
  60. package/dist/course/course-structure.service.js +445 -0
  61. package/dist/course/course-structure.service.js.map +1 -0
  62. package/dist/course/course.controller.d.ts +195 -0
  63. package/dist/course/course.controller.d.ts.map +1 -0
  64. package/dist/course/course.controller.js +104 -0
  65. package/dist/course/course.controller.js.map +1 -0
  66. package/dist/course/course.module.d.ts +3 -0
  67. package/dist/course/course.module.d.ts.map +1 -0
  68. package/dist/course/course.module.js +28 -0
  69. package/dist/course/course.module.js.map +1 -0
  70. package/dist/course/course.service.d.ts +215 -0
  71. package/dist/course/course.service.d.ts.map +1 -0
  72. package/dist/course/course.service.js +743 -0
  73. package/dist/course/course.service.js.map +1 -0
  74. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +24 -0
  75. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -0
  76. package/dist/course/dto/create-course-structure-lesson.dto.js +118 -0
  77. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -0
  78. package/dist/course/dto/create-course-structure-session.dto.d.ts +7 -0
  79. package/dist/course/dto/create-course-structure-session.dto.d.ts.map +1 -0
  80. package/dist/course/dto/create-course-structure-session.dto.js +40 -0
  81. package/dist/course/dto/create-course-structure-session.dto.js.map +1 -0
  82. package/dist/course/dto/create-course.dto.d.ts +26 -0
  83. package/dist/course/dto/create-course.dto.d.ts.map +1 -0
  84. package/dist/course/dto/create-course.dto.js +138 -0
  85. package/dist/course/dto/create-course.dto.js.map +1 -0
  86. package/dist/course/dto/update-course-structure-lesson.dto.d.ts +6 -0
  87. package/dist/course/dto/update-course-structure-lesson.dto.d.ts.map +1 -0
  88. package/dist/course/dto/update-course-structure-lesson.dto.js +9 -0
  89. package/dist/course/dto/update-course-structure-lesson.dto.js.map +1 -0
  90. package/dist/course/dto/update-course-structure-session.dto.d.ts +6 -0
  91. package/dist/course/dto/update-course-structure-session.dto.d.ts.map +1 -0
  92. package/dist/course/dto/update-course-structure-session.dto.js +9 -0
  93. package/dist/course/dto/update-course-structure-session.dto.js.map +1 -0
  94. package/dist/course/dto/update-course.dto.d.ts +6 -0
  95. package/dist/course/dto/update-course.dto.d.ts.map +1 -0
  96. package/dist/course/dto/update-course.dto.js +9 -0
  97. package/dist/course/dto/update-course.dto.js.map +1 -0
  98. package/dist/dashboard/dashboard.controller.d.ts +101 -0
  99. package/dist/dashboard/dashboard.controller.d.ts.map +1 -0
  100. package/dist/dashboard/dashboard.controller.js +40 -0
  101. package/dist/dashboard/dashboard.controller.js.map +1 -0
  102. package/dist/dashboard/dashboard.module.d.ts +3 -0
  103. package/dist/dashboard/dashboard.module.d.ts.map +1 -0
  104. package/dist/dashboard/dashboard.module.js +25 -0
  105. package/dist/dashboard/dashboard.module.js.map +1 -0
  106. package/dist/dashboard/dashboard.service.d.ts +130 -0
  107. package/dist/dashboard/dashboard.service.d.ts.map +1 -0
  108. package/dist/dashboard/dashboard.service.js +626 -0
  109. package/dist/dashboard/dashboard.service.js.map +1 -0
  110. package/dist/enterprise/dto/add-enterprise-class-group.dto.d.ts +4 -0
  111. package/dist/enterprise/dto/add-enterprise-class-group.dto.d.ts.map +1 -0
  112. package/dist/enterprise/dto/add-enterprise-class-group.dto.js +22 -0
  113. package/dist/enterprise/dto/add-enterprise-class-group.dto.js.map +1 -0
  114. package/dist/enterprise/dto/add-enterprise-course.dto.d.ts +5 -0
  115. package/dist/enterprise/dto/add-enterprise-course.dto.d.ts.map +1 -0
  116. package/dist/enterprise/dto/add-enterprise-course.dto.js +27 -0
  117. package/dist/enterprise/dto/add-enterprise-course.dto.js.map +1 -0
  118. package/dist/enterprise/dto/add-enterprise-student.dto.d.ts +5 -0
  119. package/dist/enterprise/dto/add-enterprise-student.dto.d.ts.map +1 -0
  120. package/dist/enterprise/dto/add-enterprise-student.dto.js +27 -0
  121. package/dist/enterprise/dto/add-enterprise-student.dto.js.map +1 -0
  122. package/dist/enterprise/dto/add-enterprise-user.dto.d.ts +7 -0
  123. package/dist/enterprise/dto/add-enterprise-user.dto.d.ts.map +1 -0
  124. package/dist/enterprise/dto/add-enterprise-user.dto.js +36 -0
  125. package/dist/enterprise/dto/add-enterprise-user.dto.js.map +1 -0
  126. package/dist/enterprise/dto/create-enterprise.dto.d.ts +10 -0
  127. package/dist/enterprise/dto/create-enterprise.dto.d.ts.map +1 -0
  128. package/dist/enterprise/dto/create-enterprise.dto.js +54 -0
  129. package/dist/enterprise/dto/create-enterprise.dto.js.map +1 -0
  130. package/dist/enterprise/dto/update-enterprise-student.dto.d.ts +4 -0
  131. package/dist/enterprise/dto/update-enterprise-student.dto.d.ts.map +1 -0
  132. package/dist/enterprise/dto/update-enterprise-student.dto.js +22 -0
  133. package/dist/enterprise/dto/update-enterprise-student.dto.js.map +1 -0
  134. package/dist/enterprise/dto/update-enterprise-user.dto.d.ts +5 -0
  135. package/dist/enterprise/dto/update-enterprise-user.dto.d.ts.map +1 -0
  136. package/dist/enterprise/dto/update-enterprise-user.dto.js +27 -0
  137. package/dist/enterprise/dto/update-enterprise-user.dto.js.map +1 -0
  138. package/dist/enterprise/dto/update-enterprise.dto.d.ts +6 -0
  139. package/dist/enterprise/dto/update-enterprise.dto.d.ts.map +1 -0
  140. package/dist/enterprise/dto/update-enterprise.dto.js +9 -0
  141. package/dist/enterprise/dto/update-enterprise.dto.js.map +1 -0
  142. package/dist/enterprise/enterprise.controller.d.ts +269 -0
  143. package/dist/enterprise/enterprise.controller.d.ts.map +1 -0
  144. package/dist/enterprise/enterprise.controller.js +311 -0
  145. package/dist/enterprise/enterprise.controller.js.map +1 -0
  146. package/dist/enterprise/enterprise.module.d.ts +3 -0
  147. package/dist/enterprise/enterprise.module.d.ts.map +1 -0
  148. package/dist/enterprise/enterprise.module.js +25 -0
  149. package/dist/enterprise/enterprise.module.js.map +1 -0
  150. package/dist/enterprise/enterprise.service.d.ts +282 -0
  151. package/dist/enterprise/enterprise.service.d.ts.map +1 -0
  152. package/dist/enterprise/enterprise.service.js +627 -0
  153. package/dist/enterprise/enterprise.service.js.map +1 -0
  154. package/dist/evaluation/evaluation.controller.d.ts +56 -0
  155. package/dist/evaluation/evaluation.controller.d.ts.map +1 -0
  156. package/dist/evaluation/evaluation.controller.js +76 -0
  157. package/dist/evaluation/evaluation.controller.js.map +1 -0
  158. package/dist/evaluation/evaluation.module.d.ts +3 -0
  159. package/dist/evaluation/evaluation.module.d.ts.map +1 -0
  160. package/dist/evaluation/evaluation.module.js +25 -0
  161. package/dist/evaluation/evaluation.module.js.map +1 -0
  162. package/dist/evaluation/evaluation.service.d.ts +67 -0
  163. package/dist/evaluation/evaluation.service.d.ts.map +1 -0
  164. package/dist/evaluation/evaluation.service.js +378 -0
  165. package/dist/evaluation/evaluation.service.js.map +1 -0
  166. package/dist/exam/dto/create-exam-question.dto.d.ts +25 -0
  167. package/dist/exam/dto/create-exam-question.dto.d.ts.map +1 -0
  168. package/dist/exam/dto/create-exam-question.dto.js +117 -0
  169. package/dist/exam/dto/create-exam-question.dto.js.map +1 -0
  170. package/dist/exam/dto/create-exam.dto.d.ts +11 -0
  171. package/dist/exam/dto/create-exam.dto.d.ts.map +1 -0
  172. package/dist/exam/dto/create-exam.dto.js +63 -0
  173. package/dist/exam/dto/create-exam.dto.js.map +1 -0
  174. package/dist/exam/dto/reorder-exam-questions.dto.d.ts +4 -0
  175. package/dist/exam/dto/reorder-exam-questions.dto.d.ts.map +1 -0
  176. package/dist/exam/dto/reorder-exam-questions.dto.js +23 -0
  177. package/dist/exam/dto/reorder-exam-questions.dto.js.map +1 -0
  178. package/dist/exam/dto/save-exam-attempt-answers.dto.d.ts +14 -0
  179. package/dist/exam/dto/save-exam-attempt-answers.dto.d.ts.map +1 -0
  180. package/dist/exam/dto/save-exam-attempt-answers.dto.js +68 -0
  181. package/dist/exam/dto/save-exam-attempt-answers.dto.js.map +1 -0
  182. package/dist/exam/dto/start-exam-attempt.dto.d.ts +4 -0
  183. package/dist/exam/dto/start-exam-attempt.dto.d.ts.map +1 -0
  184. package/dist/exam/dto/start-exam-attempt.dto.js +23 -0
  185. package/dist/exam/dto/start-exam-attempt.dto.js.map +1 -0
  186. package/dist/exam/dto/submit-exam-attempt.dto.d.ts +5 -0
  187. package/dist/exam/dto/submit-exam-attempt.dto.d.ts.map +1 -0
  188. package/dist/exam/dto/submit-exam-attempt.dto.js +23 -0
  189. package/dist/exam/dto/submit-exam-attempt.dto.js.map +1 -0
  190. package/dist/exam/dto/update-exam-question.dto.d.ts +6 -0
  191. package/dist/exam/dto/update-exam-question.dto.d.ts.map +1 -0
  192. package/dist/exam/dto/update-exam-question.dto.js +9 -0
  193. package/dist/exam/dto/update-exam-question.dto.js.map +1 -0
  194. package/dist/exam/dto/update-exam.dto.d.ts +6 -0
  195. package/dist/exam/dto/update-exam.dto.d.ts.map +1 -0
  196. package/dist/exam/dto/update-exam.dto.js +9 -0
  197. package/dist/exam/dto/update-exam.dto.js.map +1 -0
  198. package/dist/exam/exam-attempt.controller.d.ts +273 -0
  199. package/dist/exam/exam-attempt.controller.d.ts.map +1 -0
  200. package/dist/exam/exam-attempt.controller.js +84 -0
  201. package/dist/exam/exam-attempt.controller.js.map +1 -0
  202. package/dist/exam/exam-attempt.service.d.ts +302 -0
  203. package/dist/exam/exam-attempt.service.d.ts.map +1 -0
  204. package/dist/exam/exam-attempt.service.js +776 -0
  205. package/dist/exam/exam-attempt.service.js.map +1 -0
  206. package/dist/exam/exam.controller.d.ts +162 -0
  207. package/dist/exam/exam.controller.d.ts.map +1 -0
  208. package/dist/exam/exam.controller.js +158 -0
  209. package/dist/exam/exam.controller.js.map +1 -0
  210. package/dist/exam/exam.module.d.ts +3 -0
  211. package/dist/exam/exam.module.d.ts.map +1 -0
  212. package/dist/exam/exam.module.js +27 -0
  213. package/dist/exam/exam.module.js.map +1 -0
  214. package/dist/exam/exam.service.d.ts +179 -0
  215. package/dist/exam/exam.service.d.ts.map +1 -0
  216. package/dist/exam/exam.service.js +597 -0
  217. package/dist/exam/exam.service.js.map +1 -0
  218. package/dist/index.d.ts +28 -0
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +28 -0
  221. package/dist/index.js.map +1 -1
  222. package/dist/instructor/dto/create-instructor.dto.d.ts +10 -0
  223. package/dist/instructor/dto/create-instructor.dto.d.ts.map +1 -0
  224. package/dist/instructor/dto/create-instructor.dto.js +55 -0
  225. package/dist/instructor/dto/create-instructor.dto.js.map +1 -0
  226. package/dist/instructor/dto/update-instructor.dto.d.ts +9 -0
  227. package/dist/instructor/dto/update-instructor.dto.d.ts.map +1 -0
  228. package/dist/instructor/dto/update-instructor.dto.js +51 -0
  229. package/dist/instructor/dto/update-instructor.dto.js.map +1 -0
  230. package/dist/instructor/instructor.controller.d.ts +52 -0
  231. package/dist/instructor/instructor.controller.d.ts.map +1 -0
  232. package/dist/instructor/instructor.controller.js +98 -0
  233. package/dist/instructor/instructor.controller.js.map +1 -0
  234. package/dist/instructor/instructor.module.d.ts +3 -0
  235. package/dist/instructor/instructor.module.d.ts.map +1 -0
  236. package/dist/instructor/instructor.module.js +25 -0
  237. package/dist/instructor/instructor.module.js.map +1 -0
  238. package/dist/instructor/instructor.service.d.ts +79 -0
  239. package/dist/instructor/instructor.service.d.ts.map +1 -0
  240. package/dist/instructor/instructor.service.js +528 -0
  241. package/dist/instructor/instructor.service.js.map +1 -0
  242. package/dist/lms.module.d.ts.map +1 -1
  243. package/dist/lms.module.js +36 -4
  244. package/dist/lms.module.js.map +1 -1
  245. package/dist/reports/reports.controller.d.ts +69 -0
  246. package/dist/reports/reports.controller.d.ts.map +1 -0
  247. package/dist/reports/reports.controller.js +40 -0
  248. package/dist/reports/reports.controller.js.map +1 -0
  249. package/dist/reports/reports.module.d.ts +3 -0
  250. package/dist/reports/reports.module.d.ts.map +1 -0
  251. package/dist/reports/reports.module.js +25 -0
  252. package/dist/reports/reports.module.js.map +1 -0
  253. package/dist/reports/reports.service.d.ts +80 -0
  254. package/dist/reports/reports.service.d.ts.map +1 -0
  255. package/dist/reports/reports.service.js +366 -0
  256. package/dist/reports/reports.service.js.map +1 -0
  257. package/dist/training/dto/create-training.dto.d.ts +19 -0
  258. package/dist/training/dto/create-training.dto.d.ts.map +1 -0
  259. package/dist/training/dto/create-training.dto.js +98 -0
  260. package/dist/training/dto/create-training.dto.js.map +1 -0
  261. package/dist/training/dto/update-training.dto.d.ts +6 -0
  262. package/dist/training/dto/update-training.dto.d.ts.map +1 -0
  263. package/dist/training/dto/update-training.dto.js +9 -0
  264. package/dist/training/dto/update-training.dto.js.map +1 -0
  265. package/dist/training/training.controller.d.ts +195 -0
  266. package/dist/training/training.controller.d.ts.map +1 -0
  267. package/dist/training/training.controller.js +104 -0
  268. package/dist/training/training.controller.js.map +1 -0
  269. package/dist/training/training.module.d.ts +3 -0
  270. package/dist/training/training.module.d.ts.map +1 -0
  271. package/dist/training/training.module.js +25 -0
  272. package/dist/training/training.module.js.map +1 -0
  273. package/dist/training/training.service.d.ts +213 -0
  274. package/dist/training/training.service.d.ts.map +1 -0
  275. package/dist/training/training.service.js +497 -0
  276. package/dist/training/training.service.js.map +1 -0
  277. package/hedhog/data/dashboard.yaml +6 -0
  278. package/hedhog/data/dashboard_component.yaml +153 -0
  279. package/hedhog/data/dashboard_component_role.yaml +97 -0
  280. package/hedhog/data/dashboard_item.yaml +167 -0
  281. package/hedhog/data/dashboard_role.yaml +6 -0
  282. package/hedhog/data/instructor_qualification.yaml +16 -0
  283. package/hedhog/data/menu.yaml +129 -19
  284. package/hedhog/data/role.yaml +25 -1
  285. package/hedhog/data/route.yaml +867 -0
  286. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +1992 -0
  287. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +480 -0
  288. package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +591 -0
  289. package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +164 -0
  290. package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +120 -0
  291. package/hedhog/frontend/app/_components/lms-class-calendar.tsx.ejs +272 -0
  292. package/hedhog/frontend/app/_components/mobile-calendar.tsx.ejs +277 -0
  293. package/hedhog/frontend/app/_lib/editor/canvasInstance.ts.ejs +48 -0
  294. package/hedhog/frontend/app/_lib/editor/pctHelpers.ts.ejs +50 -0
  295. package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +268 -0
  296. package/hedhog/frontend/app/_lib/editor/types.ts.ejs +94 -0
  297. package/hedhog/frontend/app/_lib/store/useTemplateStore.ts.ejs +284 -0
  298. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +638 -0
  299. package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +916 -0
  300. package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +200 -0
  301. package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +769 -0
  302. package/hedhog/frontend/app/certificates/models/TemplateEditorPage.tsx.ejs +104 -0
  303. package/hedhog/frontend/app/certificates/models/TopBar.tsx.ejs +354 -0
  304. package/hedhog/frontend/app/certificates/models/editor/page.tsx.ejs +5 -0
  305. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +883 -0
  306. package/hedhog/frontend/app/classes/[id]/_components/event-summary-popover.tsx.ejs +279 -0
  307. package/hedhog/frontend/app/classes/[id]/_components/quick-create-session-popover.tsx.ejs +1027 -0
  308. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +3130 -993
  309. package/hedhog/frontend/app/classes/page.tsx.ejs +2731 -759
  310. package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +80 -0
  311. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +226 -0
  312. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +71 -0
  313. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +42 -0
  314. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +111 -0
  315. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +113 -0
  316. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +215 -0
  317. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +236 -0
  318. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +141 -0
  319. package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +57 -0
  320. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +60 -0
  321. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +33 -0
  322. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +933 -1103
  323. package/hedhog/frontend/app/courses/[id]/structure/page.tsx.ejs +699 -117
  324. package/hedhog/frontend/app/courses/page.tsx.ejs +1018 -1042
  325. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +317 -0
  326. package/hedhog/frontend/app/enterprise/_components/enterprise-activity-panel.tsx.ejs +88 -0
  327. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +318 -0
  328. package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +332 -0
  329. package/hedhog/frontend/app/enterprise/_components/enterprise-class-create-sheet.tsx.ejs +58 -0
  330. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +390 -0
  331. package/hedhog/frontend/app/enterprise/_components/enterprise-company-identity-card.tsx.ejs +112 -0
  332. package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +183 -0
  333. package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +363 -0
  334. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-constants.ts.ejs +88 -0
  335. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +548 -0
  336. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-utils.ts.ejs +33 -0
  337. package/hedhog/frontend/app/enterprise/_components/enterprise-mocks.ts.ejs +277 -0
  338. package/hedhog/frontend/app/enterprise/_components/enterprise-person-picker.ts.ejs +31 -0
  339. package/hedhog/frontend/app/enterprise/_components/enterprise-progress-bar.tsx.ejs +21 -0
  340. package/hedhog/frontend/app/enterprise/_components/enterprise-related-tab.tsx.ejs +224 -0
  341. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +397 -0
  342. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +167 -0
  343. package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +267 -0
  344. package/hedhog/frontend/app/enterprise/_components/enterprise-system-user-picker.ts.ejs +42 -0
  345. package/hedhog/frontend/app/enterprise/_components/enterprise-types.ts.ejs +96 -0
  346. package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +207 -0
  347. package/hedhog/frontend/app/enterprise/_components/enterprise-user-distribution-chart.tsx.ejs +149 -0
  348. package/hedhog/frontend/app/enterprise/page.tsx.ejs +596 -0
  349. package/hedhog/frontend/app/evaluations/page.tsx.ejs +1250 -0
  350. package/hedhog/frontend/app/exams/[id]/attempt/page.tsx.ejs +642 -196
  351. package/hedhog/frontend/app/exams/[id]/page.tsx.ejs +11 -0
  352. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +1316 -436
  353. package/hedhog/frontend/app/exams/page.tsx.ejs +799 -546
  354. package/hedhog/frontend/app/layout.tsx.ejs +5 -0
  355. package/hedhog/frontend/app/page.tsx.ejs +3 -1220
  356. package/hedhog/frontend/app/reports/courses/page.tsx.ejs +843 -0
  357. package/hedhog/frontend/app/reports/dashboard/page.tsx.ejs +890 -0
  358. package/hedhog/frontend/app/reports/page.tsx.ejs +802 -808
  359. package/hedhog/frontend/app/reports/students/page.tsx.ejs +772 -0
  360. package/hedhog/frontend/app/training/page.tsx.ejs +1873 -628
  361. package/hedhog/frontend/messages/en.json +1606 -111
  362. package/hedhog/frontend/messages/pt.json +1636 -134
  363. package/hedhog/frontend/widgets/active-classes-kpi.tsx.ejs +74 -0
  364. package/hedhog/frontend/widgets/active-courses-kpi.tsx.ejs +74 -0
  365. package/hedhog/frontend/widgets/approval-rate-kpi.tsx.ejs +81 -0
  366. package/hedhog/frontend/widgets/category-distribution-chart.tsx.ejs +119 -0
  367. package/hedhog/frontend/widgets/class-calendar.tsx.ejs +440 -0
  368. package/hedhog/frontend/widgets/completion-rate-kpi.tsx.ejs +81 -0
  369. package/hedhog/frontend/widgets/engagement-chart.tsx.ejs +120 -0
  370. package/hedhog/frontend/widgets/footer-summary.tsx.ejs +80 -0
  371. package/hedhog/frontend/widgets/issued-certificates-kpi.tsx.ejs +74 -0
  372. package/hedhog/frontend/widgets/latest-enrollments.tsx.ejs +166 -0
  373. package/hedhog/frontend/widgets/student-growth-chart.tsx.ejs +89 -0
  374. package/hedhog/frontend/widgets/top-courses-chart.tsx.ejs +104 -0
  375. package/hedhog/frontend/widgets/total-students-kpi.tsx.ejs +78 -0
  376. package/hedhog/frontend/widgets/upcoming-classes.tsx.ejs +152 -0
  377. package/hedhog/table/course.yaml +19 -1
  378. package/hedhog/table/course_class_group.yaml +8 -0
  379. package/hedhog/table/course_class_session.yaml +33 -0
  380. package/hedhog/table/course_instructor.yaml +27 -0
  381. package/hedhog/table/enterprise.yaml +29 -0
  382. package/hedhog/table/enterprise_class_group.yaml +20 -0
  383. package/hedhog/table/enterprise_course.yaml +23 -0
  384. package/hedhog/table/enterprise_student.yaml +24 -0
  385. package/hedhog/table/enterprise_user.yaml +35 -0
  386. package/hedhog/table/instructor_qualification.yaml +26 -0
  387. package/hedhog/table/instructor_qualification_assignment.yaml +22 -0
  388. package/hedhog/table/question.yaml +6 -0
  389. package/package.json +6 -6
  390. package/src/certificate/certificate.controller.ts +83 -0
  391. package/src/certificate/certificate.module.ts +13 -0
  392. package/src/certificate/certificate.service.ts +413 -0
  393. package/src/certificate/dto/create-certificate-template.dto.ts +25 -0
  394. package/src/certificate/dto/update-certificate-template.dto.ts +6 -0
  395. package/src/class-group/class-group.controller.ts +189 -0
  396. package/src/class-group/class-group.module.ts +12 -0
  397. package/src/class-group/class-group.service.ts +1802 -0
  398. package/src/class-group/dto/create-class-group.dto.ts +139 -0
  399. package/src/class-group/dto/create-session.dto.ts +102 -0
  400. package/src/class-group/dto/enrollment.dto.ts +70 -0
  401. package/src/class-group/dto/update-class-group.dto.ts +4 -0
  402. package/src/class-group/dto/update-session.dto.ts +9 -0
  403. package/src/course/course-structure.controller.ts +85 -0
  404. package/src/course/course-structure.service.ts +525 -0
  405. package/src/course/course.controller.ts +69 -0
  406. package/src/course/course.module.ts +15 -0
  407. package/src/course/course.service.ts +920 -0
  408. package/src/course/dto/create-course-structure-lesson.dto.ts +97 -0
  409. package/src/course/dto/create-course-structure-session.dto.ts +22 -0
  410. package/src/course/dto/create-course.dto.ts +111 -0
  411. package/src/course/dto/update-course-structure-lesson.dto.ts +6 -0
  412. package/src/course/dto/update-course-structure-session.dto.ts +6 -0
  413. package/src/course/dto/update-course.dto.ts +4 -0
  414. package/src/dashboard/dashboard.controller.ts +14 -0
  415. package/src/dashboard/dashboard.module.ts +12 -0
  416. package/src/dashboard/dashboard.service.ts +726 -0
  417. package/src/enterprise/dto/add-enterprise-class-group.dto.ts +7 -0
  418. package/src/enterprise/dto/add-enterprise-course.dto.ts +11 -0
  419. package/src/enterprise/dto/add-enterprise-student.dto.ts +16 -0
  420. package/src/enterprise/dto/add-enterprise-user.dto.ts +23 -0
  421. package/src/enterprise/dto/create-enterprise.dto.ts +41 -0
  422. package/src/enterprise/dto/update-enterprise-student.dto.ts +7 -0
  423. package/src/enterprise/dto/update-enterprise-user.dto.ts +11 -0
  424. package/src/enterprise/dto/update-enterprise.dto.ts +4 -0
  425. package/src/enterprise/enterprise.controller.ts +233 -0
  426. package/src/enterprise/enterprise.module.ts +12 -0
  427. package/src/enterprise/enterprise.service.ts +712 -0
  428. package/src/evaluation/evaluation.controller.ts +44 -0
  429. package/src/evaluation/evaluation.module.ts +12 -0
  430. package/src/evaluation/evaluation.service.ts +394 -0
  431. package/src/exam/dto/create-exam-question.dto.ts +103 -0
  432. package/src/exam/dto/create-exam.dto.ts +41 -0
  433. package/src/exam/dto/reorder-exam-questions.dto.ts +8 -0
  434. package/src/exam/dto/save-exam-attempt-answers.dto.ts +55 -0
  435. package/src/exam/dto/start-exam-attempt.dto.ts +8 -0
  436. package/src/exam/dto/submit-exam-attempt.dto.ts +8 -0
  437. package/src/exam/dto/update-exam-question.dto.ts +4 -0
  438. package/src/exam/dto/update-exam.dto.ts +4 -0
  439. package/src/exam/exam-attempt.controller.ts +65 -0
  440. package/src/exam/exam-attempt.service.ts +1008 -0
  441. package/src/exam/exam.controller.ts +102 -0
  442. package/src/exam/exam.module.ts +14 -0
  443. package/src/exam/exam.service.ts +784 -0
  444. package/src/index.ts +29 -0
  445. package/src/instructor/dto/create-instructor.dto.ts +43 -0
  446. package/src/instructor/dto/update-instructor.dto.ts +38 -0
  447. package/src/instructor/instructor.controller.ts +73 -0
  448. package/src/instructor/instructor.module.ts +12 -0
  449. package/src/instructor/instructor.service.ts +646 -0
  450. package/src/lms.module.ts +36 -4
  451. package/src/reports/reports.controller.ts +14 -0
  452. package/src/reports/reports.module.ts +12 -0
  453. package/src/reports/reports.service.ts +485 -0
  454. package/src/training/dto/create-training.dto.ts +81 -0
  455. package/src/training/dto/update-training.dto.ts +4 -0
  456. package/src/training/training.controller.ts +68 -0
  457. package/src/training/training.module.ts +12 -0
  458. package/src/training/training.service.ts +574 -0
@@ -0,0 +1,769 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import { Label } from '@/components/ui/label';
5
+ import { ScrollArea } from '@/components/ui/scroll-area';
6
+ import {
7
+ Select,
8
+ SelectContent,
9
+ SelectItem,
10
+ SelectTrigger,
11
+ SelectValue,
12
+ } from '@/components/ui/select';
13
+ import { Slider } from '@/components/ui/slider';
14
+ import { Switch } from '@/components/ui/switch';
15
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
16
+ import { Textarea } from '@/components/ui/textarea';
17
+ import {
18
+ ChevronDown,
19
+ ChevronUp,
20
+ Copy,
21
+ Eye,
22
+ EyeOff,
23
+ GripVertical,
24
+ Lock,
25
+ MousePointer2,
26
+ Trash2,
27
+ Unlock,
28
+ } from 'lucide-react';
29
+ import { useCallback, useRef, useState } from 'react';
30
+ import { toast } from 'sonner';
31
+ import { getCanvasAPI } from '../../_lib/editor/canvasInstance';
32
+ import { fromPctH, fromPctW } from '../../_lib/editor/pctHelpers';
33
+ import type { TemplateObject } from '../../_lib/editor/types';
34
+ import { FONT_FAMILIES, getObjectLabel } from '../../_lib/editor/types';
35
+ import { useTemplateStore } from '../../_lib/store/useTemplateStore';
36
+
37
+ export default function RightPanel() {
38
+ return (
39
+ <aside className="flex w-75 shrink-0 flex-col border-l border-border bg-background">
40
+ <Tabs
41
+ defaultValue="props"
42
+ className="flex flex-1 flex-col overflow-hidden"
43
+ >
44
+ <TabsList className="mx-3 mt-3 w-auto">
45
+ <TabsTrigger value="props" className="flex-1">
46
+ Propriedades
47
+ </TabsTrigger>
48
+ <TabsTrigger value="layers" className="flex-1">
49
+ Camadas
50
+ </TabsTrigger>
51
+ <TabsTrigger value="data" className="flex-1">
52
+ Dados
53
+ </TabsTrigger>
54
+ </TabsList>
55
+
56
+ <TabsContent value="props" className="flex-1 overflow-hidden">
57
+ <ScrollArea className="h-full">
58
+ <PropertiesInspector />
59
+ </ScrollArea>
60
+ </TabsContent>
61
+
62
+ <TabsContent value="layers" className="flex-1 overflow-hidden">
63
+ <ScrollArea className="h-full">
64
+ <LayersList />
65
+ </ScrollArea>
66
+ </TabsContent>
67
+
68
+ <TabsContent value="data" className="flex-1 overflow-hidden">
69
+ <ScrollArea className="h-full">
70
+ <DataView />
71
+ </ScrollArea>
72
+ </TabsContent>
73
+ </Tabs>
74
+ </aside>
75
+ );
76
+ }
77
+
78
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
79
+ * PROPERTIES INSPECTOR
80
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
81
+
82
+ function PropertiesInspector() {
83
+ const selectedId = useTemplateStore((s) => s.selectedObjectId);
84
+ const objects = useTemplateStore((s) => s.template.objects);
85
+ const obj = objects.find((o) => o.id === selectedId);
86
+
87
+ const setProp = useCallback(
88
+ (fabricProps: Record<string, unknown>) => {
89
+ if (!selectedId) return;
90
+ getCanvasAPI()?.updateObjectProp(selectedId, fabricProps);
91
+ },
92
+ [selectedId]
93
+ );
94
+
95
+ if (!obj) {
96
+ return (
97
+ <div className="flex flex-col items-center justify-center gap-2 p-8 text-center text-muted-foreground">
98
+ <MousePointer2 className="size-8" />
99
+ <p className="text-sm">
100
+ Selecione um objeto no canvas para editar suas propriedades.
101
+ </p>
102
+ </div>
103
+ );
104
+ }
105
+
106
+ const isText = obj.type === 'field' || obj.type === 'staticText';
107
+ const isShape = obj.type === 'shape';
108
+ const isImage = obj.type === 'image';
109
+
110
+ return (
111
+ <div className="flex flex-col gap-4 p-3">
112
+ <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
113
+ {getObjectLabel(obj)}
114
+ </p>
115
+
116
+ {isText && <TextProperties obj={obj} setProp={setProp} />}
117
+ {isText && <TextStrokeProperties obj={obj} setProp={setProp} />}
118
+ {isText && <TextShadowProperties obj={obj} setProp={setProp} />}
119
+ {isShape && <ShapeProperties obj={obj} setProp={setProp} />}
120
+ {isImage && <ImageProperties obj={obj} setProp={setProp} />}
121
+ <CommonProperties obj={obj} setProp={setProp} />
122
+ </div>
123
+ );
124
+ }
125
+
126
+ /* ── text core ── */
127
+ function TextProperties({
128
+ obj,
129
+ setProp,
130
+ }: {
131
+ obj: TemplateObject;
132
+ setProp: (p: Record<string, unknown>) => void;
133
+ }) {
134
+ const ts = obj.textStyle;
135
+
136
+ return (
137
+ <>
138
+ <div className="flex flex-col gap-1.5">
139
+ <Label className="text-xs">Fonte</Label>
140
+ <Select
141
+ value={ts?.fontFamily ?? 'Inter'}
142
+ onValueChange={(v) => setProp({ fontFamily: v })}
143
+ >
144
+ <SelectTrigger className="w-full">
145
+ <SelectValue />
146
+ </SelectTrigger>
147
+ <SelectContent>
148
+ {FONT_FAMILIES.map((f) => (
149
+ <SelectItem key={f} value={f}>
150
+ {f}
151
+ </SelectItem>
152
+ ))}
153
+ </SelectContent>
154
+ </Select>
155
+ </div>
156
+
157
+ <div className="flex flex-col gap-1.5">
158
+ <Label className="text-xs">
159
+ Tamanho ({Math.round(fromPctH(ts?.fontSizePct ?? 2.12))}px)
160
+ </Label>
161
+ <Slider
162
+ min={8}
163
+ max={120}
164
+ step={1}
165
+ value={[Math.round(fromPctH(ts?.fontSizePct ?? 2.12))]}
166
+ onValueChange={([v]) => setProp({ fontSize: v })}
167
+ />
168
+ </div>
169
+
170
+ <div className="flex flex-col gap-1.5">
171
+ <Label className="text-xs">Peso</Label>
172
+ <Select
173
+ value={String(ts?.fontWeight ?? 400)}
174
+ onValueChange={(v) => setProp({ fontWeight: v })}
175
+ >
176
+ <SelectTrigger className="w-full">
177
+ <SelectValue />
178
+ </SelectTrigger>
179
+ <SelectContent>
180
+ <SelectItem value="400">Normal (400)</SelectItem>
181
+ <SelectItem value="600">Semi-Bold (600)</SelectItem>
182
+ <SelectItem value="700">Bold (700)</SelectItem>
183
+ </SelectContent>
184
+ </Select>
185
+ </div>
186
+
187
+ <div className="flex items-center justify-between">
188
+ <Label className="text-xs">Italico</Label>
189
+ <Switch
190
+ checked={ts?.italic ?? false}
191
+ onCheckedChange={(v) =>
192
+ setProp({ fontStyle: v ? 'italic' : 'normal' })
193
+ }
194
+ />
195
+ </div>
196
+
197
+ <div className="flex flex-col gap-1.5">
198
+ <Label className="text-xs">Cor do Texto</Label>
199
+ <div className="flex items-center gap-2">
200
+ <input
201
+ type="color"
202
+ value={ts?.fill ?? '#333333'}
203
+ onChange={(e) => setProp({ fill: e.target.value })}
204
+ className="h-8 w-10 cursor-pointer rounded border border-border bg-transparent"
205
+ />
206
+ <span className="text-xs text-muted-foreground">
207
+ {ts?.fill ?? '#333333'}
208
+ </span>
209
+ </div>
210
+ </div>
211
+
212
+ <div className="flex flex-col gap-1.5">
213
+ <Label className="text-xs">Alinhamento</Label>
214
+ <Select
215
+ value={ts?.align ?? 'left'}
216
+ onValueChange={(v) => setProp({ textAlign: v })}
217
+ >
218
+ <SelectTrigger className="w-full">
219
+ <SelectValue />
220
+ </SelectTrigger>
221
+ <SelectContent>
222
+ <SelectItem value="left">Esquerda</SelectItem>
223
+ <SelectItem value="center">Centro</SelectItem>
224
+ <SelectItem value="right">Direita</SelectItem>
225
+ </SelectContent>
226
+ </Select>
227
+ </div>
228
+ </>
229
+ );
230
+ }
231
+
232
+ /* ── text stroke ── */
233
+ function TextStrokeProperties({
234
+ obj,
235
+ setProp,
236
+ }: {
237
+ obj: TemplateObject;
238
+ setProp: (p: Record<string, unknown>) => void;
239
+ }) {
240
+ const stroke = obj.effects?.stroke;
241
+ const hasStroke = !!stroke && fromPctW(stroke.widthPct) > 0;
242
+
243
+ return (
244
+ <div className="flex flex-col gap-2 rounded-md border border-border p-2.5">
245
+ <div className="flex items-center justify-between">
246
+ <Label className="text-xs font-semibold">Contorno do Texto</Label>
247
+ <Switch
248
+ checked={hasStroke}
249
+ onCheckedChange={(on) => {
250
+ if (on) {
251
+ setProp({ stroke: '#000000', strokeWidth: 1 });
252
+ } else {
253
+ setProp({ stroke: '', strokeWidth: 0 });
254
+ }
255
+ }}
256
+ />
257
+ </div>
258
+ {hasStroke && (
259
+ <>
260
+ <div className="flex items-center gap-2">
261
+ <Label className="w-12 text-xs">Cor</Label>
262
+ <input
263
+ type="color"
264
+ value={stroke?.color ?? '#000000'}
265
+ onChange={(e) => setProp({ stroke: e.target.value })}
266
+ className="h-7 w-9 cursor-pointer rounded border border-border bg-transparent"
267
+ />
268
+ <span className="text-xs text-muted-foreground">
269
+ {stroke?.color ?? '#000000'}
270
+ </span>
271
+ </div>
272
+ <div className="flex flex-col gap-1">
273
+ <Label className="text-xs">
274
+ Espessura ({Math.round(fromPctW(stroke?.widthPct ?? 0))}px)
275
+ </Label>
276
+ <Slider
277
+ min={1}
278
+ max={10}
279
+ step={0.5}
280
+ value={[fromPctW(stroke?.widthPct ?? 0)]}
281
+ onValueChange={([v]) => setProp({ strokeWidth: v })}
282
+ />
283
+ </div>
284
+ </>
285
+ )}
286
+ </div>
287
+ );
288
+ }
289
+
290
+ /* ── text shadow ── */
291
+ function TextShadowProperties({
292
+ obj,
293
+ setProp,
294
+ }: {
295
+ obj: TemplateObject;
296
+ setProp: (p: Record<string, unknown>) => void;
297
+ }) {
298
+ const shadow = obj.effects?.shadow;
299
+ const hasShadow = !!shadow;
300
+
301
+ /* We need to build a fabric.Shadow object for the updateObjectProp call */
302
+ function setShadowProp(partial: {
303
+ offsetX?: number;
304
+ offsetY?: number;
305
+ blur?: number;
306
+ color?: string;
307
+ }) {
308
+ const cur = {
309
+ offsetX: shadow ? fromPctW(shadow.xPct) : 2,
310
+ offsetY: shadow ? fromPctH(shadow.yPct) : 2,
311
+ blur: shadow ? fromPctW(shadow.blurPct) : 4,
312
+ color: shadow?.color ?? 'rgba(0,0,0,0.3)',
313
+ };
314
+ const merged = { ...cur, ...partial };
315
+ /* pass the raw shadow config; updateObjectProp will set it directly */
316
+ setProp({
317
+ shadow: `${merged.color} ${merged.offsetX}px ${merged.offsetY}px ${merged.blur}px`,
318
+ });
319
+ }
320
+
321
+ return (
322
+ <div className="flex flex-col gap-2 rounded-md border border-border p-2.5">
323
+ <div className="flex items-center justify-between">
324
+ <Label className="text-xs font-semibold">Sombra</Label>
325
+ <Switch
326
+ checked={hasShadow}
327
+ onCheckedChange={(on) => {
328
+ if (on) {
329
+ setProp({
330
+ shadow: 'rgba(0,0,0,0.3) 2px 2px 4px',
331
+ });
332
+ } else {
333
+ setProp({ shadow: null });
334
+ }
335
+ }}
336
+ />
337
+ </div>
338
+ {hasShadow && (
339
+ <>
340
+ <div className="flex items-center gap-2">
341
+ <Label className="w-12 text-xs">Cor</Label>
342
+ <input
343
+ type="color"
344
+ value={shadow?.color?.startsWith('#') ? shadow.color : '#000000'}
345
+ onChange={(e) => setShadowProp({ color: e.target.value })}
346
+ className="h-7 w-9 cursor-pointer rounded border border-border bg-transparent"
347
+ />
348
+ </div>
349
+ <div className="flex flex-col gap-1">
350
+ <Label className="text-xs">
351
+ Offset X ({Math.round(fromPctW(shadow?.xPct ?? 0))}px)
352
+ </Label>
353
+ <Slider
354
+ min={-20}
355
+ max={20}
356
+ step={1}
357
+ value={[Math.round(fromPctW(shadow?.xPct ?? 0))]}
358
+ onValueChange={([v]) => setShadowProp({ offsetX: v })}
359
+ />
360
+ </div>
361
+ <div className="flex flex-col gap-1">
362
+ <Label className="text-xs">
363
+ Offset Y ({Math.round(fromPctH(shadow?.yPct ?? 0))}px)
364
+ </Label>
365
+ <Slider
366
+ min={-20}
367
+ max={20}
368
+ step={1}
369
+ value={[Math.round(fromPctH(shadow?.yPct ?? 0))]}
370
+ onValueChange={([v]) => setShadowProp({ offsetY: v })}
371
+ />
372
+ </div>
373
+ <div className="flex flex-col gap-1">
374
+ <Label className="text-xs">
375
+ Blur ({Math.round(fromPctW(shadow?.blurPct ?? 0))}px)
376
+ </Label>
377
+ <Slider
378
+ min={0}
379
+ max={30}
380
+ step={1}
381
+ value={[Math.round(fromPctW(shadow?.blurPct ?? 0))]}
382
+ onValueChange={([v]) => setShadowProp({ blur: v })}
383
+ />
384
+ </div>
385
+ </>
386
+ )}
387
+ </div>
388
+ );
389
+ }
390
+
391
+ /* ── shape ── */
392
+ function ShapeProperties({
393
+ obj,
394
+ setProp,
395
+ }: {
396
+ obj: TemplateObject;
397
+ setProp: (p: Record<string, unknown>) => void;
398
+ }) {
399
+ const stroke = obj.effects?.stroke;
400
+
401
+ return (
402
+ <>
403
+ {obj.shape !== 'line' && (
404
+ <div className="flex flex-col gap-1.5">
405
+ <Label className="text-xs">Preenchimento</Label>
406
+ <div className="flex items-center gap-2">
407
+ <input
408
+ type="color"
409
+ value={obj.fill ?? '#e2e8f0'}
410
+ onChange={(e) => setProp({ fill: e.target.value })}
411
+ className="h-8 w-10 cursor-pointer rounded border border-border bg-transparent"
412
+ />
413
+ <span className="text-xs text-muted-foreground">
414
+ {obj.fill ?? '#e2e8f0'}
415
+ </span>
416
+ </div>
417
+ </div>
418
+ )}
419
+
420
+ <div className="flex flex-col gap-1.5">
421
+ <Label className="text-xs">Cor da Borda</Label>
422
+ <div className="flex items-center gap-2">
423
+ <input
424
+ type="color"
425
+ value={stroke?.color ?? '#94a3b8'}
426
+ onChange={(e) => setProp({ stroke: e.target.value })}
427
+ className="h-8 w-10 cursor-pointer rounded border border-border bg-transparent"
428
+ />
429
+ <span className="text-xs text-muted-foreground">
430
+ {stroke?.color ?? '#94a3b8'}
431
+ </span>
432
+ </div>
433
+ </div>
434
+
435
+ <div className="flex flex-col gap-1.5">
436
+ <Label className="text-xs">
437
+ Espessura Borda ({Math.round(fromPctW(stroke?.widthPct ?? 0.0625))}px)
438
+ </Label>
439
+ <Slider
440
+ min={0}
441
+ max={20}
442
+ step={1}
443
+ value={[Math.round(fromPctW(stroke?.widthPct ?? 0.0625))]}
444
+ onValueChange={([v]) => setProp({ strokeWidth: v })}
445
+ />
446
+ </div>
447
+ </>
448
+ );
449
+ }
450
+
451
+ /* ── image ── */
452
+ function ImageProperties({
453
+ obj,
454
+ setProp,
455
+ }: {
456
+ obj: TemplateObject;
457
+ setProp: (p: Record<string, unknown>) => void;
458
+ }) {
459
+ return (
460
+ <div className="flex flex-col gap-1.5">
461
+ <Label className="text-xs">Rotacao ({Math.round(obj.rotation)}deg)</Label>
462
+ <Slider
463
+ min={0}
464
+ max={360}
465
+ step={1}
466
+ value={[Math.round(obj.rotation)]}
467
+ onValueChange={([v]) => setProp({ angle: v })}
468
+ />
469
+ </div>
470
+ );
471
+ }
472
+
473
+ /* ── common: opacity + rotation ── */
474
+ function CommonProperties({
475
+ obj,
476
+ setProp,
477
+ }: {
478
+ obj: TemplateObject;
479
+ setProp: (p: Record<string, unknown>) => void;
480
+ }) {
481
+ return (
482
+ <>
483
+ <div className="flex flex-col gap-1.5">
484
+ <Label className="text-xs">
485
+ Opacidade ({Math.round(obj.opacity * 100)}%)
486
+ </Label>
487
+ <Slider
488
+ min={0}
489
+ max={100}
490
+ step={1}
491
+ value={[Math.round(obj.opacity * 100)]}
492
+ onValueChange={([v]) =>
493
+ setProp({ opacity: (v ?? Math.round(obj.opacity * 100)) / 100 })
494
+ }
495
+ />
496
+ </div>
497
+
498
+ {obj.type !== 'image' && (
499
+ <div className="flex flex-col gap-1.5">
500
+ <Label className="text-xs">
501
+ Rotacao ({Math.round(obj.rotation)}deg)
502
+ </Label>
503
+ <Slider
504
+ min={0}
505
+ max={360}
506
+ step={1}
507
+ value={[Math.round(obj.rotation)]}
508
+ onValueChange={([v]) => setProp({ angle: v })}
509
+ />
510
+ </div>
511
+ )}
512
+ </>
513
+ );
514
+ }
515
+
516
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
517
+ * LAYERS LIST
518
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
519
+
520
+ function LayersList() {
521
+ const objects = useTemplateStore((s) => s.template.objects);
522
+ const selectedId = useTemplateStore((s) => s.selectedObjectId);
523
+ const reorderFromLayers = useTemplateStore((s) => s.reorderFromLayers);
524
+
525
+ const sorted = [...objects].sort((a, b) => b.zIndex - a.zIndex);
526
+
527
+ const [dragIdx, setDragIdx] = useState<number | null>(null);
528
+ const [overIdx, setOverIdx] = useState<number | null>(null);
529
+ const dragIdRef = useRef<string | null>(null);
530
+
531
+ function handleDragStart(idx: number, id: string) {
532
+ setDragIdx(idx);
533
+ dragIdRef.current = id;
534
+ }
535
+
536
+ function handleDragOver(e: React.DragEvent, idx: number) {
537
+ e.preventDefault();
538
+ e.dataTransfer.dropEffect = 'move';
539
+ setOverIdx(idx);
540
+ }
541
+
542
+ function handleDrop(targetIdx: number) {
543
+ if (dragIdx === null || dragIdx === targetIdx) {
544
+ setDragIdx(null);
545
+ setOverIdx(null);
546
+ return;
547
+ }
548
+
549
+ const ids = sorted.map((o) => o.id);
550
+ const [moved] = ids.splice(dragIdx, 1);
551
+ if (!moved) {
552
+ setDragIdx(null);
553
+ setOverIdx(null);
554
+ return;
555
+ }
556
+ ids.splice(targetIdx, 0, moved);
557
+
558
+ reorderFromLayers(ids);
559
+
560
+ const idsBottomTop = [...ids].reverse();
561
+ getCanvasAPI()?.reorderByIds(idsBottomTop);
562
+
563
+ setDragIdx(null);
564
+ setOverIdx(null);
565
+ }
566
+
567
+ function handleDragEnd() {
568
+ setDragIdx(null);
569
+ setOverIdx(null);
570
+ }
571
+
572
+ if (sorted.length === 0) {
573
+ return (
574
+ <div className="p-6 text-center text-sm text-muted-foreground">
575
+ Nenhum objeto adicionado.
576
+ </div>
577
+ );
578
+ }
579
+
580
+ return (
581
+ <div className="flex flex-col gap-0.5 p-2">
582
+ {sorted.map((obj, idx) => (
583
+ <LayerItem
584
+ key={obj.id}
585
+ obj={obj}
586
+ idx={idx}
587
+ isSelected={obj.id === selectedId}
588
+ isDragging={dragIdx === idx}
589
+ isDropTarget={overIdx === idx && dragIdx !== idx}
590
+ onDragStart={() => handleDragStart(idx, obj.id)}
591
+ onDragOver={(e) => handleDragOver(e, idx)}
592
+ onDrop={() => handleDrop(idx)}
593
+ onDragEnd={handleDragEnd}
594
+ />
595
+ ))}
596
+ </div>
597
+ );
598
+ }
599
+
600
+ function LayerItem({
601
+ obj,
602
+ isSelected,
603
+ isDragging,
604
+ isDropTarget,
605
+ onDragStart,
606
+ onDragOver,
607
+ onDrop,
608
+ onDragEnd,
609
+ }: {
610
+ obj: TemplateObject;
611
+ idx: number;
612
+ isSelected: boolean;
613
+ isDragging: boolean;
614
+ isDropTarget: boolean;
615
+ onDragStart: () => void;
616
+ onDragOver: (e: React.DragEvent) => void;
617
+ onDrop: () => void;
618
+ onDragEnd: () => void;
619
+ }) {
620
+ const handleSelect = useCallback(() => {
621
+ getCanvasAPI()?.selectObject(obj.id);
622
+ }, [obj.id]);
623
+
624
+ const handleDelete = useCallback(() => {
625
+ getCanvasAPI()?.removeObject(obj.id);
626
+ }, [obj.id]);
627
+
628
+ const handleToggleLock = useCallback(() => {
629
+ getCanvasAPI()?.lockObject(obj.id, !obj.locked);
630
+ }, [obj.id, obj.locked]);
631
+
632
+ const handleToggleVisible = useCallback(() => {
633
+ getCanvasAPI()?.setObjectVisible(obj.id, !obj.visible);
634
+ }, [obj.id, obj.visible]);
635
+
636
+ const handleMoveUp = useCallback(() => {
637
+ getCanvasAPI()?.reorderObject(obj.id, 'up');
638
+ }, [obj.id]);
639
+
640
+ const handleMoveDown = useCallback(() => {
641
+ getCanvasAPI()?.reorderObject(obj.id, 'down');
642
+ }, [obj.id]);
643
+
644
+ return (
645
+ <div
646
+ draggable
647
+ onDragStart={(e) => {
648
+ e.dataTransfer.effectAllowed = 'move';
649
+ e.dataTransfer.setData('text/plain', obj.id);
650
+ onDragStart();
651
+ }}
652
+ onDragOver={onDragOver}
653
+ onDrop={(e) => {
654
+ e.preventDefault();
655
+ onDrop();
656
+ }}
657
+ onDragEnd={onDragEnd}
658
+ className={`flex items-center gap-1 rounded-md px-1.5 py-1.5 text-xs transition-all
659
+ ${isSelected ? 'bg-primary/10 text-foreground' : 'text-muted-foreground hover:bg-accent'}
660
+ ${isDragging ? 'opacity-40' : ''}
661
+ ${isDropTarget ? 'ring-2 ring-primary/50 ring-inset' : ''}
662
+ `}
663
+ >
664
+ <GripVertical className="size-3 shrink-0 cursor-grab text-muted-foreground/50 active:cursor-grabbing" />
665
+
666
+ <button
667
+ type="button"
668
+ onClick={handleSelect}
669
+ className="flex flex-1 items-center gap-1.5 truncate text-left"
670
+ >
671
+ <span className="truncate">{getObjectLabel(obj)}</span>
672
+ </button>
673
+
674
+ <div className="flex items-center gap-0.5">
675
+ <button
676
+ type="button"
677
+ onClick={handleMoveUp}
678
+ className="rounded p-0.5 hover:bg-accent"
679
+ title="Mover para cima"
680
+ >
681
+ <ChevronUp className="size-3" />
682
+ </button>
683
+ <button
684
+ type="button"
685
+ onClick={handleMoveDown}
686
+ className="rounded p-0.5 hover:bg-accent"
687
+ title="Mover para baixo"
688
+ >
689
+ <ChevronDown className="size-3" />
690
+ </button>
691
+
692
+ <button
693
+ type="button"
694
+ onClick={handleToggleVisible}
695
+ className="rounded p-0.5 hover:bg-accent"
696
+ title={obj.visible ? 'Ocultar' : 'Mostrar'}
697
+ >
698
+ {obj.visible ? (
699
+ <Eye className="size-3.5" />
700
+ ) : (
701
+ <EyeOff className="size-3.5" />
702
+ )}
703
+ </button>
704
+
705
+ <button
706
+ type="button"
707
+ onClick={handleToggleLock}
708
+ className="rounded p-0.5 hover:bg-accent"
709
+ title={obj.locked ? 'Destravar' : 'Travar'}
710
+ >
711
+ {obj.locked ? (
712
+ <Lock className="size-3.5" />
713
+ ) : (
714
+ <Unlock className="size-3.5" />
715
+ )}
716
+ </button>
717
+
718
+ <button
719
+ type="button"
720
+ onClick={handleDelete}
721
+ className="rounded p-0.5 text-destructive hover:bg-destructive/10"
722
+ title="Excluir"
723
+ >
724
+ <Trash2 className="size-3.5" />
725
+ </button>
726
+ </div>
727
+ </div>
728
+ );
729
+ }
730
+
731
+ /* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
732
+ * DATA / JSON VIEW
733
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
734
+
735
+ function DataView() {
736
+ const template = useTemplateStore((s) => s.template);
737
+ const json = JSON.stringify(template, null, 2);
738
+
739
+ const handleCopy = useCallback(() => {
740
+ navigator.clipboard.writeText(json).then(() => {
741
+ toast.success('JSON copiado!');
742
+ });
743
+ }, [json]);
744
+
745
+ return (
746
+ <div className="flex flex-col gap-3 p-3">
747
+ <div className="flex items-center justify-between">
748
+ <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
749
+ Template JSON
750
+ </p>
751
+ <Button
752
+ variant="outline"
753
+ size="sm"
754
+ onClick={handleCopy}
755
+ className="h-7 gap-1.5 text-xs"
756
+ >
757
+ <Copy className="size-3" />
758
+ Copiar
759
+ </Button>
760
+ </div>
761
+ <Textarea
762
+ readOnly
763
+ value={json}
764
+ rows={24}
765
+ className="font-mono text-[11px] leading-relaxed"
766
+ />
767
+ </div>
768
+ );
769
+ }