@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,277 @@
1
+ 'use client';
2
+
3
+ import {
4
+ addDays,
5
+ addMonths,
6
+ eachDayOfInterval,
7
+ format,
8
+ isSameDay,
9
+ isSameMonth,
10
+ isToday,
11
+ startOfMonth,
12
+ startOfWeek,
13
+ subMonths,
14
+ } from 'date-fns';
15
+ import { enUS, ptBR } from 'date-fns/locale';
16
+ import { ChevronLeft, ChevronRight } from 'lucide-react';
17
+ import { useState } from 'react';
18
+
19
+ export type MobileCalendarEvent<TResource = unknown> = {
20
+ id?: number;
21
+ title: string;
22
+ start: Date;
23
+ end: Date;
24
+ resource: TResource;
25
+ };
26
+
27
+ type Anchor = { x: number; y: number };
28
+
29
+ interface MobileCalendarProps<TResource = unknown> {
30
+ events: MobileCalendarEvent<TResource>[];
31
+ date: Date;
32
+ locale: string;
33
+ previousLabel: string;
34
+ nextLabel: string;
35
+ noEventsLabel: string;
36
+ onNavigate: (date: Date) => void;
37
+ onSelectEvent: (
38
+ event: MobileCalendarEvent<TResource>,
39
+ anchor: Anchor
40
+ ) => void;
41
+ onSelectDay?: (date: Date, anchor: Anchor) => void;
42
+ getEventColor?: (event: MobileCalendarEvent<TResource>) => string;
43
+ }
44
+
45
+ export function MobileCalendar<TResource = unknown>({
46
+ events,
47
+ date,
48
+ locale,
49
+ previousLabel,
50
+ nextLabel,
51
+ noEventsLabel,
52
+ onNavigate,
53
+ onSelectEvent,
54
+ onSelectDay,
55
+ getEventColor = () => '#3b82f6',
56
+ }: MobileCalendarProps<TResource>) {
57
+ const [selectedDay, setSelectedDay] = useState<Date | null>(null);
58
+ const dateLocale = locale.startsWith('pt') ? ptBR : enUS;
59
+ const monthStart = startOfMonth(date);
60
+ const gridStart = startOfWeek(monthStart, { weekStartsOn: 1 });
61
+ const days = eachDayOfInterval({
62
+ start: gridStart,
63
+ end: addDays(gridStart, 41),
64
+ });
65
+ const dayHeaders = locale.startsWith('pt')
66
+ ? ['S', 'T', 'Q', 'Q', 'S', 'S', 'D']
67
+ : ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
68
+
69
+ const getEventsForDay = (day: Date) =>
70
+ events.filter((event) => isSameDay(event.start, day));
71
+
72
+ const selectedDayEvents = selectedDay ? getEventsForDay(selectedDay) : [];
73
+
74
+ return (
75
+ <div className="space-y-4">
76
+ <div className="flex items-center justify-between gap-3 rounded-2xl border border-border/70 bg-muted/20 px-3 py-2.5">
77
+ <button
78
+ type="button"
79
+ onClick={() => {
80
+ onNavigate(subMonths(date, 1));
81
+ setSelectedDay(null);
82
+ }}
83
+ aria-label={previousLabel}
84
+ className="flex size-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
85
+ >
86
+ <ChevronLeft className="size-4" />
87
+ </button>
88
+ <div className="min-w-0 text-center">
89
+ <p className="truncate text-sm font-semibold capitalize text-foreground">
90
+ {format(
91
+ date,
92
+ locale.startsWith('pt') ? "MMMM 'de' yyyy" : 'MMMM yyyy',
93
+ { locale: dateLocale }
94
+ )}
95
+ </p>
96
+ </div>
97
+ <button
98
+ type="button"
99
+ onClick={() => {
100
+ onNavigate(addMonths(date, 1));
101
+ setSelectedDay(null);
102
+ }}
103
+ aria-label={nextLabel}
104
+ className="flex size-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
105
+ >
106
+ <ChevronRight className="size-4" />
107
+ </button>
108
+ </div>
109
+
110
+ <div className="rounded-3xl border border-border/70 bg-card p-3 shadow-xs lg:p-4">
111
+ <div className="mb-2 grid grid-cols-7">
112
+ {dayHeaders.map((dayHeader, index) => (
113
+ <div
114
+ key={`${dayHeader}-${index}`}
115
+ className="py-1 text-center text-[11px] font-medium uppercase tracking-wide text-muted-foreground"
116
+ >
117
+ {dayHeader}
118
+ </div>
119
+ ))}
120
+ </div>
121
+
122
+ <div className="grid grid-cols-7 gap-1 lg:gap-2">
123
+ {days.map((day) => {
124
+ const dayEvents = getEventsForDay(day);
125
+ const isCurrentMonthDay = isSameMonth(day, date);
126
+ const isTodayDay = isToday(day);
127
+ const isSelectedDay =
128
+ selectedDay !== null && isSameDay(day, selectedDay);
129
+ const visibleDesktopEvents = dayEvents.slice(0, 3);
130
+ const hiddenDesktopEvents = Math.max(dayEvents.length - 3, 0);
131
+
132
+ return (
133
+ <div
134
+ key={day.toISOString()}
135
+ onClick={(e) => {
136
+ setSelectedDay((current) =>
137
+ current && isSameDay(current, day) ? null : day
138
+ );
139
+ if (isCurrentMonthDay) {
140
+ onSelectDay?.(day, { x: e.clientX, y: e.clientY });
141
+ }
142
+ }}
143
+ className={[
144
+ 'rounded-2xl border border-border/60 p-1.5 transition-colors lg:min-h-32 lg:p-2',
145
+ isCurrentMonthDay
146
+ ? 'cursor-pointer bg-background/90 hover:border-border hover:bg-muted/30'
147
+ : 'bg-muted/20 opacity-30',
148
+ isSelectedDay ? 'ring-1 ring-primary/35' : '',
149
+ ].join(' ')}
150
+ >
151
+ <div
152
+ className={[
153
+ 'flex w-full rounded-xl transition-colors',
154
+ 'justify-center lg:justify-start',
155
+ ].join(' ')}
156
+ >
157
+ <span
158
+ className={[
159
+ 'flex size-8 items-center justify-center rounded-full text-sm transition-colors lg:size-9',
160
+ isTodayDay
161
+ ? 'bg-primary font-semibold text-primary-foreground'
162
+ : '',
163
+ isSelectedDay && !isTodayDay
164
+ ? 'bg-muted font-semibold text-foreground'
165
+ : '',
166
+ !isTodayDay && !isSelectedDay && isCurrentMonthDay
167
+ ? 'text-foreground'
168
+ : '',
169
+ ].join(' ')}
170
+ >
171
+ {format(day, 'd')}
172
+ </span>
173
+ </div>
174
+
175
+ <div className="mt-1 flex h-2 items-center justify-center gap-1 lg:hidden">
176
+ {dayEvents.slice(0, 3).map((event, index) => (
177
+ <span
178
+ key={`${event.id ?? event.title}-${index}`}
179
+ className="size-1.5 rounded-full"
180
+ style={{ backgroundColor: getEventColor(event) }}
181
+ />
182
+ ))}
183
+ </div>
184
+
185
+ {isCurrentMonthDay && dayEvents.length > 0 && (
186
+ <div className="mt-2 hidden space-y-1 lg:block">
187
+ {visibleDesktopEvents.map((event, index) => (
188
+ <button
189
+ key={`${event.id ?? event.title}-${index}`}
190
+ type="button"
191
+ onClick={(e) => {
192
+ e.stopPropagation();
193
+ onSelectEvent(event, { x: e.clientX, y: e.clientY });
194
+ }}
195
+ className="flex w-full cursor-pointer items-center rounded-md px-2 py-1 text-left text-[10px] font-medium text-white shadow-sm transition-opacity hover:opacity-90"
196
+ style={{ backgroundColor: getEventColor(event) }}
197
+ title={event.title}
198
+ >
199
+ <span className="truncate">{event.title}</span>
200
+ </button>
201
+ ))}
202
+
203
+ {hiddenDesktopEvents > 0 && (
204
+ <button
205
+ type="button"
206
+ onClick={(e) => {
207
+ e.stopPropagation();
208
+ setSelectedDay(day);
209
+ }}
210
+ className="flex w-full cursor-pointer items-center justify-center rounded-md border border-dashed border-border/80 px-2 py-1 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground"
211
+ >
212
+ +{hiddenDesktopEvents}
213
+ </button>
214
+ )}
215
+ </div>
216
+ )}
217
+ </div>
218
+ );
219
+ })}
220
+ </div>
221
+ </div>
222
+
223
+ {selectedDay && (
224
+ <div className="rounded-2xl border border-border/70 bg-card p-3">
225
+ <div className="mb-3 flex items-center justify-between gap-2 border-b border-border/60 pb-2">
226
+ <p className="text-sm font-semibold text-foreground">
227
+ {format(
228
+ selectedDay,
229
+ locale.startsWith('pt') ? "dd 'de' MMMM" : 'MMMM dd',
230
+ { locale: dateLocale }
231
+ )}
232
+ </p>
233
+ <span className="text-xs text-muted-foreground">
234
+ {selectedDayEvents.length}
235
+ </span>
236
+ </div>
237
+
238
+ {selectedDayEvents.length === 0 ? (
239
+ <p className="py-3 text-center text-sm text-muted-foreground">
240
+ {noEventsLabel}
241
+ </p>
242
+ ) : (
243
+ <div className="space-y-2">
244
+ {selectedDayEvents.map((event) => (
245
+ <button
246
+ key={
247
+ event.id ?? `${event.title}-${event.start.toISOString()}`
248
+ }
249
+ type="button"
250
+ onClick={(e) =>
251
+ onSelectEvent(event, { x: e.clientX, y: e.clientY })
252
+ }
253
+ className="flex w-full cursor-pointer items-center gap-3 rounded-2xl border border-border/60 bg-muted/20 px-3 py-3 text-left transition-colors hover:bg-muted/50"
254
+ >
255
+ <span
256
+ className="size-2.5 shrink-0 rounded-full"
257
+ style={{ backgroundColor: getEventColor(event) }}
258
+ />
259
+ <div className="min-w-0 flex-1">
260
+ <p className="truncate text-sm font-medium text-foreground">
261
+ {event.title}
262
+ </p>
263
+ <p className="text-xs text-muted-foreground">
264
+ {format(event.start, 'HH:mm')} -{' '}
265
+ {format(event.end, 'HH:mm')}
266
+ </p>
267
+ </div>
268
+ <ChevronRight className="size-4 shrink-0 text-muted-foreground" />
269
+ </button>
270
+ ))}
271
+ </div>
272
+ )}
273
+ </div>
274
+ )}
275
+ </div>
276
+ );
277
+ }
@@ -0,0 +1,48 @@
1
+ import { BackgroundFit, Template } from './types';
2
+
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ export interface CanvasAPI {
5
+ addObject: (
6
+ type: 'field' | 'staticText' | 'image' | 'shape',
7
+ opts?: { key?: string; shape?: 'rect' | 'line' }
8
+ ) => void;
9
+ /** Drop-aware: create at canvas-local point (already transformed) */
10
+ addObjectAt: (
11
+ type: 'field' | 'staticText' | 'image' | 'shape',
12
+ canvasX: number,
13
+ canvasY: number,
14
+ opts?: { key?: string; shape?: 'rect' | 'line' }
15
+ ) => void;
16
+ removeObject: (id: string) => void;
17
+ duplicateObject: (id: string) => void;
18
+ selectObject: (id: string) => void;
19
+ updateObjectProp: (id: string, fabricProps: Record<string, any>) => void;
20
+ setBackground: (dataUrl: string, fit?: BackgroundFit) => void;
21
+ lockObject: (id: string, locked: boolean) => void;
22
+ setObjectVisible: (id: string, visible: boolean) => void;
23
+ loadTemplate: (template: Template) => void;
24
+ setZoom: (zoom: number) => void;
25
+ reorderObject: (id: string, direction: 'up' | 'down') => void;
26
+ /** Set Fabric z-order by array of ids (bottom→top) */
27
+ reorderByIds: (ids: string[]) => void;
28
+ /** Convert client (page) coords to Fabric canvas coords */
29
+ clientToCanvas: (
30
+ clientX: number,
31
+ clientY: number
32
+ ) => { x: number; y: number } | null;
33
+ getCanvasElement: () => HTMLCanvasElement | null;
34
+ }
35
+
36
+ let api: CanvasAPI | null = null;
37
+
38
+ export function registerCanvasAPI(a: CanvasAPI) {
39
+ api = a;
40
+ }
41
+
42
+ export function unregisterCanvasAPI() {
43
+ api = null;
44
+ }
45
+
46
+ export function getCanvasAPI(): CanvasAPI | null {
47
+ return api;
48
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Pct <-> Px conversion helpers.
3
+ *
4
+ * Convention (documented):
5
+ * - xPct, wPct, stroke widthPct, shadow xPct, blurPct → relative to CANVAS_W
6
+ * - yPct, hPct, fontSizePct, shadow yPct → relative to CANVAS_H
7
+ *
8
+ * All Pct values are 0-100 range (e.g. 50 = 50% of canvas dimension).
9
+ * Rounding: 6 decimal places to preserve fidelity on import/export.
10
+ */
11
+
12
+ import { CANVAS_H, CANVAS_W } from './types';
13
+
14
+ const PRECISION = 6;
15
+
16
+ function r(n: number): number {
17
+ return parseFloat(n.toFixed(PRECISION));
18
+ }
19
+
20
+ /* ── X-axis (relative to CANVAS_W) ── */
21
+ export function toPctX(px: number): number {
22
+ return r((px / CANVAS_W) * 100);
23
+ }
24
+ export function fromPctX(pct: number): number {
25
+ return (pct / 100) * CANVAS_W;
26
+ }
27
+
28
+ /* ── Y-axis (relative to CANVAS_H) ── */
29
+ export function toPctY(px: number): number {
30
+ return r((px / CANVAS_H) * 100);
31
+ }
32
+ export function fromPctY(pct: number): number {
33
+ return (pct / 100) * CANVAS_H;
34
+ }
35
+
36
+ /* ── Width-axis sizes (stroke, shadow blur/offsetX — relative to CANVAS_W) ── */
37
+ export function toPctW(px: number): number {
38
+ return r((px / CANVAS_W) * 100);
39
+ }
40
+ export function fromPctW(pct: number): number {
41
+ return (pct / 100) * CANVAS_W;
42
+ }
43
+
44
+ /* ── Height-axis sizes (fontSize, shadow offsetY — relative to CANVAS_H) ── */
45
+ export function toPctH(px: number): number {
46
+ return r((px / CANVAS_H) * 100);
47
+ }
48
+ export function fromPctH(pct: number): number {
49
+ return (pct / 100) * CANVAS_H;
50
+ }
@@ -0,0 +1,268 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import {
3
+ fromPctH,
4
+ fromPctW,
5
+ fromPctX,
6
+ fromPctY,
7
+ toPctH,
8
+ toPctW,
9
+ toPctX,
10
+ toPctY,
11
+ } from './pctHelpers';
12
+ import type { Template, TemplateObject, TextStyle } from './types';
13
+ import { CANVAS_H, CANVAS_W, createDefaultTemplate } from './types';
14
+
15
+ /* ────────────────────────────────────────────
16
+ * Fabric Object → TemplateObject (partial)
17
+ * Used during live drag/move to update position only.
18
+ * ──────────────────────────────────────────── */
19
+ export function fabricObjToTemplatePartial(
20
+ fObj: any
21
+ ): Omit<TemplateObject, 'id' | 'type' | 'key' | 'shape' | 'text' | 'zIndex'> {
22
+ const left = fObj.left ?? 0;
23
+ const top = fObj.top ?? 0;
24
+ const w = (fObj.width ?? 0) * (fObj.scaleX ?? 1);
25
+ const h = (fObj.height ?? 0) * (fObj.scaleY ?? 1);
26
+
27
+ return {
28
+ xPct: toPctX(left),
29
+ yPct: toPctY(top),
30
+ wPct: toPctX(w),
31
+ hPct: toPctY(h),
32
+ rotation: parseFloat((fObj.angle ?? 0).toFixed(2)),
33
+ opacity: parseFloat((fObj.opacity ?? 1).toFixed(4)),
34
+ visible: fObj.visible !== false,
35
+ locked: fObj.lockMovementX === true,
36
+ };
37
+ }
38
+
39
+ /* ────────────────────────────────────────────
40
+ * Fabric Object → full TemplateObject
41
+ * ──────────────────────────────────────────── */
42
+ export function fabricObjToFull(fObj: any): TemplateObject {
43
+ const partial = fabricObjToTemplatePartial(fObj);
44
+ const id: string = fObj._tplId ?? crypto.randomUUID();
45
+ const type: TemplateObject['type'] = fObj._tplType ?? 'shape';
46
+ const key: string | undefined = fObj._tplKey;
47
+ const shape: 'rect' | 'line' | undefined = fObj._tplShape;
48
+
49
+ const obj: TemplateObject = {
50
+ ...partial,
51
+ id,
52
+ type,
53
+ key,
54
+ shape,
55
+ zIndex: 0,
56
+ };
57
+
58
+ /* text */
59
+ if (type === 'field' || type === 'staticText') {
60
+ obj.text = fObj.text ?? '';
61
+ obj.textStyle = {
62
+ fontFamily: fObj.fontFamily ?? 'Inter',
63
+ fontSizePct: toPctH(fObj.fontSize ?? 24),
64
+ fontWeight:
65
+ fObj.fontWeight === 'bold' ? 700 : Number(fObj.fontWeight) || 400,
66
+ italic: fObj.fontStyle === 'italic',
67
+ fill: typeof fObj.fill === 'string' ? fObj.fill : '#333333',
68
+ align: (fObj.textAlign as TextStyle['align']) ?? 'left',
69
+ lineHeight: fObj.lineHeight ?? 1.4,
70
+ letterSpacing: fObj.charSpacing ? fObj.charSpacing / 1000 : 0,
71
+ };
72
+
73
+ /* text stroke */
74
+ const hasStroke = fObj.stroke && fObj.strokeWidth && fObj.strokeWidth > 0;
75
+ const shadow = fObj.shadow;
76
+ if (hasStroke || shadow) {
77
+ obj.effects = {};
78
+ if (hasStroke) {
79
+ obj.effects.stroke = {
80
+ color: fObj.stroke,
81
+ widthPct: toPctW(fObj.strokeWidth),
82
+ };
83
+ }
84
+ if (shadow) {
85
+ obj.effects.shadow = {
86
+ xPct: toPctW(shadow.offsetX ?? 0),
87
+ yPct: toPctH(shadow.offsetY ?? 0),
88
+ blurPct: toPctW(shadow.blur ?? 0),
89
+ color: shadow.color ?? 'rgba(0,0,0,0.3)',
90
+ };
91
+ }
92
+ }
93
+ }
94
+
95
+ /* shape effects + fill */
96
+ if (type === 'shape') {
97
+ obj.fill = typeof fObj.fill === 'string' ? fObj.fill : '#cccccc';
98
+ obj.effects = {
99
+ stroke: {
100
+ color: fObj.stroke ?? '#999999',
101
+ widthPct: toPctW(fObj.strokeWidth ?? 1),
102
+ },
103
+ };
104
+ }
105
+
106
+ /* image */
107
+ if (type === 'image') {
108
+ obj.image = { src: fObj._tplImageSrc ?? '' };
109
+ }
110
+
111
+ return obj;
112
+ }
113
+
114
+ /* ────────────────────────────────────────────
115
+ * Full canvas → Template JSON
116
+ * ──────────────────────────────────────────── */
117
+ export function fabricToTemplateJSON(
118
+ canvas: any,
119
+ name: string,
120
+ backgroundSrc: string | null
121
+ ): Template {
122
+ const objects: TemplateObject[] = canvas
123
+ .getObjects()
124
+ .filter((fObj: any) => !fObj._isGuide)
125
+ .map((fObj: any, idx: number) => {
126
+ const o = fabricObjToFull(fObj);
127
+ o.zIndex = idx;
128
+ return o;
129
+ });
130
+
131
+ return {
132
+ version: 1,
133
+ name,
134
+ canvas: { width: CANVAS_W, height: CANVAS_H },
135
+ background: { src: backgroundSrc, fit: 'contain' },
136
+ objects,
137
+ };
138
+ }
139
+
140
+ /* ────────────────────────────────────────────
141
+ * TemplateObject → Fabric Object creation
142
+ * ──────────────────────────────────────────── */
143
+ export function createFabricFromTemplate(
144
+ tObj: TemplateObject,
145
+ fabric: any
146
+ ): any | null {
147
+ const left = fromPctX(tObj.xPct);
148
+ const top = fromPctY(tObj.yPct);
149
+ const w = fromPctX(tObj.wPct);
150
+ const h = fromPctY(tObj.hPct);
151
+
152
+ const common: Record<string, any> = {
153
+ left,
154
+ top,
155
+ angle: tObj.rotation,
156
+ opacity: tObj.opacity,
157
+ visible: tObj.visible,
158
+ lockMovementX: tObj.locked,
159
+ lockMovementY: tObj.locked,
160
+ lockRotation: tObj.locked,
161
+ lockScalingX: tObj.locked,
162
+ lockScalingY: tObj.locked,
163
+ hasControls: !tObj.locked,
164
+ selectable: !tObj.locked,
165
+ };
166
+
167
+ let fObj: any = null;
168
+
169
+ if (tObj.type === 'field' || tObj.type === 'staticText') {
170
+ const ts = tObj.textStyle;
171
+ const fabricProps: Record<string, any> = {
172
+ ...common,
173
+ width: w,
174
+ fontSize: ts ? fromPctH(ts.fontSizePct) : 24,
175
+ fontFamily: ts?.fontFamily ?? 'Inter',
176
+ fontWeight: String(ts?.fontWeight ?? 400),
177
+ fontStyle: ts?.italic ? 'italic' : 'normal',
178
+ fill: ts?.fill ?? '#333333',
179
+ textAlign: ts?.align ?? 'left',
180
+ lineHeight: ts?.lineHeight ?? 1.4,
181
+ charSpacing: ts?.letterSpacing ? ts.letterSpacing * 1000 : 0,
182
+ };
183
+
184
+ /* restore text stroke */
185
+ if (tObj.effects?.stroke) {
186
+ fabricProps.stroke = tObj.effects.stroke.color;
187
+ fabricProps.strokeWidth = fromPctW(tObj.effects.stroke.widthPct);
188
+ }
189
+
190
+ /* restore shadow */
191
+ if (tObj.effects?.shadow) {
192
+ const s = tObj.effects.shadow;
193
+ fabricProps.shadow = new fabric.Shadow({
194
+ offsetX: fromPctW(s.xPct),
195
+ offsetY: fromPctH(s.yPct),
196
+ blur: fromPctW(s.blurPct),
197
+ color: s.color,
198
+ });
199
+ }
200
+
201
+ fObj = new fabric.Textbox(tObj.text ?? '', fabricProps);
202
+ } else if (tObj.type === 'shape' && tObj.shape === 'rect') {
203
+ fObj = new fabric.Rect({
204
+ ...common,
205
+ width: w,
206
+ height: h,
207
+ fill: tObj.fill ?? '#cccccc',
208
+ stroke: tObj.effects?.stroke?.color ?? '#999999',
209
+ strokeWidth: tObj.effects?.stroke
210
+ ? fromPctW(tObj.effects.stroke.widthPct)
211
+ : 1,
212
+ });
213
+ } else if (tObj.type === 'shape' && tObj.shape === 'line') {
214
+ fObj = new fabric.Line([0, 0, w, 0], {
215
+ ...common,
216
+ stroke: tObj.effects?.stroke?.color ?? '#333333',
217
+ strokeWidth: tObj.effects?.stroke
218
+ ? fromPctW(tObj.effects.stroke.widthPct)
219
+ : 2,
220
+ });
221
+ } else if (tObj.type === 'image') {
222
+ fObj = new fabric.Rect({
223
+ ...common,
224
+ width: w || 150,
225
+ height: h || 150,
226
+ fill: '#e2e8f0',
227
+ stroke: '#cbd5e1',
228
+ strokeWidth: 1,
229
+ rx: 4,
230
+ ry: 4,
231
+ });
232
+ fObj._tplImageSrc = tObj.image?.src ?? '';
233
+ }
234
+
235
+ if (fObj) {
236
+ fObj._tplId = tObj.id;
237
+ fObj._tplType = tObj.type;
238
+ fObj._tplKey = tObj.key;
239
+ fObj._tplShape = tObj.shape;
240
+ }
241
+
242
+ return fObj;
243
+ }
244
+
245
+ /* ────────────────────────────────────────────
246
+ * Load template onto a canvas
247
+ * ──────────────────────────────────────────── */
248
+ export function templateJSONToFabric(
249
+ canvas: any,
250
+ template: Template,
251
+ fabric: any
252
+ ) {
253
+ /* remove all existing user objects (keep guides) */
254
+ const existing = canvas.getObjects().slice();
255
+ existing.forEach((o: any) => canvas.remove(o));
256
+ canvas.backgroundImage = undefined;
257
+
258
+ /* sorted by zIndex ascending (bottom first in fabric) */
259
+ const sorted = [...template.objects].sort((a, b) => a.zIndex - b.zIndex);
260
+ sorted.forEach((tObj) => {
261
+ const fObj = createFabricFromTemplate(tObj, fabric);
262
+ if (fObj) canvas.add(fObj);
263
+ });
264
+
265
+ canvas.requestRenderAll();
266
+ }
267
+
268
+ export { createDefaultTemplate };