@hed-hog/lms 0.0.365 → 0.0.370

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 (243) hide show
  1. package/dist/certificate/certificate.controller.d.ts +1 -1
  2. package/dist/certificate/certificate.controller.d.ts.map +1 -1
  3. package/dist/certificate/certificate.controller.js +4 -2
  4. package/dist/certificate/certificate.controller.js.map +1 -1
  5. package/dist/certificate/certificate.service.d.ts +50 -0
  6. package/dist/certificate/certificate.service.d.ts.map +1 -1
  7. package/dist/certificate/certificate.service.js +73 -0
  8. package/dist/certificate/certificate.service.js.map +1 -1
  9. package/dist/class-group/class-group.controller.d.ts +1 -0
  10. package/dist/class-group/class-group.controller.d.ts.map +1 -1
  11. package/dist/class-group/class-group.service.d.ts +1 -0
  12. package/dist/class-group/class-group.service.d.ts.map +1 -1
  13. package/dist/course/course-ai-usage.service.d.ts +58 -0
  14. package/dist/course/course-ai-usage.service.d.ts.map +1 -0
  15. package/dist/course/course-ai-usage.service.js +176 -0
  16. package/dist/course/course-ai-usage.service.js.map +1 -0
  17. package/dist/course/course-audio-transcription.service.d.ts +65 -1
  18. package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
  19. package/dist/course/course-audio-transcription.service.js +381 -29
  20. package/dist/course/course-audio-transcription.service.js.map +1 -1
  21. package/dist/course/course-export-scorm12.service.d.ts +3 -0
  22. package/dist/course/course-export-scorm12.service.d.ts.map +1 -1
  23. package/dist/course/course-export-scorm12.service.js +141 -6
  24. package/dist/course/course-export-scorm12.service.js.map +1 -1
  25. package/dist/course/course-export.service.d.ts.map +1 -1
  26. package/dist/course/course-export.service.js +2 -1
  27. package/dist/course/course-export.service.js.map +1 -1
  28. package/dist/course/course-lesson.controller.d.ts +25 -3
  29. package/dist/course/course-lesson.controller.d.ts.map +1 -1
  30. package/dist/course/course-lesson.controller.js +71 -8
  31. package/dist/course/course-lesson.controller.js.map +1 -1
  32. package/dist/course/course-structure.controller.d.ts +30 -7
  33. package/dist/course/course-structure.controller.d.ts.map +1 -1
  34. package/dist/course/course-structure.controller.js +37 -4
  35. package/dist/course/course-structure.controller.js.map +1 -1
  36. package/dist/course/course-structure.service.d.ts +37 -5
  37. package/dist/course/course-structure.service.d.ts.map +1 -1
  38. package/dist/course/course-structure.service.js +165 -20
  39. package/dist/course/course-structure.service.js.map +1 -1
  40. package/dist/course/course-transcription-translation.service.d.ts +31 -0
  41. package/dist/course/course-transcription-translation.service.d.ts.map +1 -0
  42. package/dist/course/course-transcription-translation.service.js +227 -0
  43. package/dist/course/course-transcription-translation.service.js.map +1 -0
  44. package/dist/course/course-video-agent-pipeline.service.d.ts +70 -0
  45. package/dist/course/course-video-agent-pipeline.service.d.ts.map +1 -0
  46. package/dist/course/course-video-agent-pipeline.service.js +398 -0
  47. package/dist/course/course-video-agent-pipeline.service.js.map +1 -0
  48. package/dist/course/course-video-hls.service.d.ts +14 -0
  49. package/dist/course/course-video-hls.service.d.ts.map +1 -1
  50. package/dist/course/course-video-hls.service.js +25 -8
  51. package/dist/course/course-video-hls.service.js.map +1 -1
  52. package/dist/course/course.controller.d.ts +2 -0
  53. package/dist/course/course.controller.d.ts.map +1 -1
  54. package/dist/course/course.module.d.ts.map +1 -1
  55. package/dist/course/course.module.js +9 -0
  56. package/dist/course/course.module.js.map +1 -1
  57. package/dist/course/course.service.d.ts +2 -0
  58. package/dist/course/course.service.d.ts.map +1 -1
  59. package/dist/course/course.service.js +36 -2
  60. package/dist/course/course.service.js.map +1 -1
  61. package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
  62. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
  63. package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
  64. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
  65. package/dist/course/dto/create-course-export.dto.d.ts +1 -0
  66. package/dist/course/dto/create-course-export.dto.d.ts.map +1 -1
  67. package/dist/course/dto/create-course-export.dto.js +6 -0
  68. package/dist/course/dto/create-course-export.dto.js.map +1 -1
  69. package/dist/course/ffmpeg.util.d.ts +10 -0
  70. package/dist/course/ffmpeg.util.d.ts.map +1 -0
  71. package/dist/course/ffmpeg.util.js +79 -0
  72. package/dist/course/ffmpeg.util.js.map +1 -0
  73. package/dist/course/lms-bulk-upload-automation.service.d.ts +3 -1
  74. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  75. package/dist/course/lms-bulk-upload-automation.service.js +33 -16
  76. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  77. package/dist/course/lms-bulk-upload.controller.d.ts +3 -0
  78. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  79. package/dist/course/lms-bulk-upload.service.d.ts +3 -0
  80. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  81. package/dist/course/lms-bulk-upload.service.js +48 -29
  82. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  83. package/dist/course/subtitle.util.d.ts +46 -0
  84. package/dist/course/subtitle.util.d.ts.map +1 -0
  85. package/dist/course/subtitle.util.js +206 -0
  86. package/dist/course/subtitle.util.js.map +1 -0
  87. package/dist/enterprise/training/training-admin.controller.d.ts +2 -0
  88. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  89. package/dist/enterprise/training/training-admin.service.d.ts +2 -0
  90. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  91. package/dist/enterprise/training/training-student.service.d.ts +27 -0
  92. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  93. package/dist/enterprise/training/training-student.service.js +197 -10
  94. package/dist/enterprise/training/training-student.service.js.map +1 -1
  95. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +3 -1
  96. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
  97. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +19 -5
  98. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
  99. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -1
  100. package/dist/lesson-xp-map/lesson-xp-map.module.js +2 -1
  101. package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -1
  102. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
  103. package/dist/lms.module.d.ts.map +1 -1
  104. package/dist/lms.module.js +14 -0
  105. package/dist/lms.module.js.map +1 -1
  106. package/dist/platforma/dto/heartbeat.dto.d.ts +9 -0
  107. package/dist/platforma/dto/heartbeat.dto.d.ts.map +1 -0
  108. package/dist/platforma/dto/heartbeat.dto.js +50 -0
  109. package/dist/platforma/dto/heartbeat.dto.js.map +1 -0
  110. package/dist/platforma/handlers/emit-certificate.handler.d.ts +27 -0
  111. package/dist/platforma/handlers/emit-certificate.handler.d.ts.map +1 -0
  112. package/dist/platforma/handlers/emit-certificate.handler.js +117 -0
  113. package/dist/platforma/handlers/emit-certificate.handler.js.map +1 -0
  114. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts +31 -0
  115. package/dist/platforma/handlers/lesson-heartbeat.handler.d.ts.map +1 -0
  116. package/dist/platforma/handlers/lesson-heartbeat.handler.js +281 -0
  117. package/dist/platforma/handlers/lesson-heartbeat.handler.js.map +1 -0
  118. package/dist/platforma/platforma-heartbeat.service.d.ts +10 -0
  119. package/dist/platforma/platforma-heartbeat.service.d.ts.map +1 -0
  120. package/dist/platforma/platforma-heartbeat.service.js +50 -0
  121. package/dist/platforma/platforma-heartbeat.service.js.map +1 -0
  122. package/dist/platforma/platforma-performance.service.d.ts +121 -0
  123. package/dist/platforma/platforma-performance.service.d.ts.map +1 -0
  124. package/dist/platforma/platforma-performance.service.js +500 -0
  125. package/dist/platforma/platforma-performance.service.js.map +1 -0
  126. package/dist/platforma/platforma-search.service.d.ts +21 -0
  127. package/dist/platforma/platforma-search.service.d.ts.map +1 -0
  128. package/dist/platforma/platforma-search.service.js +64 -0
  129. package/dist/platforma/platforma-search.service.js.map +1 -0
  130. package/dist/platforma/platforma-video.service.d.ts +8 -0
  131. package/dist/platforma/platforma-video.service.d.ts.map +1 -1
  132. package/dist/platforma/platforma-video.service.js +45 -2
  133. package/dist/platforma/platforma-video.service.js.map +1 -1
  134. package/dist/platforma/platforma.controller.d.ts +213 -1
  135. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  136. package/dist/platforma/platforma.controller.js +159 -2
  137. package/dist/platforma/platforma.controller.js.map +1 -1
  138. package/dist/realtime/lms-realtime.controller.d.ts +2 -0
  139. package/dist/realtime/lms-realtime.controller.d.ts.map +1 -1
  140. package/dist/realtime/lms-realtime.controller.js +31 -0
  141. package/dist/realtime/lms-realtime.controller.js.map +1 -1
  142. package/dist/realtime/lms-realtime.service.d.ts +1 -1
  143. package/dist/realtime/lms-realtime.service.d.ts.map +1 -1
  144. package/dist/realtime/lms-realtime.service.js.map +1 -1
  145. package/dist/training/dto/create-training.dto.d.ts +9 -0
  146. package/dist/training/dto/create-training.dto.d.ts.map +1 -1
  147. package/dist/training/dto/create-training.dto.js +45 -1
  148. package/dist/training/dto/create-training.dto.js.map +1 -1
  149. package/dist/training/training.controller.d.ts +144 -0
  150. package/dist/training/training.controller.d.ts.map +1 -1
  151. package/dist/training/training.service.d.ts +149 -0
  152. package/dist/training/training.service.d.ts.map +1 -1
  153. package/dist/training/training.service.js +332 -167
  154. package/dist/training/training.service.js.map +1 -1
  155. package/hedhog/data/image_type.yaml +10 -0
  156. package/hedhog/data/route.yaml +251 -0
  157. package/hedhog/data/setting_group.yaml +97 -0
  158. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +139 -27
  159. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +182 -29
  160. package/hedhog/frontend/app/classes/_components/classes-calendar-view.tsx.ejs +277 -0
  161. package/hedhog/frontend/app/classes/page.tsx.ejs +127 -20
  162. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +69 -57
  163. package/hedhog/frontend/app/courses/[id]/_components/CourseIssuedCertificatesCard.tsx.ejs +168 -0
  164. package/hedhog/frontend/app/courses/[id]/structure/_components/course-ai-costs-tab.tsx.ejs +191 -0
  165. package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +81 -1
  166. package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +12 -0
  167. package/hedhog/frontend/app/courses/[id]/structure/_components/course-overview-tab.tsx.ejs +141 -30
  168. package/hedhog/frontend/app/courses/[id]/structure/_components/course-xp-overview-tab.tsx.ejs +13 -13
  169. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +69 -1
  170. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson-xp-tab.tsx.ejs +11 -23
  171. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +267 -19
  172. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +114 -86
  173. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +239 -31
  174. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +344 -59
  175. package/hedhog/frontend/app/courses/[id]/structure/_components/lesson-video-preview.tsx.ejs +200 -0
  176. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +1 -0
  177. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +3 -0
  178. package/hedhog/frontend/app/courses/[id]/structure/_components/xp-premium-pills.tsx.ejs +1 -8
  179. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -7
  180. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -0
  181. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-ai-costs.ts.ejs +40 -0
  182. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-content-overview.ts.ejs +2 -0
  183. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +25 -0
  184. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +148 -0
  185. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +157 -8
  186. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +1 -22
  187. package/hedhog/frontend/app/courses/page.tsx.ejs +66 -13
  188. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +6 -0
  189. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-calendar-tab.tsx.ejs +264 -0
  190. package/hedhog/frontend/app/enterprise/page.tsx.ejs +104 -47
  191. package/hedhog/frontend/app/exams/page.tsx.ejs +38 -4
  192. package/hedhog/frontend/app/instructors/page.tsx.ejs +87 -46
  193. package/hedhog/frontend/app/paths/page.tsx.ejs +650 -168
  194. package/hedhog/frontend/app/training/page.tsx.ejs +38 -4
  195. package/hedhog/frontend/messages/en.json +41 -12
  196. package/hedhog/frontend/messages/pt.json +44 -13
  197. package/hedhog/query/triggers.sql +33 -0
  198. package/hedhog/table/course_ai_usage.yaml +46 -0
  199. package/hedhog/table/course_enrollment.yaml +3 -0
  200. package/hedhog/table/course_lesson.yaml +3 -0
  201. package/hedhog/table/course_lesson_answer.yaml +37 -0
  202. package/hedhog/table/course_lesson_transcription_segment.yaml +8 -0
  203. package/hedhog/table/learning_path.yaml +6 -0
  204. package/hedhog/table/learning_path_module.yaml +22 -0
  205. package/hedhog/table/learning_path_step.yaml +9 -6
  206. package/hedhog/table/lesson_view_event.yaml +66 -0
  207. package/package.json +8 -7
  208. package/src/certificate/certificate.controller.ts +2 -0
  209. package/src/certificate/certificate.service.ts +99 -0
  210. package/src/course/course-ai-usage.service.ts +221 -0
  211. package/src/course/course-audio-transcription.service.ts +471 -43
  212. package/src/course/course-export-scorm12.service.ts +149 -5
  213. package/src/course/course-export.service.ts +1 -0
  214. package/src/course/course-lesson.controller.ts +59 -6
  215. package/src/course/course-structure.controller.ts +19 -1
  216. package/src/course/course-structure.service.ts +184 -10
  217. package/src/course/course-transcription-translation.service.ts +293 -0
  218. package/src/course/course-video-agent-pipeline.service.ts +471 -0
  219. package/src/course/course-video-hls.service.ts +30 -10
  220. package/src/course/course.module.ts +9 -0
  221. package/src/course/course.service.ts +46 -1
  222. package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
  223. package/src/course/dto/create-course-export.dto.ts +6 -0
  224. package/src/course/ffmpeg.util.ts +65 -0
  225. package/src/course/lms-bulk-upload-automation.service.ts +33 -8
  226. package/src/course/lms-bulk-upload.service.ts +20 -1
  227. package/src/course/subtitle.util.ts +220 -0
  228. package/src/enterprise/training/training-student.service.ts +224 -4
  229. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +14 -0
  230. package/src/lesson-xp-map/lesson-xp-map.module.ts +2 -1
  231. package/src/lms.module.ts +14 -0
  232. package/src/platforma/dto/heartbeat.dto.ts +30 -0
  233. package/src/platforma/handlers/emit-certificate.handler.ts +117 -0
  234. package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -0
  235. package/src/platforma/platforma-heartbeat.service.ts +33 -0
  236. package/src/platforma/platforma-performance.service.ts +606 -0
  237. package/src/platforma/platforma-search.service.ts +48 -0
  238. package/src/platforma/platforma-video.service.ts +59 -3
  239. package/src/platforma/platforma.controller.ts +130 -0
  240. package/src/realtime/lms-realtime.controller.ts +27 -1
  241. package/src/realtime/lms-realtime.service.ts +2 -1
  242. package/src/training/dto/create-training.dto.ts +36 -0
  243. package/src/training/training.service.ts +360 -163
