@hed-hog/lms 0.0.364 → 0.0.366

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 (290) hide show
  1. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts +1 -0
  2. package/dist/bitcode-wallet/bitcode-wallet.service.d.ts.map +1 -1
  3. package/dist/bitcode-wallet/bitcode-wallet.service.js +22 -3
  4. package/dist/bitcode-wallet/bitcode-wallet.service.js.map +1 -1
  5. package/dist/class-group/class-group.controller.d.ts +1 -0
  6. package/dist/class-group/class-group.controller.d.ts.map +1 -1
  7. package/dist/class-group/class-group.service.d.ts +1 -0
  8. package/dist/class-group/class-group.service.d.ts.map +1 -1
  9. package/dist/course/course-export-scorm12-worker.service.d.ts +21 -0
  10. package/dist/course/course-export-scorm12-worker.service.d.ts.map +1 -0
  11. package/dist/course/course-export-scorm12-worker.service.js +109 -0
  12. package/dist/course/course-export-scorm12-worker.service.js.map +1 -0
  13. package/dist/course/course-export-scorm12.service.d.ts +42 -0
  14. package/dist/course/course-export-scorm12.service.d.ts.map +1 -0
  15. package/dist/course/course-export-scorm12.service.js +628 -0
  16. package/dist/course/course-export-scorm12.service.js.map +1 -0
  17. package/dist/course/course-export.service.d.ts +84 -0
  18. package/dist/course/course-export.service.d.ts.map +1 -0
  19. package/dist/course/course-export.service.js +237 -0
  20. package/dist/course/course-export.service.js.map +1 -0
  21. package/dist/course/course-structure.controller.d.ts +20 -10
  22. package/dist/course/course-structure.controller.d.ts.map +1 -1
  23. package/dist/course/course-structure.controller.js +20 -4
  24. package/dist/course/course-structure.controller.js.map +1 -1
  25. package/dist/course/course-structure.service.d.ts +12 -4
  26. package/dist/course/course-structure.service.d.ts.map +1 -1
  27. package/dist/course/course-structure.service.js +98 -23
  28. package/dist/course/course-structure.service.js.map +1 -1
  29. package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
  30. package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
  31. package/dist/course/course-video-agent-pipeline.service.js +398 -0
  32. package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
  33. package/dist/course/course-video-hls.service.d.ts +71 -0
  34. package/dist/course/course-video-hls.service.d.ts.map +1 -0
  35. package/dist/course/course-video-hls.service.js +784 -0
  36. package/dist/course/course-video-hls.service.js.map +1 -0
  37. package/dist/course/course.controller.d.ts +47 -13
  38. package/dist/course/course.controller.d.ts.map +1 -1
  39. package/dist/course/course.controller.js +40 -26
  40. package/dist/course/course.controller.js.map +1 -1
  41. package/dist/course/course.mcp-tools.js +1 -1
  42. package/dist/course/course.mcp-tools.js.map +1 -1
  43. package/dist/course/course.module.d.ts.map +1 -1
  44. package/dist/course/course.module.js +16 -0
  45. package/dist/course/course.module.js.map +1 -1
  46. package/dist/course/course.service.d.ts +8 -9
  47. package/dist/course/course.service.d.ts.map +1 -1
  48. package/dist/course/course.service.js +93 -50
  49. package/dist/course/course.service.js.map +1 -1
  50. package/dist/course/dto/cleanup-course-storage.dto.d.ts +1 -1
  51. package/dist/course/dto/cleanup-course-storage.dto.d.ts.map +1 -1
  52. package/dist/course/dto/cleanup-course-storage.dto.js +1 -0
  53. package/dist/course/dto/cleanup-course-storage.dto.js.map +1 -1
  54. package/dist/course/dto/cleanup-upload-history.dto.d.ts +1 -1
  55. package/dist/course/dto/cleanup-upload-history.dto.d.ts.map +1 -1
  56. package/dist/course/dto/cleanup-upload-history.dto.js +1 -1
  57. package/dist/course/dto/cleanup-upload-history.dto.js.map +1 -1
  58. package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
  59. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
  60. package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
  61. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
  62. package/dist/course/dto/create-course-export.dto.d.ts +14 -0
  63. package/dist/course/dto/create-course-export.dto.d.ts.map +1 -0
  64. package/dist/course/dto/create-course-export.dto.js +71 -0
  65. package/dist/course/dto/create-course-export.dto.js.map +1 -0
  66. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +2 -2
  67. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  68. package/dist/course/dto/create-course-structure-lesson.dto.js +3 -2
  69. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  70. package/dist/course/ffmpeg.util.d.ts +10 -0
  71. package/dist/course/ffmpeg.util.d.ts.map +1 -0
  72. package/dist/course/ffmpeg.util.js +79 -0
  73. package/dist/course/ffmpeg.util.js.map +1 -0
  74. package/dist/course/lms-bulk-upload-automation.service.d.ts +18 -1
  75. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  76. package/dist/course/lms-bulk-upload-automation.service.js +106 -8
  77. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  78. package/dist/course/lms-bulk-upload-infra.service.d.ts +1 -0
  79. package/dist/course/lms-bulk-upload-infra.service.d.ts.map +1 -1
  80. package/dist/course/lms-bulk-upload-infra.service.js +32 -8
  81. package/dist/course/lms-bulk-upload-infra.service.js.map +1 -1
  82. package/dist/course/lms-bulk-upload.controller.d.ts +30 -3
  83. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  84. package/dist/course/lms-bulk-upload.controller.js +43 -2
  85. package/dist/course/lms-bulk-upload.controller.js.map +1 -1
  86. package/dist/course/lms-bulk-upload.service.d.ts +11 -0
  87. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  88. package/dist/course/lms-bulk-upload.service.js +59 -6
  89. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  90. package/dist/course/lms-setting.controller.d.ts +2 -1
  91. package/dist/course/lms-setting.controller.d.ts.map +1 -1
  92. package/dist/course/lms-setting.controller.js +4 -2
  93. package/dist/course/lms-setting.controller.js.map +1 -1
  94. package/dist/course/scorm12-schemas.d.ts +4 -0
  95. package/dist/course/scorm12-schemas.d.ts.map +1 -0
  96. package/dist/course/scorm12-schemas.js +9 -0
  97. package/dist/course/scorm12-schemas.js.map +1 -0
  98. package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
  99. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  100. package/dist/enterprise/training/training-admin.service.d.ts +2 -0
  101. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  102. package/dist/enterprise/training/training-student.service.d.ts +51 -0
  103. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  104. package/dist/enterprise/training/training-student.service.js +217 -4
  105. package/dist/enterprise/training/training-student.service.js.map +1 -1
  106. package/dist/evaluation/evaluation.service.d.ts +18 -0
  107. package/dist/evaluation/evaluation.service.d.ts.map +1 -1
  108. package/dist/evaluation/evaluation.service.js +125 -0
  109. package/dist/evaluation/evaluation.service.js.map +1 -1
  110. package/dist/exam/dto/create-standalone-question.dto.d.ts +12 -0
  111. package/dist/exam/dto/create-standalone-question.dto.d.ts.map +1 -0
  112. package/dist/exam/dto/create-standalone-question.dto.js +70 -0
  113. package/dist/exam/dto/create-standalone-question.dto.js.map +1 -0
  114. package/dist/exam/exam.module.d.ts.map +1 -1
  115. package/dist/exam/exam.module.js +2 -1
  116. package/dist/exam/exam.module.js.map +1 -1
  117. package/dist/exam/exam.service.d.ts +21 -0
  118. package/dist/exam/exam.service.d.ts.map +1 -1
  119. package/dist/exam/exam.service.js +80 -0
  120. package/dist/exam/exam.service.js.map +1 -1
  121. package/dist/exam/question.controller.d.ts +27 -0
  122. package/dist/exam/question.controller.d.ts.map +1 -0
  123. package/dist/exam/question.controller.js +53 -0
  124. package/dist/exam/question.controller.js.map +1 -0
  125. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +4 -0
  126. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
  127. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +161 -25
  128. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
  129. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
  130. package/dist/lms-commerce-access.subscriber.d.ts +11 -0
  131. package/dist/lms-commerce-access.subscriber.d.ts.map +1 -0
  132. package/dist/lms-commerce-access.subscriber.js +74 -0
  133. package/dist/lms-commerce-access.subscriber.js.map +1 -0
  134. package/dist/lms.module.d.ts.map +1 -1
  135. package/dist/lms.module.js +16 -5
  136. package/dist/lms.module.js.map +1 -1
  137. package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
  138. package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
  139. package/dist/platforma/dto/heartbeat.dto.js +50 -0
  140. package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
  141. package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
  142. package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
  143. package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
  144. package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
  145. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
  146. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
  147. package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
  148. package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
  149. package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
  150. package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
  151. package/dist/platforma/platforma-heartbeat.service.js +50 -0
  152. package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
  153. package/dist/platforma/platforma-performance.service.d.ts +121 -0
  154. package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
  155. package/dist/platforma/platforma-performance.service.js +500 -0
  156. package/dist/platforma/platforma-performance.service.js.map +1 -0
  157. package/dist/platforma/platforma-search.service.d.ts +21 -0
  158. package/dist/platforma/platforma-search.service.d.ts.map +1 -0
  159. package/dist/platforma/platforma-search.service.js +64 -0
  160. package/dist/platforma/platforma-search.service.js.map +1 -0
  161. package/dist/platforma/platforma-video.service.d.ts +39 -0
  162. package/dist/platforma/platforma-video.service.d.ts.map +1 -0
  163. package/dist/platforma/platforma-video.service.js +301 -0
  164. package/dist/platforma/platforma-video.service.js.map +1 -0
  165. package/dist/platforma/platforma.controller.d.ts +209 -1
  166. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  167. package/dist/platforma/platforma.controller.js +208 -2
  168. package/dist/platforma/platforma.controller.js.map +1 -1
  169. package/dist/realtime/lms-realtime.controller.d.ts +2 -0
  170. package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
  171. package/dist/realtime/lms-realtime.controller.js +31 -0
  172. package/dist/realtime/lms-realtime.controller.js.map +1 -1
  173. package/dist/realtime/lms-realtime.service.d.ts +1 -1
  174. package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
  175. package/dist/realtime/lms-realtime.service.js.map +1 -1
  176. package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts +5 -0
  177. package/dist/student-xp/dto/grant-skill-card-xp.dto.d.ts.map +1 -0
  178. package/dist/student-xp/dto/grant-skill-card-xp.dto.js +26 -0
  179. package/dist/student-xp/dto/grant-skill-card-xp.dto.js.map +1 -0
  180. package/dist/student-xp/student-xp.controller.d.ts +15 -0
  181. package/dist/student-xp/student-xp.controller.d.ts.map +1 -1
  182. package/dist/student-xp/student-xp.controller.js +24 -0
  183. package/dist/student-xp/student-xp.controller.js.map +1 -1
  184. package/dist/student-xp/student-xp.service.d.ts +16 -0
  185. package/dist/student-xp/student-xp.service.d.ts.map +1 -1
  186. package/dist/student-xp/student-xp.service.js +51 -1
  187. package/dist/student-xp/student-xp.service.js.map +1 -1
  188. package/hedhog/data/evaluation_topic.yaml +17 -0
  189. package/hedhog/data/menu.yaml +0 -17
  190. package/hedhog/data/queue_definition.yaml +48 -0
  191. package/hedhog/data/route.yaml +94 -124
  192. package/hedhog/data/setting_group.yaml +19 -19
  193. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +337 -41
  194. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
  195. package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
  196. package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
  197. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +69 -4
  198. package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +420 -0
  199. package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +308 -0
  200. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +158 -45
  201. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
  202. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
  203. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -63
  204. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-panel.tsx.ejs +8 -3
  205. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +31 -8
  206. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-overlay.tsx.ejs +16 -9
  207. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +201 -401
  208. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +378 -690
  209. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -2
  210. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +3 -9
  211. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +1 -1
  212. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
  213. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +6 -10
  214. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +49 -0
  215. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -3
  216. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -1
  217. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +106 -0
  218. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +28 -1
  219. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +0 -2
  220. package/hedhog/frontend/app/courses/page.tsx.ejs +85 -9
  221. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
  222. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
  223. package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
  224. package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
  225. package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
  226. package/hedhog/frontend/app/paths/page.tsx.ejs +38 -4
  227. package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
  228. package/hedhog/frontend/messages/en.json +44 -28
  229. package/hedhog/frontend/messages/pt.json +47 -29
  230. package/hedhog/table/course_enrollment.yaml +3 -0
  231. package/hedhog/table/course_export.yaml +62 -0
  232. package/hedhog/table/lesson_view_event.yaml +66 -0
  233. package/package.json +14 -9
  234. package/src/bitcode-wallet/bitcode-wallet.service.ts +43 -4
  235. package/src/course/course-export-scorm12-worker.service.ts +124 -0
  236. package/src/course/course-export-scorm12.service.ts +668 -0
  237. package/src/course/course-export.service.ts +280 -0
  238. package/src/course/course-structure.controller.ts +16 -2
  239. package/src/course/course-structure.service.ts +100 -7
  240. package/src/course/course-video-agent-pipeline.service.ts +471 -0
  241. package/src/course/course-video-hls.service.ts +966 -0
  242. package/src/course/course.controller.ts +33 -19
  243. package/src/course/course.mcp-tools.ts +1 -1
  244. package/src/course/course.module.ts +16 -0
  245. package/src/course/course.service.ts +119 -61
  246. package/src/course/dto/cleanup-course-storage.dto.ts +1 -0
  247. package/src/course/dto/cleanup-upload-history.dto.ts +1 -1
  248. package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
  249. package/src/course/dto/create-course-export.dto.ts +56 -0
  250. package/src/course/dto/create-course-structure-lesson.dto.ts +4 -3
  251. package/src/course/ffmpeg.util.ts +65 -0
  252. package/src/course/lms-bulk-upload-automation.service.ts +156 -6
  253. package/src/course/lms-bulk-upload-infra.service.ts +39 -6
  254. package/src/course/lms-bulk-upload.controller.ts +32 -2
  255. package/src/course/lms-bulk-upload.service.ts +70 -7
  256. package/src/course/lms-setting.controller.ts +4 -2
  257. package/src/course/scorm12-schemas.ts +9 -0
  258. package/src/enterprise/training/training-student.service.ts +221 -2
  259. package/src/evaluation/evaluation.service.ts +123 -0
  260. package/src/exam/dto/create-standalone-question.dto.ts +66 -0
  261. package/src/exam/exam.module.ts +2 -1
  262. package/src/exam/exam.service.ts +86 -0
  263. package/src/exam/question.controller.ts +28 -0
  264. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +205 -31
  265. package/src/lms-commerce-access.subscriber.ts +88 -0
  266. package/src/lms.module.ts +16 -5
  267. package/src/platforma/dto/heartbeat.dto.ts +30 -0
  268. package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
  269. package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
  270. package/src/platforma/platforma-heartbeat.service.ts +33 -0
  271. package/src/platforma/platforma-performance.service.ts +606 -0
  272. package/src/platforma/platforma-search.service.ts +48 -0
  273. package/src/platforma/platforma-video.service.ts +346 -0
  274. package/src/platforma/platforma.controller.ts +137 -1
  275. package/src/platforma/platforma.service.ts +268 -268
  276. package/src/realtime/lms-realtime.controller.ts +27 -1
  277. package/src/realtime/lms-realtime.service.ts +2 -1
  278. package/src/student-xp/dto/grant-skill-card-xp.dto.ts +10 -0
  279. package/src/student-xp/student-xp.controller.ts +18 -2
  280. package/src/student-xp/student-xp.service.ts +84 -2
  281. package/hedhog/data/video_resolution_profile.yaml +0 -7
  282. package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +0 -607
  283. package/hedhog/table/course_video_resolution_profile.yaml +0 -22
  284. package/hedhog/table/video_resolution_profile.yaml +0 -18
  285. package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +0 -16
  286. package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +0 -16
  287. package/src/video-resolution-profile/video-resolution-profile.controller.ts +0 -62
  288. package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +0 -128
  289. package/src/video-resolution-profile/video-resolution-profile.module.ts +0 -13
  290. package/src/video-resolution-profile/video-resolution-profile.service.ts +0 -117
