@hed-hog/lms 0.0.353 → 0.0.355

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 (135) hide show
  1. package/dist/course/course-audio-transcription.service.d.ts +29 -0
  2. package/dist/course/course-audio-transcription.service.d.ts.map +1 -0
  3. package/dist/course/course-audio-transcription.service.js +291 -0
  4. package/dist/course/course-audio-transcription.service.js.map +1 -0
  5. package/dist/course/course-lesson.controller.d.ts +10 -0
  6. package/dist/course/course-lesson.controller.d.ts.map +1 -0
  7. package/dist/course/course-lesson.controller.js +62 -0
  8. package/dist/course/course-lesson.controller.js.map +1 -0
  9. package/dist/course/course-structure.controller.d.ts +41 -15
  10. package/dist/course/course-structure.controller.d.ts.map +1 -1
  11. package/dist/course/course-structure.controller.js +50 -6
  12. package/dist/course/course-structure.controller.js.map +1 -1
  13. package/dist/course/course-structure.service.d.ts +50 -15
  14. package/dist/course/course-structure.service.d.ts.map +1 -1
  15. package/dist/course/course-structure.service.js +238 -73
  16. package/dist/course/course-structure.service.js.map +1 -1
  17. package/dist/course/course-video-conversion.service.d.ts +20 -2
  18. package/dist/course/course-video-conversion.service.d.ts.map +1 -1
  19. package/dist/course/course-video-conversion.service.js +730 -10
  20. package/dist/course/course-video-conversion.service.js.map +1 -1
  21. package/dist/course/course.controller.d.ts +24 -8
  22. package/dist/course/course.controller.d.ts.map +1 -1
  23. package/dist/course/course.module.d.ts.map +1 -1
  24. package/dist/course/course.module.js +5 -3
  25. package/dist/course/course.module.js.map +1 -1
  26. package/dist/course/course.service.d.ts +24 -8
  27. package/dist/course/course.service.d.ts.map +1 -1
  28. package/dist/course/course.service.js +112 -176
  29. package/dist/course/course.service.js.map +1 -1
  30. package/dist/course/dto/create-course-lesson-frame.dto.d.ts +5 -0
  31. package/dist/course/dto/create-course-lesson-frame.dto.d.ts.map +1 -0
  32. package/dist/course/dto/create-course-lesson-frame.dto.js +30 -0
  33. package/dist/course/dto/create-course-lesson-frame.dto.js.map +1 -0
  34. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +4 -2
  35. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  36. package/dist/course/dto/create-course-structure-lesson.dto.js +10 -3
  37. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  38. package/dist/course/dto/create-course.dto.d.ts +1 -1
  39. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  40. package/dist/course/dto/create-course.dto.js +6 -6
  41. package/dist/course/dto/create-course.dto.js.map +1 -1
  42. package/dist/course/dto/update-course-lesson-frame.dto.d.ts +5 -0
  43. package/dist/course/dto/update-course-lesson-frame.dto.d.ts.map +1 -0
  44. package/dist/course/dto/update-course-lesson-frame.dto.js +32 -0
  45. package/dist/course/dto/update-course-lesson-frame.dto.js.map +1 -0
  46. package/dist/course/dto/update-course-resources.dto.d.ts +4 -2
  47. package/dist/course/dto/update-course-resources.dto.d.ts.map +1 -1
  48. package/dist/course/dto/update-course-resources.dto.js +10 -3
  49. package/dist/course/dto/update-course-resources.dto.js.map +1 -1
  50. package/dist/course/dto/update-transcription-segments.dto.d.ts +10 -0
  51. package/dist/course/dto/update-transcription-segments.dto.d.ts.map +1 -0
  52. package/dist/course/dto/update-transcription-segments.dto.js +38 -0
  53. package/dist/course/dto/update-transcription-segments.dto.js.map +1 -0
  54. package/dist/course/lms-setting.controller.d.ts +13 -0
  55. package/dist/course/lms-setting.controller.d.ts.map +1 -0
  56. package/dist/course/lms-setting.controller.js +53 -0
  57. package/dist/course/lms-setting.controller.js.map +1 -0
  58. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  59. package/dist/enterprise/training/training-admin.service.js +74 -33
  60. package/dist/enterprise/training/training-admin.service.js.map +1 -1
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +2 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/lms.module.d.ts.map +1 -1
  66. package/dist/lms.module.js +6 -0
  67. package/dist/lms.module.js.map +1 -1
  68. package/hedhog/data/route.yaml +63 -0
  69. package/hedhog/data/setting_group.yaml +76 -0
  70. package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +3 -0
  71. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +10 -1
  72. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +7 -2
  73. package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +5 -2
  74. package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +7 -1
  75. package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +3 -0
  76. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +95 -50
  77. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +11 -36
  78. package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +19 -5
  79. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
  80. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +30 -10
  81. package/hedhog/frontend/app/courses/[id]/structure/_components/CourseInstructorsSummaryCard.tsx.ejs +95 -0
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +29 -11
  83. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +42 -31
  84. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +10 -1
  85. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -9
  86. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +25 -22
  87. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +12 -9
  88. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +315 -229
  89. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +3220 -534
  90. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +20 -15
  91. package/hedhog/frontend/app/courses/[id]/structure/_components/icon-action-tooltip.tsx.ejs +35 -0
  92. package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +76 -67
  93. package/hedhog/frontend/app/courses/[id]/structure/_components/search-filter.tsx.ejs +13 -10
  94. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +18 -16
  95. package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +6 -0
  96. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +1 -1
  97. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +23 -11
  98. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-helpers.ts.ejs +48 -9
  99. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-course.tsx.ejs +11 -2
  100. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +121 -15
  101. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +69 -14
  102. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +11 -0
  103. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +31 -0
  104. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +32 -6
  105. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +57 -3
  106. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +46 -6
  107. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +14 -0
  108. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-audio-files.ts.ejs +23 -0
  109. package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +34 -0
  110. package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +76 -0
  111. package/hedhog/frontend/messages/en.json +39 -3
  112. package/hedhog/frontend/messages/pt.json +39 -3
  113. package/hedhog/table/course.yaml +8 -0
  114. package/hedhog/table/course_lesson_file.yaml +12 -4
  115. package/hedhog/table/course_lesson_transcription_segment.yaml +22 -0
  116. package/hedhog/table/course_lesson_video_frame.yaml +25 -0
  117. package/package.json +8 -8
  118. package/src/course/course-audio-transcription.service.ts +393 -0
  119. package/src/course/course-lesson.controller.ts +28 -0
  120. package/src/course/course-structure.controller.ts +49 -3
  121. package/src/course/course-structure.service.ts +294 -32
  122. package/src/course/course-video-conversion.service.ts +972 -6
  123. package/src/course/course.module.ts +5 -3
  124. package/src/course/course.service.ts +87 -139
  125. package/src/course/dto/create-course-lesson-frame.dto.ts +14 -0
  126. package/src/course/dto/create-course-structure-lesson.dto.ts +19 -3
  127. package/src/course/dto/create-course.dto.ts +5 -5
  128. package/src/course/dto/update-course-lesson-frame.dto.ts +16 -0
  129. package/src/course/dto/update-course-resources.dto.ts +18 -3
  130. package/src/course/dto/update-transcription-segments.dto.ts +20 -0
  131. package/src/course/lms-setting.controller.ts +30 -0
  132. package/src/enterprise/training/training-admin.service.ts +77 -24
  133. package/src/index.ts +2 -0
  134. package/src/lms.module.ts +6 -0
  135. package/hedhog/table/course_instructor.yaml +0 -27