@@ -7,25 +7,44 @@ import type { TranscriptionSegment } from '../_components/types';
7
7
 
8
8
  type ApiTranscriptionSegment = {
9
9
  id?: number;
10
+ locale_id?: number | null;
10
11
  start_seconds?: number | string;
11
12
  end_seconds?: number | string;
12
13
  text?: string;
13
14
  };
14
15
 
15
- export function useTranscriptionSegmentsQuery(lessonId: string | null) {
16
+ export type TranscriptionLocale = {
17
+ id: number | null;
18
+ code: string | null;
19
+ name: string | null;
20
+ region: string | null;
21
+ };
22
+
23
+ export function useTranscriptionSegmentsQuery(
24
+ lessonId: string | null,
25
+ localeId?: number | null,
26
+ ) {
16
27
  const { request } = useApp();
17
28
 
29
+ const localeParam =
30
+ localeId === undefined
31
+ ? ''
32
+ : localeId === null
33
+ ? '?locale_id=null'
34
+ : `?locale_id=${localeId}`;
35
+
18
36
  return useQuery<TranscriptionSegment[]>({
19
- queryKey: ['lesson-transcription-segments', lessonId],
37
+ queryKey: ['lesson-transcription-segments', lessonId, localeId ?? 'all'],
20
38
  enabled: Boolean(lessonId),
21
39
  queryFn: async () => {
22
40
  const res = await request<ApiTranscriptionSegment[]>({
23
- url: `/lms/lessons/${lessonId}/transcription-segments`,
41
+ url: `/lms/lessons/${lessonId}/transcription-segments${localeParam}`,
24
42
  method: 'GET',
25
43
  });
26
44
 
27
45
  return (res.data ?? []).map((segment) => ({
28
46
  id: Number(segment.id ?? 0),
47
+ localeId: segment.locale_id ?? null,
29
48
  startSeconds: Number(segment.start_seconds ?? 0),
30
49
  endSeconds: Number(segment.end_seconds ?? 0),
31
50
  text: String(segment.text ?? ''),
@@ -35,13 +54,38 @@ export function useTranscriptionSegmentsQuery(lessonId: string | null) {
35
54
  });
36
55
  }
37
56
 
57
+ export function useTranscriptionLocalesQuery(lessonId: string | null) {
58
+ const { request } = useApp();
59
+
60
+ return useQuery<TranscriptionLocale[]>({
61
+ queryKey: ['lesson-transcription-locales', lessonId],
62
+ enabled: Boolean(lessonId),
63
+ queryFn: async () => {
64
+ const res = await request<TranscriptionLocale[]>({
65
+ url: `/lms/lessons/${lessonId}/transcription-locales`,
66
+ method: 'GET',
67
+ });
68
+ return res.data ?? [];
69
+ },
70
+ initialData: [],
71
+ });
72
+ }
73
+
38
74
  export function useUpdateTranscriptionSegmentsMutation(
39
- lessonId: string | null
75
+ lessonId: string | null,
76
+ localeId?: number | null,
40
77
  ) {
41
78
  const { request } = useApp();
42
79
  const [isPending, setIsPending] = useState(false);
43
80
  const [error, setError] = useState<Error | null>(null);
44
81
 
82
+ const localeParam =
83
+ localeId === undefined
84
+ ? ''
85
+ : localeId === null
86
+ ? '?locale_id=null'
87
+ : `?locale_id=${localeId}`;
88
+
45
89
  const mutate = useCallback(
46
90
  async (
47
91
  segments: Array<{
@@ -49,13 +93,13 @@ export function useUpdateTranscriptionSegmentsMutation(
49
93
  endSeconds: number;
50
94
  text: string;
51
95
  }>,
52
- options?: { onSuccess?: () => void; onError?: (err: Error) => void }
96
+ options?: { onSuccess?: () => void; onError?: (err: Error) => void },
53
97
  ) => {
54
98
  setIsPending(true);
55
99
  setError(null);
56
100
  try {
57
101
  await request({
58
- url: `/lms/lessons/${lessonId}/transcription-segments`,
102
+ url: `/lms/lessons/${lessonId}/transcription-segments${localeParam}`,
59
103
  method: 'PUT',
60
104
  data: { segments },
61
105
  });
@@ -69,7 +113,7 @@ export function useUpdateTranscriptionSegmentsMutation(
69
113
  setIsPending(false);
70
114
  }
71
115
  },
72
- [lessonId, request]
116
+ [lessonId, localeParam, request],
73
117
  );
74
118
 
75
119
  return { mutate, isPending, error };
@@ -99,7 +143,112 @@ export function useStartTranscriptionMutation(lessonId: string | null) {
99
143
  setIsPending(false);
100
144
  }
101
145
  },
102
- [lessonId, request]
146
+ [lessonId, request],
147
+ );
148
+
149
+ return { mutate, isPending, error };
150
+ }
151
+
152
+ export function useDeleteTranscriptionLocaleMutation(lessonId: string | null) {
153
+ const { request } = useApp();
154
+ const [isPending, setIsPending] = useState(false);
155
+ const [error, setError] = useState<Error | null>(null);
156
+
157
+ const mutate = useCallback(
158
+ async (
159
+ localeId: number | null,
160
+ options?: { onSuccess?: () => void; onError?: (err: Error) => void },
161
+ ) => {
162
+ const localeParam = localeId === null ? '?locale_id=null' : `?locale_id=${localeId}`;
163
+ setIsPending(true);
164
+ setError(null);
165
+ try {
166
+ await request({
167
+ url: `/lms/lessons/${lessonId}/transcription-segments${localeParam}`,
168
+ method: 'DELETE',
169
+ });
170
+ options?.onSuccess?.();
171
+ } catch (err) {
172
+ const e = err instanceof Error ? err : new Error(String(err));
173
+ setError(e);
174
+ options?.onError?.(e);
175
+ throw e;
176
+ } finally {
177
+ setIsPending(false);
178
+ }
179
+ },
180
+ [lessonId, request],
181
+ );
182
+
183
+ return { mutate, isPending, error };
184
+ }
185
+
186
+ export function useDeleteCourseTranscriptionsMutation(courseId: string | null) {
187
+ const { request } = useApp();
188
+ const [isPending, setIsPending] = useState(false);
189
+ const [error, setError] = useState<Error | null>(null);
190
+
191
+ const mutate = useCallback(
192
+ async (options?: {
193
+ onSuccess?: (result: { deleted: number }) => void;
194
+ onError?: (err: Error) => void;
195
+ }) => {
196
+ setIsPending(true);
197
+ setError(null);
198
+ try {
199
+ const res = await request<{ success: boolean; deleted: number }>({
200
+ url: `/lms/courses/${courseId}/structure/transcription-segments`,
201
+ method: 'DELETE',
202
+ });
203
+ options?.onSuccess?.(res.data ?? { deleted: 0 });
204
+ } catch (err) {
205
+ const e = err instanceof Error ? err : new Error(String(err));
206
+ setError(e);
207
+ options?.onError?.(e);
208
+ throw e;
209
+ } finally {
210
+ setIsPending(false);
211
+ }
212
+ },
213
+ [courseId, request],
214
+ );
215
+
216
+ return { mutate, isPending, error };
217
+ }
218
+
219
+ export function useTranslateTranscriptionMutation(lessonId: string | null) {
220
+ const { request } = useApp();
221
+ const [isPending, setIsPending] = useState(false);
222
+ const [error, setError] = useState<Error | null>(null);
223
+
224
+ const mutate = useCallback(
225
+ async (
226
+ targetLocaleId: number,
227
+ sourceLocaleId?: number | null,
228
+ options?: {
229
+ onSuccess?: (result: { queueJobId: number; status: string }) => void;
230
+ onError?: (err: Error) => void;
231
+ },
232
+ ) => {
233
+ setIsPending(true);
234
+ setError(null);
235
+ try {
236
+ const res = await request<{ queueJobId: number; status: string }>({
237
+ url: `/lms/lessons/${lessonId}/transcription/translate`,
238
+ method: 'POST',
239
+ data: { targetLocaleId, sourceLocaleId: sourceLocaleId ?? null },
240
+ });
241
+ options?.onSuccess?.(res.data ?? { queueJobId: 0, status: 'pending' });
242
+ } catch (err) {
243
+ const e = err instanceof Error ? err : new Error(String(err));
244
+ setError(e);
245
+ options?.onError?.(e);
246
+ throw e;
247
+ } finally {
248
+ setIsPending(false);
249
+ }
250
+ },
251
+ [lessonId, request],
103
252
  );
104
253
 
105
254
  return { mutate, isPending, error };
@@ -8,26 +8,22 @@ import {
8
8
  TooltipTrigger,
9
9
  } from '@/components/ui/tooltip';
10
10
  import { cn } from '@/lib/utils';
11
- import { Eye, Pencil, Trash2 } from 'lucide-react';
11
+ import { Eye, Trash2 } from 'lucide-react';
12
12
  import type { MouseEvent } from 'react';
13
13
 
14
14
  type CourseRowActionsProps = {
15
15
  className?: string;
16
16
  onView: () => void;
17
- onEdit: () => void;
18
17
  onDelete: (event: MouseEvent<HTMLButtonElement>) => void;
19
18
  viewLabel: string;
20
- editLabel: string;
21
19
  deleteLabel: string;
22
20
  };
23
21
 
24
22
  export function CourseRowActions({
25
23
  className,
26
24
  onView,
27
- onEdit,
28
25
  onDelete,
29
26
  viewLabel,
30
- editLabel,
31
27
  deleteLabel,
32
28
  }: CourseRowActionsProps) {
33
29
  return (
@@ -50,23 +46,6 @@ export function CourseRowActions({
50
46
  <TooltipContent>{viewLabel}</TooltipContent>
51
47
  </Tooltip>
52
48
 
53
- <Tooltip>
54
- <TooltipTrigger asChild>
55
- <Button
56
- type="button"
57
- variant="ghost"
58
- size="icon"
59
- className="h-8 w-8"
60
- onClick={onEdit}
61
- aria-label={editLabel}
62
- >
63
- <Pencil className="size-4" />
64
- <span className="sr-only">{editLabel}</span>
65
- </Button>
66
- </TooltipTrigger>
67
- <TooltipContent>{editLabel}</TooltipContent>
68
- </Tooltip>
69
-
70
49
  <Tooltip>
71
50
  <TooltipTrigger asChild>
72
51
  <Button
@@ -318,7 +318,7 @@ const OFFERING_TYPE_COLOR: Record<string, string> = {
318
318
  hibrido: 'bg-teal-50 text-teal-700 border-teal-200',
319
319
  };
320
320
 
321
- const PAGE_SIZES = [6, 12, 24] as const;
321
+ const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
322
322
 
323
323
  // ── Seed Data ─────────────────────────────────────────────────────────────────
324
324
 
@@ -388,10 +388,44 @@ export default function CursosPage() {
388
388
 
389
389
  // Pagination
390
390
  const [currentPage, setCurrentPage] = useState(1);
391
+
392
+ const { data: generalSettings } = useQuery<{
393
+ data: Array<{ slug: string; value: string }>;
394
+ }>({
395
+ queryKey: ['setting-group-general'],
396
+ queryFn: async () => {
397
+ const response = await request<{
398
+ data: Array<{ slug: string; value: string }>;
399
+ }>({
400
+ url: '/setting/group/general',
401
+ method: 'GET',
402
+ });
403
+ return response.data;
404
+ },
405
+ staleTime: 5 * 60 * 1000,
406
+ });
407
+
408
+ const pageSizeOptions = useMemo(() => {
409
+ const setting = generalSettings?.data?.find(
410
+ (s) => s.slug === 'pagination-page-sizes'
411
+ );
412
+ if (!setting?.value) return DEFAULT_PAGE_SIZES;
413
+ try {
414
+ const parsed = JSON.parse(setting.value) as string[];
415
+ const sizes = parsed
416
+ .map(Number)
417
+ .filter((n) => !isNaN(n) && n > 0)
418
+ .sort((a, b) => a - b);
419
+ return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
420
+ } catch {
421
+ return DEFAULT_PAGE_SIZES;
422
+ }
423
+ }, [generalSettings]);
424
+
391
425
  const [pageSize, setPageSize] = usePersistedPageSize({
392
426
  storageKey: 'pagination:global:pageSize',
393
427
  defaultValue: 12,
394
- allowedValues: PAGE_SIZES,
428
+ allowedValues: pageSizeOptions,
395
429
  });
396
430
 
397
431
  const form = useForm<CourseSheetFormValues>({
@@ -1091,7 +1125,7 @@ export default function CursosPage() {
1091
1125
  ],
1092
1126
  },
1093
1127
  ]}
1094
- afterSearchButton={
1128
+ actions={
1095
1129
  <ViewModeToggle
1096
1130
  viewMode={viewMode}
1097
1131
  onViewModeChange={setViewMode}
@@ -1292,12 +1326,9 @@ export default function CursosPage() {
1292
1326
  />
1293
1327
  ) : (
1294
1328
  <div className="relative">
1295
- {cardsRefreshing && (
1296
- <div className="absolute inset-0 z-10 rounded-2xl bg-background/55 backdrop-blur-[1px]" />
1297
- )}
1298
1329
  {viewMode === 'cards' ? (
1299
1330
  <motion.div
1300
- className={`grid gap-4 sm:grid-cols-2 lg:grid-cols-3 ${cardsRefreshing ? 'pointer-events-none' : ''}`}
1331
+ className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
1301
1332
  variants={stagger}
1302
1333
  initial="hidden"
1303
1334
  animate="show"
@@ -1366,12 +1397,10 @@ export default function CursosPage() {
1366
1397
  <CourseRowActions
1367
1398
  className="ml-auto -mr-1"
1368
1399
  onView={() => goToCourseDetails(curso)}
1369
- onEdit={() => goToCourseDetails(curso)}
1370
1400
  onDelete={(event) =>
1371
1401
  void openDeleteDialog(curso, event)
1372
1402
  }
1373
1403
  viewLabel={t('table.actions.viewDetails')}
1374
- editLabel={t('table.actions.edit')}
1375
1404
  deleteLabel={t('table.actions.delete')}
1376
1405
  />
1377
1406
  </div>
@@ -1388,6 +1417,19 @@ export default function CursosPage() {
1388
1417
  >
1389
1418
  {getStatusLabel(curso)}
1390
1419
  </Badge>
1420
+ {curso.status === 'publicado' && (
1421
+ <span
1422
+ className={`inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-medium ${
1423
+ curso.listado
1424
+ ? 'border-emerald-200 bg-emerald-50 text-emerald-700'
1425
+ : 'border-amber-200 bg-amber-50 text-amber-700'
1426
+ }`}
1427
+ >
1428
+ {curso.listado
1429
+ ? t('status.listed')
1430
+ : t('status.unlisted')}
1431
+ </span>
1432
+ )}
1391
1433
  {deletingCourseIds.has(curso.id) && (
1392
1434
  <motion.div
1393
1435
  initial={{ opacity: 0, scale: 0.8 }}
@@ -1464,7 +1506,7 @@ export default function CursosPage() {
1464
1506
  </motion.div>
1465
1507
  ) : (
1466
1508
  <div
1467
- className={`overflow-hidden rounded-xl border border-border/70 ${cardsRefreshing ? 'pointer-events-none' : ''}`}
1509
+ className="overflow-hidden rounded-xl border border-border/70"
1468
1510
  >
1469
1511
  <Table>
1470
1512
  <TableHeader>
@@ -1558,6 +1600,19 @@ export default function CursosPage() {
1558
1600
  >
1559
1601
  {getStatusLabel(curso)}
1560
1602
  </Badge>
1603
+ {curso.status === 'publicado' && (
1604
+ <span
1605
+ className={`inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium ${
1606
+ curso.listado
1607
+ ? 'border-emerald-200 bg-emerald-50 text-emerald-700'
1608
+ : 'border-amber-200 bg-amber-50 text-amber-700'
1609
+ }`}
1610
+ >
1611
+ {curso.listado
1612
+ ? t('status.listed')
1613
+ : t('status.unlisted')}
1614
+ </span>
1615
+ )}
1561
1616
  {deletingCourseIds.has(curso.id) && (
1562
1617
  <motion.div
1563
1618
  initial={{ opacity: 0, scale: 0.8 }}
@@ -1641,12 +1696,10 @@ export default function CursosPage() {
1641
1696
  <CourseRowActions
1642
1697
  className="ml-auto"
1643
1698
  onView={() => goToCourseDetails(curso)}
1644
- onEdit={() => goToCourseDetails(curso)}
1645
1699
  onDelete={(event) =>
1646
1700
  void openDeleteDialog(curso, event)
1647
1701
  }
1648
1702
  viewLabel={t('table.actions.viewDetails')}
1649
- editLabel={t('table.actions.edit')}
1650
1703
  deleteLabel={t('table.actions.delete')}
1651
1704
  />
1652
1705
  </TableCell>
@@ -1672,7 +1725,7 @@ export default function CursosPage() {
1672
1725
  setPageSize(nextPageSize);
1673
1726
  setCurrentPage(1);
1674
1727
  }}
1675
- pageSizeOptions={PAGE_SIZES}
1728
+ pageSizeOptions={pageSizeOptions}
1676
1729
  selectedCount={selectedIds.size}
1677
1730
  />
1678
1731
  </div>
@@ -25,6 +25,7 @@ import { notFound, useParams, useRouter } from 'next/navigation';
25
25
  import { useState } from 'react';
26
26
  import { AdministratorsTab } from '../_components/enterprise-administrators-tab';
27
27
  import { ClassesTab } from '../_components/enterprise-classes-tab';
28
+ import { ClassesCalendarTab } from '../_components/enterprise-classes-calendar-tab';
28
29
  import { CoursesTab } from '../_components/enterprise-courses-tab';
29
30
  import { EnterpriseSheet } from '../_components/enterprise-sheet';
30
31
  import { StudentsTab } from '../_components/enterprise-students-tab';
@@ -265,6 +266,7 @@ export default function EnterpriseDetailPage() {
265
266
  <TabsTrigger value="overview">{t('tabs.overview')}</TabsTrigger>
266
267
  <TabsTrigger value="crm">{t('tabs.crm')}</TabsTrigger>
267
268
  <TabsTrigger value="classes">{t('tabs.classes')}</TabsTrigger>
269
+ <TabsTrigger value="classesCalendar">{t('tabs.classesCalendar')}</TabsTrigger>
268
270
  <TabsTrigger value="courses">{t('tabs.courses')}</TabsTrigger>
269
271
  <TabsTrigger value="students">{t('tabs.students')}</TabsTrigger>
270
272
  <TabsTrigger value="administrators">
@@ -288,6 +290,10 @@ export default function EnterpriseDetailPage() {
288
290
  <ClassesTab enterpriseId={account.id} />
289
291
  </TabsContent>
290
292
 
293
+ <TabsContent value="classesCalendar" className="mt-4 px-1">
294
+ <ClassesCalendarTab enterpriseId={account.id} />
295
+ </TabsContent>
296
+
291
297
  <TabsContent value="courses" className="mt-4">
292
298
  <CoursesTab enterpriseId={account.id} />
293
299
  </TabsContent>