@@ -7,8 +7,17 @@ import {
7
7
  PageHeader,
8
8
  PaginationFooter,
9
9
  SearchBar,
10
- ViewModeToggle,
11
10
  } from '@/components/entity-list';
11
+ import {
12
+ Tooltip,
13
+ TooltipContent,
14
+ TooltipProvider,
15
+ TooltipTrigger,
16
+ } from '@/components/ui/tooltip';
17
+ import {
18
+ ToggleGroup,
19
+ ToggleGroupItem,
20
+ } from '@/components/ui/toggle-group';
12
21
  import { Badge } from '@/components/ui/badge';
13
22
  import { Button } from '@/components/ui/button';
14
23
  import { Card, CardContent } from '@/components/ui/card';
@@ -39,10 +48,13 @@ import { motion } from 'framer-motion';
39
48
  import {
40
49
  AlertTriangle,
41
50
  BarChart3,
51
+ CalendarDays,
42
52
  CalendarIcon,
43
53
  Clock,
44
54
  Eye,
55
+ LayoutGrid,
45
56
  Laptop,
57
+ List,
46
58
  Loader2,
47
59
  MapPin,
48
60
  Monitor,
@@ -59,6 +71,7 @@ import { useRouter } from 'next/navigation';
59
71
  import { useEffect, useMemo, useRef, useState } from 'react';
60
72
  import { toast } from 'sonner';
61
73
  import { ClassFormSheet } from '../_components/class-form-sheet';
74
+ import { ClassesCalendarView } from './_components/classes-calendar-view';
62
75
 
63
76
  // ── Types ─────────────────────────────────────────────────────────────────────
64
77
 
@@ -229,7 +242,7 @@ function mapApiClass(item: ApiClass): Turma {
229
242
  };
230
243
  }
