@hed-hog/lms 0.0.366 → 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 (169) 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/course/course-ai-usage.service.d.ts +58 -0
  10. package/dist/course/course-ai-usage.service.d.ts.map +1 -0
  11. package/dist/course/course-ai-usage.service.js +176 -0
  12. package/dist/course/course-ai-usage.service.js.map +1 -0
  13. package/dist/course/course-audio-transcription.service.d.ts +65 -1
  14. package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
  15. package/dist/course/course-audio-transcription.service.js +381 -29
  16. package/dist/course/course-audio-transcription.service.js.map +1 -1
  17. package/dist/course/course-export-scorm12.service.d.ts +3 -0
  18. package/dist/course/course-export-scorm12.service.d.ts.map +1 -1
  19. package/dist/course/course-export-scorm12.service.js +141 -6
  20. package/dist/course/course-export-scorm12.service.js.map +1 -1
  21. package/dist/course/course-export.service.d.ts.map +1 -1
  22. package/dist/course/course-export.service.js +2 -1
  23. package/dist/course/course-export.service.js.map +1 -1
  24. package/dist/course/course-lesson.controller.d.ts +25 -3
  25. package/dist/course/course-lesson.controller.d.ts.map +1 -1
  26. package/dist/course/course-lesson.controller.js +71 -8
  27. package/dist/course/course-lesson.controller.js.map +1 -1
  28. package/dist/course/course-structure.controller.d.ts +26 -5
  29. package/dist/course/course-structure.controller.d.ts.map +1 -1
  30. package/dist/course/course-structure.controller.js +31 -1
  31. package/dist/course/course-structure.controller.js.map +1 -1
  32. package/dist/course/course-structure.service.d.ts +37 -5
  33. package/dist/course/course-structure.service.d.ts.map +1 -1
  34. package/dist/course/course-structure.service.js +165 -20
  35. package/dist/course/course-structure.service.js.map +1 -1
  36. package/dist/course/course-transcription-translation.service.d.ts +31 -0
  37. package/dist/course/course-transcription-translation.service.d.ts.map +1 -0
  38. package/dist/course/course-transcription-translation.service.js +227 -0
  39. package/dist/course/course-transcription-translation.service.js.map +1 -0
  40. package/dist/course/course-video-agent-pipeline.service.js +7 -7
  41. package/dist/course/course-video-agent-pipeline.service.js.map +1 -1
  42. package/dist/course/course.module.d.ts.map +1 -1
  43. package/dist/course/course.module.js +4 -0
  44. package/dist/course/course.module.js.map +1 -1
  45. package/dist/course/dto/create-course-bulk-job.dto.d.ts +2 -1
  46. package/dist/course/dto/create-course-bulk-job.dto.d.ts.map +1 -1
  47. package/dist/course/dto/create-course-bulk-job.dto.js +6 -1
  48. package/dist/course/dto/create-course-bulk-job.dto.js.map +1 -1
  49. package/dist/course/dto/create-course-export.dto.d.ts +1 -0
  50. package/dist/course/dto/create-course-export.dto.d.ts.map +1 -1
  51. package/dist/course/dto/create-course-export.dto.js +6 -0
  52. package/dist/course/dto/create-course-export.dto.js.map +1 -1
  53. package/dist/course/lms-bulk-upload-automation.service.d.ts.map +1 -1
  54. package/dist/course/lms-bulk-upload-automation.service.js +26 -13
  55. package/dist/course/lms-bulk-upload-automation.service.js.map +1 -1
  56. package/dist/course/lms-bulk-upload.controller.d.ts +3 -0
  57. package/dist/course/lms-bulk-upload.controller.d.ts.map +1 -1
  58. package/dist/course/lms-bulk-upload.service.d.ts +3 -0
  59. package/dist/course/lms-bulk-upload.service.d.ts.map +1 -1
  60. package/dist/course/lms-bulk-upload.service.js +48 -29
  61. package/dist/course/lms-bulk-upload.service.js.map +1 -1
  62. package/dist/course/subtitle.util.d.ts +46 -0
  63. package/dist/course/subtitle.util.d.ts.map +1 -0
  64. package/dist/course/subtitle.util.js +206 -0
  65. package/dist/course/subtitle.util.js.map +1 -0
  66. package/dist/enterprise/training/training-student.service.d.ts +27 -0
  67. package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
  68. package/dist/enterprise/training/training-student.service.js +197 -10
  69. package/dist/enterprise/training/training-student.service.js.map +1 -1
  70. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts +3 -1
  71. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.d.ts.map +1 -1
  72. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js +19 -5
  73. package/dist/lesson-xp-map/lesson-xp-ai-calculation.service.js.map +1 -1
  74. package/dist/lesson-xp-map/lesson-xp-map.module.d.ts.map +1 -1
  75. package/dist/lesson-xp-map/lesson-xp-map.module.js +2 -1
  76. package/dist/lesson-xp-map/lesson-xp-map.module.js.map +1 -1
  77. package/dist/libraries/lms/tsconfig.tsbuildinfo +1 -1
  78. package/dist/lms.module.d.ts.map +1 -1
  79. package/dist/lms.module.js +4 -0
  80. package/dist/lms.module.js.map +1 -1
  81. package/dist/platforma/platforma-performance.service.js +121 -121
  82. package/dist/platforma/platforma-video.service.d.ts +8 -0
  83. package/dist/platforma/platforma-video.service.d.ts.map +1 -1
  84. package/dist/platforma/platforma-video.service.js +45 -2
  85. package/dist/platforma/platforma-video.service.js.map +1 -1
  86. package/dist/platforma/platforma.controller.d.ts +99 -1
  87. package/dist/platforma/platforma.controller.d.ts.map +1 -1
  88. package/dist/platforma/platforma.controller.js +111 -2
  89. package/dist/platforma/platforma.controller.js.map +1 -1
  90. package/dist/training/dto/create-training.dto.d.ts +9 -0
  91. package/dist/training/dto/create-training.dto.d.ts.map +1 -1
  92. package/dist/training/dto/create-training.dto.js +45 -1
  93. package/dist/training/dto/create-training.dto.js.map +1 -1
  94. package/dist/training/training.controller.d.ts +144 -0
  95. package/dist/training/training.controller.d.ts.map +1 -1
  96. package/dist/training/training.service.d.ts +149 -0
  97. package/dist/training/training.service.d.ts.map +1 -1
  98. package/dist/training/training.service.js +332 -167
  99. package/dist/training/training.service.js.map +1 -1
  100. package/hedhog/data/image_type.yaml +10 -0
  101. package/hedhog/data/route.yaml +251 -0
  102. package/hedhog/data/setting_group.yaml +97 -0
  103. package/hedhog/frontend/app/bulk-upload-sessions/page.tsx.ejs +139 -27
  104. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +69 -57
  105. package/hedhog/frontend/app/courses/[id]/_components/CourseIssuedCertificatesCard.tsx.ejs +168 -0
  106. package/hedhog/frontend/app/courses/[id]/structure/_components/course-ai-costs-tab.tsx.ejs +191 -0
  107. package/hedhog/frontend/app/courses/[id]/structure/_components/course-export-sheet.tsx.ejs +81 -1
  108. package/hedhog/frontend/app/courses/[id]/structure/_components/course-exports-tab.tsx.ejs +12 -0
  109. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +69 -1
  110. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +267 -19
  111. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +114 -86
  112. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +239 -31
  113. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +344 -59
  114. package/hedhog/frontend/app/courses/[id]/structure/_components/lesson-video-preview.tsx.ejs +200 -0
  115. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +1 -0
  116. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +3 -0
  117. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -7
  118. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +4 -0
  119. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-ai-costs.ts.ejs +40 -0
  120. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-exports.ts.ejs +25 -0
  121. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +148 -0
  122. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +157 -8
  123. package/hedhog/frontend/app/courses/_components/CourseRowActions.tsx.ejs +1 -22
  124. package/hedhog/frontend/app/courses/page.tsx.ejs +26 -4
  125. package/hedhog/frontend/app/paths/page.tsx.ejs +612 -164
  126. package/hedhog/frontend/messages/en.json +23 -12
  127. package/hedhog/frontend/messages/pt.json +23 -12
  128. package/hedhog/query/triggers.sql +33 -0
  129. package/hedhog/table/course_ai_usage.yaml +46 -0
  130. package/hedhog/table/course_lesson.yaml +3 -0
  131. package/hedhog/table/course_lesson_answer.yaml +37 -0
  132. package/hedhog/table/course_lesson_transcription_segment.yaml +8 -0
  133. package/hedhog/table/learning_path.yaml +6 -0
  134. package/hedhog/table/learning_path_module.yaml +22 -0
  135. package/hedhog/table/learning_path_step.yaml +9 -6
  136. package/hedhog/table/lesson_view_event.yaml +66 -66
  137. package/package.json +9 -9
  138. package/src/certificate/certificate.controller.ts +2 -0
  139. package/src/certificate/certificate.service.ts +99 -0
  140. package/src/course/course-ai-usage.service.ts +221 -0
  141. package/src/course/course-audio-transcription.service.ts +471 -43
  142. package/src/course/course-export-scorm12.service.ts +149 -5
  143. package/src/course/course-export.service.ts +1 -0
  144. package/src/course/course-lesson.controller.ts +59 -6
  145. package/src/course/course-structure.controller.ts +16 -0
  146. package/src/course/course-structure.service.ts +184 -10
  147. package/src/course/course-transcription-translation.service.ts +293 -0
  148. package/src/course/course-video-agent-pipeline.service.ts +471 -471
  149. package/src/course/course.module.ts +4 -0
  150. package/src/course/dto/create-course-bulk-job.dto.ts +7 -3
  151. package/src/course/dto/create-course-export.dto.ts +6 -0
  152. package/src/course/ffmpeg.util.ts +65 -65
  153. package/src/course/lms-bulk-upload-automation.service.ts +29 -7
  154. package/src/course/lms-bulk-upload.service.ts +20 -1
  155. package/src/course/subtitle.util.ts +220 -0
  156. package/src/enterprise/training/training-student.service.ts +224 -4
  157. package/src/lesson-xp-map/lesson-xp-ai-calculation.service.ts +14 -0
  158. package/src/lesson-xp-map/lesson-xp-map.module.ts +2 -1
  159. package/src/lms.module.ts +4 -0
  160. package/src/platforma/dto/heartbeat.dto.ts +30 -30
  161. package/src/platforma/handlers/emit-certificate.handler.ts +117 -117
  162. package/src/platforma/handlers/lesson-heartbeat.handler.ts +343 -343
  163. package/src/platforma/platforma-heartbeat.service.ts +33 -33
  164. package/src/platforma/platforma-performance.service.ts +606 -606
  165. package/src/platforma/platforma-search.service.ts +48 -48
  166. package/src/platforma/platforma-video.service.ts +59 -3
  167. package/src/platforma/platforma.controller.ts +88 -0
  168. package/src/training/dto/create-training.dto.ts +36 -0
  169. package/src/training/training.service.ts +360 -163