@@ -1,24 +1,35 @@
1
1
  import { PrismaService } from '@hed-hog/api-prisma';
2
+ import { FileService } from '@hed-hog/core';
2
3
  import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
3
4
  import { InstructorService } from '../instructor/instructor.service';
4
5
  import { CourseOperationsIntegrationService } from './course-operations-integration.service';
6
+ import { CreateCourseLessonFrameDto } from './dto/create-course-lesson-frame.dto';
5
7
  import { CreateCourseStructureLessonDto } from './dto/create-course-structure-lesson.dto';
6
8
  import { CreateCourseStructureSessionDto } from './dto/create-course-structure-session.dto';
7
9
  import { MoveLessonDto } from './dto/move-lesson.dto';
8
10
  import { PasteLessonsDto } from './dto/paste-lessons.dto';
9
11
  import { ReorderLessonsDto } from './dto/reorder-lessons.dto';
10
12
  import { ReorderSessionsDto } from './dto/reorder-sessions.dto';
13
+ import { UpdateCourseLessonFrameDto } from './dto/update-course-lesson-frame.dto';
11
14
  import { UpdateCourseResourcesDto } from './dto/update-course-resources.dto';
12
15
  import { UpdateCourseStructureLessonDto } from './dto/update-course-structure-lesson.dto';