231
244
 
232
- type ViewMode = 'cards' | 'list';
245
+ type ViewMode = 'cards' | 'list' | 'calendar';
233
246
 
234
247
  // ── Constants ─────────────────────────────────────────────────────────────────
235
248
 
@@ -269,7 +282,7 @@ const TIPO_ICON: Record<string, LucideIcon> = {
269
282
  hibrida: Laptop,
270
283
  };
271
284
 
272
- const PAGE_SIZES = [6, 12, 24];
285
+ const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
273
286
 
274
287
  // ── Animations ────────────────────────────────────────────────────────────────
275
288
 
@@ -306,15 +319,49 @@ export default function TurmasPage() {
306
319
  const [viewMode, setViewMode] = usePersistedViewMode<ViewMode>({
307
320
  storageKey: 'lms:classes:view-mode',
308
321
  defaultValue: 'cards',
309
- allowedValues: ['cards', 'list'],
322
+ allowedValues: ['cards', 'list', 'calendar'],
310
323
  });
311
324
 
312
325
  // Pagination
313
326
  const [currentPage, setCurrentPage] = useState(1);
327
+
328
+ const { data: generalSettings } = useQuery<{
329
+ data: Array<{ slug: string; value: string }>;
330
+ }>({
331
+ queryKey: ['setting-group-general'],
332
+ queryFn: async () => {
333
+ const response = await request<{
334
+ data: Array<{ slug: string; value: string }>;
335
+ }>({
336
+ url: '/setting/group/general',
337
+ method: 'GET',
338
+ });
339
+ return response.data;
340
+ },
341
+ staleTime: 5 * 60 * 1000,
342
+ });
343
+
344
+ const pageSizeOptions = useMemo(() => {
345
+ const setting = generalSettings?.data?.find(
346
+ (s) => s.slug === 'pagination-page-sizes'
347
+ );
348
+ if (!setting?.value) return DEFAULT_PAGE_SIZES;
349
+ try {
350
+ const parsed = JSON.parse(setting.value) as string[];
351
+ const sizes = parsed
352
+ .map(Number)
353
+ .filter((n) => !isNaN(n) && n > 0)
354
+ .sort((a, b) => a - b);
355
+ return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
356
+ } catch {
357
+ return DEFAULT_PAGE_SIZES;
358
+ }
359
+ }, [generalSettings]);
360
+
314
361
  const [pageSize, setPageSize] = usePersistedPageSize({
315
362
  storageKey: 'pagination:global:pageSize',
316
363
  defaultValue: 12,
317
- allowedValues: PAGE_SIZES,
364
+ allowedValues: pageSizeOptions,
318
365
  });
319
366
 
320
367
  const { data: coursesResponse } = useQuery<ApiCourseList>({
@@ -333,6 +380,9 @@ export default function TurmasPage() {
333
380
  },
334
381
  });
335
382
 
383
+ const effectivePage = viewMode === 'calendar' ? 1 : currentPage;
384
+ const effectivePageSize = viewMode === 'calendar' ? 500 : pageSize;
385
+
336
386
  const {
337
387
  data: classesResponse,
338
388
  refetch: refetchClasses,
@@ -341,8 +391,8 @@ export default function TurmasPage() {
341
391
  } = useQuery<ApiClassList>({
342
392
  queryKey: [
343
393
  'lms-classes-list',
344
- currentPage,
345
- pageSize,
394
+ effectivePage,
395
+ effectivePageSize,
346
396
  buscaDebounced,
347
397
  filtroStatusInput,
348
398
  filtroTipoInput,
@@ -359,8 +409,8 @@ export default function TurmasPage() {
359
409
  url: '/lms/classes',
360
410
  method: 'GET',
361
411
  params: {
362
- page: currentPage,
363
- pageSize,
412
+ page: effectivePage,
413
+ pageSize: effectivePageSize,
364
414
  ...(buscaDebounced ? { search: buscaDebounced } : {}),
365
415
  ...(filtroStatusInput !== 'todos'
366
416
  ? { status: toApiStatus(filtroStatusInput) }
@@ -742,13 +792,62 @@ export default function TurmasPage() {
742
792
  className: 'sm:w-56',
743
793
  },
744
794
  ]}
745
- afterSearchButton={
746
- <ViewModeToggle
747
- viewMode={viewMode}
748
- onViewModeChange={setViewMode}
749
- listLabel={t('viewMode.list')}
750
- cardsLabel={t('viewMode.cards')}
751
- />
795
+ actions={
796
+ <TooltipProvider>
797
+ <ToggleGroup
798
+ type="single"
799
+ value={viewMode}
800
+ onValueChange={(value) => {
801
+ if (
802
+ value === 'cards' ||
803
+ value === 'list' ||
804
+ value === 'calendar'
805
+ ) {
806
+ setViewMode(value);
807
+ }
808
+ }}
809
+ variant="outline"
810
+ size="sm"
811
+ className="shrink-0"
812
+ >
813
+ <Tooltip>
814
+ <TooltipTrigger asChild>
815
+ <ToggleGroupItem
816
+ value="cards"
817
+ aria-label={t('viewMode.cards')}
818
+ className="cursor-pointer"
819
+ >
820
+ <LayoutGrid className="h-4 w-4" />
821
+ </ToggleGroupItem>
822
+ </TooltipTrigger>
823
+ <TooltipContent>{t('viewMode.cards')}</TooltipContent>
824
+ </Tooltip>
825
+ <Tooltip>
826
+ <TooltipTrigger asChild>
827
+ <ToggleGroupItem
828
+ value="list"
829
+ aria-label={t('viewMode.list')}
830
+ className="cursor-pointer"
831
+ >
832
+ <List className="h-4 w-4" />
833
+ </ToggleGroupItem>
834
+ </TooltipTrigger>
835
+ <TooltipContent>{t('viewMode.list')}</TooltipContent>
836
+ </Tooltip>
837
+ <Tooltip>
838
+ <TooltipTrigger asChild>
839
+ <ToggleGroupItem
840
+ value="calendar"
841
+ aria-label={t('viewMode.calendar')}
842
+ className="cursor-pointer"
843
+ >
844
+ <CalendarDays className="h-4 w-4" />
845
+ </ToggleGroupItem>
846
+ </TooltipTrigger>
847
+ <TooltipContent>{t('viewMode.calendar')}</TooltipContent>
848
+ </Tooltip>
849
+ </ToggleGroup>
850
+ </TooltipProvider>
752
851
  }
753
852
  />
754
853
  <div className="flex items-center justify-between gap-3">
@@ -774,7 +873,13 @@ export default function TurmasPage() {
774
873
 
775
874
  {/* Classes list */}
776
875
  {loading ? (
777
- viewMode === 'cards' ? (
876
+ viewMode === 'calendar' ? (
877
+ <div className="grid grid-cols-7 gap-px">
878
+ {Array.from({ length: 42 }).map((_, i) => (
879
+ <div key={i} className="h-16 animate-pulse rounded bg-muted/30" />
880
+ ))}
881
+ </div>
882
+ ) : viewMode === 'cards' ? (
778
883
  <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
779
884
  {Array.from({ length: 6 }).map((_, i) => (
780
885
  <Card key={i} className="overflow-hidden border-border/70">
@@ -1018,7 +1123,7 @@ export default function TurmasPage() {
1018
1123
  );
1019
1124
  })}
1020
1125
  </motion.div>
1021
- ) : (
1126
+ ) : viewMode === 'list' ? (
1022
1127
  <div className="overflow-hidden rounded-xl border border-border/70">
1023
1128
  <Table>
1024
1129
  <TableHeader>
@@ -1146,10 +1251,12 @@ export default function TurmasPage() {
1146
1251
  </TableBody>
1147
1252
  </Table>
1148
1253
  </div>
1254
+ ) : (
1255
+ <ClassesCalendarView turmas={visibleTurmas} />
1149
1256
  )}
1150
1257
 
1151
1258
  {/* Pagination footer */}
1152
- {!loading && visibleTurmas.length > 0 && (
1259
+ {!loading && visibleTurmas.length > 0 && viewMode !== 'calendar' && (
1153
1260
  <div className="mt-6">
1154
1261
  <PaginationFooter
1155
1262
  currentPage={currentPage}
@@ -1160,7 +1267,7 @@ export default function TurmasPage() {
1160
1267
  setPageSize(value);
1161
1268
  setCurrentPage(1);
1162
1269
  }}
1163
- pageSizeOptions={PAGE_SIZES}
1270
+ pageSizeOptions={pageSizeOptions}
1164
1271
  />
1165
1272
  </div>
1166
1273
  )}
@@ -9,14 +9,16 @@ import {
9
9
  ResizablePanel,
10
10
  ResizablePanelGroup,
11
11
  } from '@/components/ui/resizable';
12
- import { Sheet, SheetHeader, SheetTitle } from '@/components/ui/sheet';
13
12
  import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
13
+ import { Sheet, SheetHeader, SheetTitle } from '@/components/ui/sheet';
14
14
  import { useIsMobile } from '@/hooks/use-mobile';
15
15
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
16
16
  import { useIsMutating, useQueryClient } from '@tanstack/react-query';
17
17
  import { AlertCircle, Menu, RefreshCw } from 'lucide-react';
18
18
  import { useTranslations } from 'next-intl';
19
+ import { useRouter, useSearchParams } from 'next/navigation';
19
20
 
21
+ import { useLmsRealtimeRefresh } from '../../_lib/hooks/use-lms-realtime-refresh';
20
22
  import { ConfirmDialog } from './structure/_components/confirm-dialog';
21
23
  import { CourseTreePanel } from './structure/_components/course-tree-panel';
22
24
  import { CourseTreeSkeleton } from './structure/_components/course-tree-skeleton';
@@ -37,7 +39,6 @@ import {
37
39
  usePasteSessionsMutation,
38
40
  } from './structure/_data/use-course-structure-mutations';
39
41
  import { useCourseStructureQuery } from './structure/_data/use-course-structure-query';
40
- import { useLmsRealtimeRefresh } from '../../_lib/hooks/use-lms-realtime-refresh';
41
42
 
42
43
  interface Props {
43
44
  params: Promise<{ id: string }>;
@@ -60,6 +61,8 @@ export default function CourseStructurePage({ params }: Props) {
60
61
  const isMobile = useIsMobile();
61
62
  const { request } = useApp();
62
63
  const queryClient = useQueryClient();
64
+ const router = useRouter();
65
+ const searchParams = useSearchParams();
63
66
 
64
67
  const { data: courseSummary } = useQuery<ApiCourseSummary>({
65
68
  queryKey: ['lms-course-detail', id],
@@ -85,6 +88,10 @@ export default function CourseStructurePage({ params }: Props) {
85
88
 
86
89
  const setStructureFromApi = useStructureStore((s) => s.setStructureFromApi);
87
90
  const setCourseId = useStructureStore((s) => s.setCourseId);
91
+ const selectItem = useStructureStore((s) => s.selectItem);
92
+
93
+ const restoredFromUrl = useRef(false);
94
+ const suppressNextSyncRef = useRef(false);
88
95
 
89
96
  useEffect(() => {
90
97
  setCourseId(id);
@@ -126,6 +133,31 @@ export default function CourseStructurePage({ params }: Props) {
126
133
  sessions: apiSessions,
127
134
  lessons: apiLessons,
128
135
  });
136
+
137
+ if (!restoredFromUrl.current) {
138
+ restoredFromUrl.current = true;
139
+ const sel = searchParams.get('sel');
140
+ const type = searchParams.get('type') as
141
+ | 'course'
142
+ | 'session'
143
+ | 'lesson'
144
+ | null;
145
+ if (
146
+ sel &&
147
+ (type === 'course' || type === 'session' || type === 'lesson')
148
+ ) {
149
+ const exists =
150
+ type === 'course'
151
+ ? mergedCourse?.id === sel
152
+ : type === 'session'
153
+ ? apiSessions.some((s) => s.id === sel)
154
+ : apiLessons.some((l) => l.id === sel);
155
+ if (exists) {
156
+ suppressNextSyncRef.current = true;
157
+ selectItem(sel, type);
158
+ }
159
+ }
160
+ }
129
161
  }
130
162
  }, [
131
163
  apiCourse,
@@ -159,6 +191,33 @@ export default function CourseStructurePage({ params }: Props) {
159
191
  const copiedType = useStructureStore((s) => s.copiedType);
160
192
  const copiedIds = useStructureStore((s) => s.copiedIds);
161
193
 
194
+ // ── URL sync: selection → URL ──────────────────────────────────────────────
195
+ useEffect(() => {
196
+ if (!restoredFromUrl.current) return;
197
+ if (suppressNextSyncRef.current) {
198
+ suppressNextSyncRef.current = false;
199
+ return;
200
+ }
201
+ const params = new URLSearchParams(searchParams.toString());
202
+ if (activeItemId && activeItemType) {
203
+ params.set('sel', activeItemId);
204
+ params.set('type', activeItemType);
205
+ }
206
+ params.delete('tab');
207
+ router.replace(`?${params.toString()}`, { scroll: false });
208
+ }, [activeItemId, activeItemType]); // eslint-disable-line react-hooks/exhaustive-deps
209
+
210
+ const handleTabChange = useCallback(
211
+ (tab: string) => {
212
+ const params = new URLSearchParams(searchParams.toString());
213
+ params.set('tab', tab);
214
+ router.replace(`?${params.toString()}`, { scroll: false });
215
+ },
216
+ [router, searchParams]
217
+ );
218
+
219
+ const defaultTab = searchParams.get('tab') ?? undefined;
220
+
162
221
  useCourseStructureShortcuts({
163
222
  searchRef,
164
223
  detailPanelRef,
@@ -277,7 +336,10 @@ export default function CourseStructurePage({ params }: Props) {
277
336
  className="flex flex-col h-full min-h-0"
278
337
  >
279
338
  <div className="h-full min-h-0">
280
- <DetailPanel />
339
+ <DetailPanel
340
+ defaultTab={defaultTab}
341
+ onTabChange={handleTabChange}
342
+ />
281
343
  </div>
282
344
  </div>
283
345
  </ResizablePanel>
@@ -305,7 +367,10 @@ export default function CourseStructurePage({ params }: Props) {
305
367
  className="flex-1 min-h-0 border rounded-lg overflow-hidden"
306
368
  >
307
369
  <div className="h-full min-h-0">
308
- <DetailPanel />
370
+ <DetailPanel
371
+ defaultTab={defaultTab}
372
+ onTabChange={handleTabChange}
373
+ />
309
374
  </div>
310
375
  </div>
311
376