@@ -1,48 +1,48 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { ClassGroupService } from '../class-group/class-group.service';
3
- import { TrainingStudentService } from '../enterprise/training/training-student.service';
4
- import { ExamService } from '../exam/exam.service';
5
- import { InstructorService } from '../instructor/instructor.service';
6
- import { TrainingService } from '../training/training.service';
7
-
8
- @Injectable()
9
- export class PlatformaSearchService {
10
- constructor(
11
- private readonly trainingStudentService: TrainingStudentService,
12
- private readonly trainingService: TrainingService,
13
- private readonly examService: ExamService,
14
- private readonly classGroupService: ClassGroupService,
15
- private readonly instructorService: InstructorService,
16
- ) {}
17
-
18
- async search(q: string, types: string[], pageSize = 5) {
19
- const all = types.length === 0;
20
-
21
- const [courses, tracks, exams, classrooms, instructors] =
22
- await Promise.allSettled([
23
- all || types.includes('course')
24
- ? this.trainingStudentService.getPublishedCourses({ search: q, pageSize })
25
- : Promise.resolve(null),
26
- all || types.includes('track')
27
- ? this.trainingService.list({ search: q, status: 'ativa', pageSize })
28
- : Promise.resolve(null),
29
- all || types.includes('exam')
30
- ? this.examService.list({ search: q, status: 'published', pageSize })
31
- : Promise.resolve(null),
32
- all || types.includes('classroom')
33
- ? this.classGroupService.list({ search: q, pageSize })
34
- : Promise.resolve(null),
35
- all || types.includes('instructor')
36
- ? this.instructorService.list({ search: q, status: 'active', pageSize })
37
- : Promise.resolve(null),
38
- ]);
39
-
40
- return {
41
- courses: courses.status === 'fulfilled' ? courses.value : null,
42
- tracks: tracks.status === 'fulfilled' ? tracks.value : null,
43
- exams: exams.status === 'fulfilled' ? exams.value : null,
44
- classrooms: classrooms.status === 'fulfilled' ? classrooms.value : null,
45
- instructors: instructors.status === 'fulfilled' ? instructors.value : null,
46
- };
47
- }
48
- }
1
+ import { Injectable } from '@nestjs/common';
2
+ import { ClassGroupService } from '../class-group/class-group.service';
3
+ import { TrainingStudentService } from '../enterprise/training/training-student.service';
4
+ import { ExamService } from '../exam/exam.service';
5
+ import { InstructorService } from '../instructor/instructor.service';
6
+ import { TrainingService } from '../training/training.service';
7
+
8
+ @Injectable()
9
+ export class PlatformaSearchService {
10
+ constructor(
11
+ private readonly trainingStudentService: TrainingStudentService,
12
+ private readonly trainingService: TrainingService,
13
+ private readonly examService: ExamService,
14
+ private readonly classGroupService: ClassGroupService,
15
+ private readonly instructorService: InstructorService,
16
+ ) {}
17
+
18
+ async search(q: string, types: string[], pageSize = 5) {
19
+ const all = types.length === 0;
20
+
21
+ const [courses, tracks, exams, classrooms, instructors] =
22
+ await Promise.allSettled([
23
+ all || types.includes('course')
24
+ ? this.trainingStudentService.getPublishedCourses({ search: q, pageSize })
25
+ : Promise.resolve(null),
26
+ all || types.includes('track')
27
+ ? this.trainingService.list({ search: q, status: 'ativa', pageSize })
28
+ : Promise.resolve(null),
29
+ all || types.includes('exam')
30
+ ? this.examService.list({ search: q, status: 'published', pageSize })
31
+ : Promise.resolve(null),
32
+ all || types.includes('classroom')
33
+ ? this.classGroupService.list({ search: q, pageSize })
34
+ : Promise.resolve(null),
35
+ all || types.includes('instructor')
36
+ ? this.instructorService.list({ search: q, status: 'active', pageSize })
37
+ : Promise.resolve(null),
38
+ ]);
39
+
40
+ return {
41
+ courses: courses.status === 'fulfilled' ? courses.value : null,
42
+ tracks: tracks.status === 'fulfilled' ? tracks.value : null,
43
+ exams: exams.status === 'fulfilled' ? exams.value : null,
44
+ classrooms: classrooms.status === 'fulfilled' ? classrooms.value : null,
45
+ instructors: instructors.status === 'fulfilled' ? instructors.value : null,
46
+ };
47
+ }
48
+ }
@@ -1,5 +1,6 @@
1
1
  import { FileService } from '@hed-hog/core';