13
16
  import { UpdateCourseStructureSessionDto } from './dto/update-course-structure-session.dto';
17
+ import { UpdateTranscriptionSegmentsDTO } from './dto/update-transcription-segments.dto';
14
18
 
15
19
  type LessonUiType = 'video' | 'questao' | 'post' | 'exercicio';
16
20
  type LessonDbType = 'video' | 'text' | 'quiz';
21
+ type LessonFileType =
22
+ | 'video_original'
23
+ | `video_profile:${number}`
24
+ | 'lesson_audio'
25
+ | 'student_download'
26
+ | 'supplementary_material';
17
27
 
18
28
  @Injectable()
19
29
  export class CourseStructureService {
20
30
  constructor(
21
31
  private readonly prisma: PrismaService,
32
+ private readonly fileService: FileService,
22
33
  private readonly instructorService: InstructorService,
23
34
  private readonly operationsIntegration: CourseOperationsIntegrationService,
24
35
  ) {}
@@ -38,6 +49,17 @@ export class CourseStructureService {
38
49
  orderBy: { order: 'asc' },
39
50
  include: {
40
51
  course_lesson_file: true,
52
+ course_lesson_video_frame: {
53
+ orderBy: { id: 'asc' },
54
+ include: {
55
+ file: {
56
+ select: {
57
+ id: true,
58
+ filename: true,
59
+ },
60
+ },
61
+ },
62
+ },
41
63
  course_lesson_question: {
42
64
  orderBy: { order: 'asc' },
43
65
  },
@@ -118,8 +140,15 @@ export class CourseStructureService {
118
140
  nome: fileLink.title,
119
141
  fileId: fileLink.file_id,
120
142
  tamanho: '',
121
- tipo: (fileLink as any).tipo ?? '',
122
- publico: (fileLink as any).publico ?? true,
143
+ type: this.readLessonFileType(fileLink),
144
+ is_public: this.readLessonFileVisibility(fileLink),
145
+ })),
146
+ frames: lesson.course_lesson_video_frame.map((frame) => ({
147
+ id: String(frame.id),
148
+ nome: frame.file?.filename ?? `frame-${frame.id}`,
149
+ fileId: frame.file_id ?? 0,
150
+ timeSeconds: frame.time_seconds ?? 0,
151
+ url: frame.file_id ? `/file/open/${frame.file_id}` : undefined,
123
152
  })),
124
153
  instrutores: lesson.course_lesson_instructor.map((item) => ({
125
154
  id: String(item.instructor_id),
@@ -174,8 +203,8 @@ export class CourseStructureService {
174
203
  id: row.id,
175
204
  nome: row.title,
176
205
  fileId: row.file_id,
177
- tipo: 'file',
178
- publico: true,
206
+ type: 'student_download',
207
+ is_public: true,
179
208
  }));
180
209
  }
181
210
 
@@ -413,6 +442,145 @@ export class CourseStructureService {
413
442
  return { success: true };
414
443
  }
415
444
 
445
+ async deleteLessonFrame(
446
+ courseId: number,
447
+ sessionId: number,
448
+ lessonId: number,
449
+ frameId: number,
450
+ ) {
451
+ const frame = await this.prisma.course_lesson_video_frame.findFirst({
452
+ where: {
453
+ id: frameId,
454
+ course_lesson_id: lessonId,
455
+ course_lesson: {
456
+ course_module_id: sessionId,
457
+ course_module: { course_id: courseId },
458
+ },
459
+ },
460
+ select: {
461
+ id: true,
462
+ file_id: true,
463
+ },
464
+ });
465
+
466
+ if (!frame) {
467
+ throw new NotFoundException('Lesson frame not found for this lesson');
468
+ }
469
+
470
+ await this.prisma.course_lesson_video_frame.delete({ where: { id: frameId } });
471
+
472
+ if (frame.file_id) {
473
+ await this.fileService.delete('pt', { ids: [frame.file_id] }).catch(() => undefined);
474
+ }
475
+
476
+ return { success: true };
477
+ }
478
+
479
+ async createLessonFrame(
480
+ courseId: number,
481
+ sessionId: number,
482
+ lessonId: number,
483
+ dto: CreateCourseLessonFrameDto,
484
+ ) {
485
+ const lesson = await this.prisma.course_lesson.findFirst({
486
+ where: {
487
+ id: lessonId,
488
+ course_module_id: sessionId,
489
+ course_module: { course_id: courseId },
490
+ },
491
+ select: { id: true },
492
+ });
493
+
494
+ if (!lesson) {
495
+ throw new NotFoundException('Lesson not found for this session');
496
+ }
497
+
498
+ await this.fileService.get(dto.fileId);
499
+
500
+ const created = await this.prisma.course_lesson_video_frame.create({
501
+ data: {
502
+ course_lesson_id: lessonId,
503
+ file_id: dto.fileId,
504
+ time_seconds: dto.timeSeconds,
505
+ },
506
+ select: {
507
+ id: true,
508
+ file_id: true,
509
+ time_seconds: true,
510
+ },
511
+ });
512
+
513
+ return {
514
+ success: true,
515
+ id: created.id,
516
+ fileId: created.file_id,
517
+ timeSeconds: created.time_seconds,
518
+ };
519
+ }
520
+
521
+ async updateLessonFrame(
522
+ courseId: number,
523
+ sessionId: number,
524
+ lessonId: number,
525
+ frameId: number,
526
+ dto: UpdateCourseLessonFrameDto,
527
+ ) {
528
+ const frame = await this.prisma.course_lesson_video_frame.findFirst({
529
+ where: {
530
+ id: frameId,
531
+ course_lesson_id: lessonId,
532
+ course_lesson: {
533
+ course_module_id: sessionId,
534
+ course_module: { course_id: courseId },
535
+ },
536
+ },
537
+ select: {
538
+ id: true,
539
+ file_id: true,
540
+ },
541
+ });
542
+
543
+ if (!frame) {
544
+ throw new NotFoundException('Lesson frame not found for this lesson');
545
+ }
546
+
547
+ if (dto.timeSeconds === undefined && dto.fileId === undefined) {
548
+ throw new BadRequestException('No frame update fields provided');
549
+ }
550
+
551
+ if (dto.fileId !== undefined) {
552
+ await this.fileService.get(dto.fileId);
553
+ }
554
+
555
+ const updated = await this.prisma.course_lesson_video_frame.update({
556
+ where: { id: frameId },
557
+ data: {
558
+ ...(dto.timeSeconds !== undefined && { time_seconds: dto.timeSeconds }),
559
+ ...(dto.fileId !== undefined && { file_id: dto.fileId }),
560
+ },
561
+ select: {
562
+ id: true,
563
+ file_id: true,
564
+ time_seconds: true,
565
+ },
566
+ });
567
+
568
+ if (
569
+ dto.fileId !== undefined &&
570
+ frame.file_id &&
571
+ frame.file_id !== dto.fileId
572
+ ) {
573
+ await this.fileService.delete('pt', { ids: [frame.file_id] }).catch(() => undefined);
574
+ }
575
+
576
+ return {
577
+ success: true,
578
+ id: updated.id,
579
+ fileId: updated.file_id,
580
+ timeSeconds: updated.time_seconds,
581
+ };
582
+ }
583
+
416
584
  async reorderSessions(courseId: number, dto: ReorderSessionsDto) {
417
585
  await this.ensureCourseExists(courseId);
418
586
 
@@ -608,13 +776,12 @@ export class CourseStructureService {
608
776
  });
609
777
 
610
778
  if (lesson.course_lesson_file.length > 0) {
611
- await this.prisma.course_lesson_file.createMany({
779
+ await (this.prisma as any).course_lesson_file.createMany({
612
780
  data: lesson.course_lesson_file.map((f) => ({
613
781
  course_lesson_id: newLesson.id,
614
782
  title: f.title,
615
783
  file_id: f.file_id,
616
- tipo: (f as any).tipo ?? null,
617
- publico: (f as any).publico ?? true,
784
+ ...this.buildLessonFilePersistence((f as any).type, (f as any).is_public),
618
785
  })),
619
786
  });
620
787
  }
@@ -711,13 +878,12 @@ export class CourseStructureService {
711
878
  });
712
879
 
713
880
  if (sourceLesson.course_lesson_file.length > 0) {
714
- await this.prisma.course_lesson_file.createMany({
881
+ await (this.prisma as any).course_lesson_file.createMany({
715
882
  data: sourceLesson.course_lesson_file.map((f) => ({
716
883
  course_lesson_id: newLesson.id,
717
884
  title: f.title,
718
885
  file_id: f.file_id,
719
- tipo: (f as any).tipo ?? null,
720
- publico: (f as any).publico ?? true,
886
+ ...this.buildLessonFilePersistence((f as any).type, (f as any).is_public),
721
887
  })),
722
888
  });
723
889
  }
@@ -801,13 +967,12 @@ export class CourseStructureService {
801
967
  });
802
968
 
803
969
  if (lesson.course_lesson_file.length > 0) {
804
- await this.prisma.course_lesson_file.createMany({
970
+ await (this.prisma as any).course_lesson_file.createMany({
805
971
  data: lesson.course_lesson_file.map((f) => ({
806
972
  course_lesson_id: newLesson.id,
807
973
  title: f.title,
808
974
  file_id: f.file_id,
809
- tipo: (f as any).tipo ?? null,
810
- publico: (f as any).publico ?? true,
975
+ ...this.buildLessonFilePersistence((f as any).type, (f as any).is_public),
811
976
  })),
812
977
  });
813
978
  }
@@ -847,6 +1012,52 @@ export class CourseStructureService {
847
1012
  return { lessons: result };
848
1013
  }
849
1014
 
1015
+ async getTranscriptionSegments(lessonId: number) {
1016
+ return (this.prisma as any).course_lesson_transcription_segment.findMany({
1017
+ where: { course_lesson_id: lessonId },
1018
+ orderBy: { start_seconds: 'asc' },
1019
+ select: {
1020
+ id: true,
1021
+ start_seconds: true,
1022
+ end_seconds: true,
1023
+ text: true,
1024
+ },
1025
+ });
1026
+ }
1027
+
1028
+ async updateTranscriptionSegments(
1029
+ lessonId: number,
1030
+ dto: UpdateTranscriptionSegmentsDTO,
1031
+ ) {
1032
+ return this.prisma.$transaction([
1033
+ (this.prisma as any).course_lesson_transcription_segment.deleteMany({
1034
+ where: { course_lesson_id: lessonId },
1035
+ }),
1036
+ (this.prisma as any).course_lesson_transcription_segment.createMany({
1037
+ data: (dto.segments ?? []).map((segment) => ({
1038
+ course_lesson_id: lessonId,
1039
+ start_seconds: segment.startSeconds,
1040
+ end_seconds: segment.endSeconds,
1041
+ text: segment.text,
1042
+ })),
1043
+ }),
1044
+ ]);
1045
+ }
1046
+
1047
+ async getAudioFiles(lessonId: number) {
1048
+ return (this.prisma as any).course_lesson_file.findMany({
1049
+ where: {
1050
+ course_lesson_id: lessonId,
1051
+ type: 'lesson_audio',
1052
+ },
1053
+ include: {
1054
+ locale: true,
1055
+ file: true,
1056
+ },
1057
+ orderBy: { created_at: 'asc' },
1058
+ });
1059
+ }
1060
+
850
1061
  private async syncLessonRelations(
851
1062
  lessonId: number,
852
1063
  dto: Partial<
@@ -857,9 +1068,15 @@ export class CourseStructureService {
857
1068
  >,
858
1069
  client: any = this.prisma,
859
1070
  ) {
860
- const instructorIds = [...new Set(dto.instructorIds ?? [])];
1071
+ const shouldSyncInstructors = Object.prototype.hasOwnProperty.call(
1072
+ dto,
1073
+ 'instructorIds',
1074
+ );
1075
+ const instructorIds = shouldSyncInstructors
1076
+ ? [...new Set(dto.instructorIds ?? [])]
1077
+ : [];
861
1078
 
862
- if (instructorIds.length > 0) {
1079
+ if (shouldSyncInstructors && instructorIds.length > 0) {
863
1080
  await this.instructorService.assertQualifiedInstructorIds(
864
1081
  instructorIds,
865
1082
  ['course-lessons'],
@@ -867,18 +1084,20 @@ export class CourseStructureService {
867
1084
  }
868
1085
 
869
1086
  const syncWithClient = async (tx: any) => {
870
- await tx.course_lesson_instructor.deleteMany({
871
- where: { course_lesson_id: lessonId },
872
- });
873
-
874
- if (instructorIds.length > 0) {
875
- await tx.course_lesson_instructor.createMany({
876
- data: instructorIds.map((instructorId, index) => ({
877
- course_lesson_id: lessonId,
878
- instructor_id: instructorId,
879
- role: index === 0 ? 'lead' : 'assistant',
880
- })),
1087
+ if (shouldSyncInstructors) {
1088
+ await tx.course_lesson_instructor.deleteMany({
1089
+ where: { course_lesson_id: lessonId },
881
1090
  });
1091
+
1092
+ if (instructorIds.length > 0) {
1093
+ await tx.course_lesson_instructor.createMany({
1094
+ data: instructorIds.map((instructorId, index) => ({
1095
+ course_lesson_id: lessonId,
1096
+ instructor_id: instructorId,
1097
+ role: index === 0 ? 'lead' : 'assistant',
1098
+ })),
1099
+ });
1100
+ }
882
1101
  }
883
1102
 
884
1103
  await tx.course_lesson_file.deleteMany({
@@ -887,13 +1106,12 @@ export class CourseStructureService {
887
1106
 
888
1107
  const recursos = dto.recursos ?? [];
889
1108
  if (recursos.length > 0) {
890
- await tx.course_lesson_file.createMany({
1109
+ await (tx as any).course_lesson_file.createMany({
891
1110
  data: recursos.map((recurso) => ({
892
1111
  course_lesson_id: lessonId,
893
1112
  title: recurso.nome,
894
1113
  file_id: recurso.fileId ?? null,
895
- tipo: recurso.tipo ?? null,
896
- publico: recurso.publico ?? true,
1114
+ ...this.buildLessonFilePersistence((recurso as any).type, (recurso as any).is_public),
897
1115
  })),
898
1116
  });
899
1117
  }
@@ -993,8 +1211,8 @@ export class CourseStructureService {
993
1211
  nome: fileLink.title,
994
1212
  fileId: fileLink.file_id,
995
1213
  tamanho: '',
996
- tipo: (fileLink as any).tipo ?? '',
997
- publico: (fileLink as any).publico ?? true,
1214
+ type: this.readLessonFileType(fileLink),
1215
+ is_public: this.readLessonFileVisibility(fileLink),
998
1216
  })),
999
1217
  instrutores: lesson.course_lesson_instructor.map((item) => ({
1000
1218
  id: String(item.instructor_id),
@@ -1079,7 +1297,7 @@ export class CourseStructureService {
1079
1297
  };
1080
1298
 
1081
1299
  if (data.tipo === 'video') {
1082
- payload.videoProvedor = data.videoProvedor ?? 'youtube';
1300
+ payload.videoProvedor = data.videoProvedor ?? 'file_storage';
1083
1301
  payload.videoUrl = data.videoUrl ?? '';
1084
1302
  payload.duracaoAutomatica = data.duracaoAutomatica ?? false;
1085
1303
  payload.transcricao = data.transcricao ?? '';
@@ -1109,6 +1327,50 @@ export class CourseStructureService {
1109
1327
  }
1110
1328
  }
1111
1329
 
1330
+ private normalizeLessonFileType(value?: string | null): LessonFileType {
1331
+ if (value === 'video_original') {
1332
+ return 'video_original';
1333
+ }
1334
+
1335
+ if (value?.startsWith('video_profile:')) {
1336
+ const profileId = Number(value.replace('video_profile:', ''));
1337
+ if (Number.isInteger(profileId) && profileId > 0) {
1338
+ return `video_profile:${profileId}`;
1339
+ }
1340
+ }
1341
+
1342
+ if (value === 'lesson_audio') {
1343
+ return 'lesson_audio';
1344
+ }
1345
+
1346
+ if (value === 'student_download' || value === 'file' || value === 'download') {
1347
+ return 'student_download';
1348
+ }
1349
+
1350
+ return 'supplementary_material';
1351
+ }
1352
+
1353
+ private readLessonFileType(fileLink: any): LessonFileType {
1354
+ return this.normalizeLessonFileType(fileLink?.type);
1355
+ }
1356
+
1357
+ private readLessonFileVisibility(fileLink: any): boolean {
1358
+ return Boolean(fileLink?.is_public ?? true);
1359
+ }
1360
+
1361
+ private buildLessonFilePersistence(
1362
+ typeValue?: string | null,
1363
+ isPublicValue?: boolean,
1364
+ ) {
1365
+ const normalizedType = this.normalizeLessonFileType(typeValue);
1366
+ const normalizedIsPublic = Boolean(isPublicValue ?? true);
1367
+
1368
+ return {
1369
+ type: normalizedType,
1370
+ is_public: normalizedIsPublic,
1371
+ };
1372
+ }
1373
+
1112
1374
  private personName(person?: {
1113
1375
  name?: string | null;
1114
1376
  } | null) {