2
2
  import { PrismaService } from '@hed-hog/api-prisma';
3
+ import { balanceSubtitleLines } from '../course/subtitle.util';
3
4
  import {
4
5
  forwardRef,
5
6
  Inject,
@@ -128,6 +129,56 @@ export class PlatformaVideoService {
128
129
  stream.pipe(res);
129
130
  }
130
131
 
132
+ // ---------------------------------------------------------------------------
133
+ // Admin preview endpoints (no published check)
134
+ // ---------------------------------------------------------------------------
135
+
136
+ async generateAdminHlsToken(lessonId: number) {
137
+ const lesson = await this.prisma.course_lesson.findFirst({
138
+ where: { id: lessonId },
139
+ select: {
140
+ id: true,
141
+ course_lesson_file: {
142
+ where: { type: 'video_hls', file_id: { not: null } },
143
+ select: { id: true },
144
+ },
145
+ },
146
+ });
147
+
148
+ if (!lesson) throw new NotFoundException('Lesson not found');
149
+ if (lesson.course_lesson_file.length === 0) throw new NotFoundException('No HLS stream available for this lesson');
150
+
151
+ const token = this.jwtService.sign(
152
+ { lessonId },
153
+ { secret: process.env.JWT_SECRET, expiresIn: '4h' },
154
+ );
155
+
156
+ return { token, expiresIn: 14400 };
157
+ }
158
+
159
+ async generateAdminSubtitlesToken(lessonId: number, localeId?: number) {
160
+ const lesson = await this.prisma.course_lesson.findFirst({
161
+ where: { id: lessonId },
162
+ select: { id: true },
163
+ });
164
+
165
+ if (!lesson) throw new NotFoundException('Lesson not found');
166
+
167
+ const where: Record<string, unknown> = { course_lesson_id: lessonId };
168
+ if (localeId !== undefined) where['locale_id'] = localeId;
169
+
170
+ const count = await (this.prisma as any).course_lesson_transcription_segment.count({ where });
171
+
172
+ if (count === 0) throw new NotFoundException('No subtitles available for this lesson');
173
+
174
+ const token = this.jwtService.sign(
175
+ { lessonId, localeId: localeId ?? null, type: 'subtitles' },
176
+ { secret: process.env.JWT_SECRET, expiresIn: '4h' },
177
+ );
178
+
179
+ return { token, expiresIn: 14400 };
180
+ }
181
+
131
182
  // ---------------------------------------------------------------------------
132
183
  // HLS endpoints
133
184
  // ---------------------------------------------------------------------------
@@ -299,18 +350,23 @@ export class PlatformaVideoService {
299
350
 
300
351
  async serveSubtitles(token: string, res: Response) {
301
352
  let lessonId: number;
353
+ let localeId: number | null | undefined;
302
354
  try {
303
- const payload = this.jwtService.verify<{ lessonId: number; type: string }>(token, {
355
+ const payload = this.jwtService.verify<{ lessonId: number; localeId?: number | null; type: string }>(token, {
304
356
  secret: process.env.JWT_SECRET,
305
357
  });
306
358
  if (payload.type !== 'subtitles') throw new Error('Wrong token type');
307
359
  lessonId = payload.lessonId;
360
+ localeId = payload.localeId;
308
361
  } catch {
309
362
  throw new UnauthorizedException('Invalid or expired subtitles token');
310
363
  }
311
364
 
365
+ const where: Record<string, unknown> = { course_lesson_id: lessonId };
366
+ if (localeId != null) where['locale_id'] = localeId;
367
+
312
368
  const segments = await (this.prisma as any).course_lesson_transcription_segment.findMany({
313
- where: { course_lesson_id: lessonId },
369
+ where,
314
370
  orderBy: { start_seconds: 'asc' },
315
371
  select: { start_seconds: true, end_seconds: true, text: true },
316
372
  });
@@ -339,7 +395,7 @@ export class PlatformaVideoService {
339
395
  const cues = segments.map((seg, i) => {
340
396
  const start = this.secondsToVttTime(seg.start_seconds);
341
397
  const end = this.secondsToVttTime(seg.end_seconds);
342
- return `${i + 1}\n${start} --> ${end}\n${seg.text}`;
398
+ return `${i + 1}\n${start} --> ${end}\n${balanceSubtitleLines(seg.text)}`;
343
399
  });
344
400
  return `WEBVTT\n\n${cues.join('\n\n')}\n`;
345
401
  }
@@ -4,6 +4,10 @@ import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Put, Q
4
4
  import type { Request, Response } from 'express';
5
5
  import { BitcodeWalletService } from '../bitcode-wallet/bitcode-wallet.service';
6
6
  import { CreateCurrentBitcodeWalletTransactionDto } from '../bitcode-wallet/dto/create-current-bitcode-wallet-transaction.dto';
7
+ import { LmsCertificateService } from '../certificate/certificate.service';
8
+ import { UpdateCertificatePublicAccessDto } from '../certificate/dto/update-certificate-public-access.dto';
9
+ import { CourseLessonDiscussionService } from '../course-lesson-discussion/course-lesson-discussion.service';
10
+ import { CreateCourseLessonDiscussionTopicDto } from '../course-lesson-discussion/dto/create-course-lesson-discussion-topic.dto';
7
11
  import { EvaluationService } from '../evaluation/evaluation.service';
8
12
  import { TrainingStudentService } from '../enterprise/training/training-student.service';
9
13
  import { HeartbeatDto } from './dto/heartbeat.dto';
@@ -27,6 +31,8 @@ export class PlataformaController {
27
31
  private readonly heartbeatService: PlatformaHeartbeatService,
28
32
  private readonly performanceService: PlatformaPerformanceService,
29
33
  private readonly searchService: PlatformaSearchService,
34
+ private readonly certificateService: LmsCertificateService,
35
+ private readonly discussionService: CourseLessonDiscussionService,
30
36
  ) {}
31
37
 
32
38
  @Get()
@@ -79,6 +85,30 @@ export class PlataformaController {
79
85
  return this.trainingStudentService.getLessonDetail(lessonId);
80
86
  }
81
87
 
88
+ @Get('lessons/:lessonId/my-answers')
89
+ getLessonStudentAnswers(
90
+ @Param('lessonId', ParseIntPipe) lessonId: number,
91
+ @User('id') userId: number,
92
+ ) {
93
+ return this.trainingStudentService.getLessonStudentAnswers(userId, lessonId);
94
+ }
95
+
96
+ @Post('lessons/:lessonId/questions/:questionId/answer')
97
+ submitLessonAnswer(
98
+ @Param('lessonId', ParseIntPipe) lessonId: number,
99
+ @Param('questionId', ParseIntPipe) questionId: number,
100
+ @User('id') userId: number,
101
+ @Body() body: { optionId?: number; answerText?: string },
102
+ ) {
103
+ return this.trainingStudentService.submitLessonAnswer(
104
+ userId,
105
+ lessonId,
106
+ questionId,
107
+ body.optionId ?? null,
108
+ body.answerText ?? null,
109
+ );
110
+ }
111
+
82
112
  @Get('lessons/:lessonId/video-token')
83
113
  getVideoToken(
84
114
  @Param('lessonId', ParseIntPipe) lessonId: number,
@@ -280,4 +310,62 @@ export class PlataformaController {
280
310
  ) {
281
311
  return this.platforma.updateProfile(userId, dto);
282
312
  }
313
+
314
+ @Get('certificates')
315
+ getStudentCertificates(
316
+ @User('id') userId: number,
317
+ @Query('page') page?: string,
318
+ @Query('pageSize') pageSize?: string,
319
+ @Query('type') type?: string,
320
+ ) {
321
+ return this.certificateService.listIssuedForStudent(userId, {
322
+ page: page ? Number(page) : 1,
323
+ pageSize: pageSize ? Number(pageSize) : 12,
324
+ type,
325
+ });
326
+ }
327
+
328
+ @Patch('certificates/:id/public-access')
329
+ updateStudentCertificateAccess(
330
+ @User('id') userId: number,
331
+ @Param('id', ParseIntPipe) id: number,
332
+ @Body() dto: UpdateCertificatePublicAccessDto,
333
+ ) {
334
+ return this.certificateService.updatePublicAccessForStudent(userId, id, dto.publicAccess);
335
+ }
336
+
337
+ @Get('lessons/:lessonId/discussion/topics')
338
+ listDiscussionTopics(
339
+ @Param('lessonId', ParseIntPipe) lessonId: number,
340
+ @User('id') userId: number,
341
+ ) {
342
+ return this.discussionService.listTopics(lessonId, userId);
343
+ }
344
+
345
+ @Post('lessons/:lessonId/discussion/topics')
346
+ createDiscussionTopic(
347
+ @Param('lessonId', ParseIntPipe) lessonId: number,
348
+ @User('id') userId: number,
349
+ @Body() dto: CreateCourseLessonDiscussionTopicDto,
350
+ ) {
351
+ return this.discussionService.createTopic(lessonId, userId, dto);
352
+ }
353
+
354
+ @Post('lessons/:lessonId/discussion/topics/:topicId/likes')
355
+ likeDiscussionTopic(
356
+ @Param('lessonId', ParseIntPipe) lessonId: number,
357
+ @Param('topicId', ParseIntPipe) topicId: number,
358
+ @User('id') userId: number,
359
+ ) {
360
+ return this.discussionService.likeTopic(lessonId, topicId, userId);
361
+ }
362
+
363
+ @Delete('lessons/:lessonId/discussion/topics/:topicId/likes')
364
+ unlikeDiscussionTopic(
365
+ @Param('lessonId', ParseIntPipe) lessonId: number,
366
+ @Param('topicId', ParseIntPipe) topicId: number,
367
+ @User('id') userId: number,
368
+ ) {
369
+ return this.discussionService.unlikeTopic(lessonId, topicId, userId);
370
+ }
283
371
  }
@@ -31,6 +31,28 @@ export class LearningPathItemDto {
31
31
  isRequired?: boolean;
32
32
  }
33
33
 
34
+ export class LearningPathModuleDto {
35
+ @IsString()
36
+ @IsNotEmpty()
37
+ @MaxLength(255)
38
+ title: string;
39
+
40
+ @IsString()
41
+ @IsOptional()
42
+ description?: string;
43
+
44
+ @IsInt()
45
+ @Min(0)
46
+ @IsOptional()
47
+ order?: number;
48
+
49
+ @IsArray()
50
+ @ValidateNested({ each: true })
51
+ @Type(() => LearningPathItemDto)
52
+ @IsOptional()
53
+ items?: LearningPathItemDto[];
54
+ }
55
+
34
56
  export class CreateTrainingDto {
35
57
  @IsString()
36
58
  @IsNotEmpty()
@@ -82,4 +104,18 @@ export class CreateTrainingDto {
82
104
  @IsOptional()
83
105
  @MaxLength(32)
84
106
  secondaryColor?: string;
107
+
108
+ @IsInt()
109
+ @IsOptional()
110
+ bannerFileId?: number | null;
111
+
112
+ @IsInt()
113
+ @IsOptional()
114
+ certificateTemplateId?: number | null;
115
+
116
+ @IsArray()
117
+ @ValidateNested({ each: true })
118
+ @Type(() => LearningPathModuleDto)
119
+ @IsOptional()
120
+ modules?: LearningPathModuleDto[];
85
121
  }