@hed-hog/lms 0.0.353 → 0.0.354

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 +9 -9
  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
@@ -2,14 +2,16 @@ import { PrismaModule } from '@hed-hog/api-prisma';
2
2
  import { CoreModule } from '@hed-hog/core';
3
3
  import { QueueModule } from '@hed-hog/queue';
4
4
  import { forwardRef, Module } from '@nestjs/common';
5
+ import { InstructorModule } from '../instructor/instructor.module';
6
+ import { CourseLessonController } from './course-lesson.controller';
5
7
  import { CourseOperationsIntegrationService } from './course-operations-integration.service';
6
8
  import { CourseStructureController } from './course-structure.controller';
9
+ import { LmsSettingController } from './lms-setting.controller';
7
10
  import { CourseStructureService } from './course-structure.service';
8
11
  import { CourseVideoConversionService } from './course-video-conversion.service';
9
- import { LmsCoursesMcpTools } from './course.mcp-tools';
10
12
  import { CourseController } from './course.controller';
13
+ import { LmsCoursesMcpTools } from './course.mcp-tools';
11
14
  import { CourseService } from './course.service';
12
- import { InstructorModule } from '../instructor/instructor.module';
13
15
 
14
16
  @Module({
15
17
  imports: [
@@ -18,7 +20,7 @@ import { InstructorModule } from '../instructor/instructor.module';
18
20
  forwardRef(() => CoreModule),
19
21
  forwardRef(() => QueueModule),
20
22
  ],
21
- controllers: [CourseController, CourseStructureController],
23
+ controllers: [CourseController, CourseStructureController, CourseLessonController, LmsSettingController],
22
24
  providers: [
23
25
  CourseOperationsIntegrationService,
24
26
  CourseService,
@@ -258,65 +258,76 @@ export class CourseService {
258
258
  }
259
259
 
260
260
  async getById(id: number) {
261
- const c = await this.prisma.course.findUnique({
262
- where: { id },
263
- include: {
264
- course_category: {
265
- include: {
266
- category: { select: { id: true, slug: true } },
267
- },
268
- },
269
- course_image: {
270
- where: {
271
- image_type: {
272
- slug: { in: ['course-logo', 'course-banner'] },
261
+ const [c, lessonInstructors] = await Promise.all([
262
+ this.prisma.course.findUnique({
263
+ where: { id },
264
+ include: {
265
+ course_category: {
266
+ include: {
267
+ category: { select: { id: true, slug: true } },
273
268
  },
274
269
  },
275
- include: {
276
- image_type: {
277
- select: {
278
- slug: true,
279
- suggested_width: true,
280
- suggested_height: true,
281
- allowed_extensions: true,
282
- aspect_ratio: true,
270
+ course_image: {
271
+ where: {
272
+ image_type: {
273
+ slug: { in: ['course-logo', 'course-banner'] },
283
274
  },
284
275
  },
285
- file: {
286
- select: { id: true, filename: true },
287
- },
288
- },
289
- orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
290
- },
291
- course_instructor: {
292
- include: {
293
- instructor: {
294
- select: {
295
- id: true,
296
- person: {
297
- select: {
298
- id: true,
299
- name: true,
300
- avatar_id: true,
301
- },
276
+ include: {
277
+ image_type: {
278
+ select: {
279
+ slug: true,
280
+ suggested_width: true,
281
+ suggested_height: true,
282
+ allowed_extensions: true,
283
+ aspect_ratio: true,
302
284
  },
303
285
  },
286
+ file: {
287
+ select: { id: true, filename: true },
288
+ },
289
+ },
290
+ orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
291
+ },
292
+ certificate_template: {
293
+ select: { id: true, slug: true },
294
+ },
295
+ _count: {
296
+ select: {
297
+ course_enrollment: true,
298
+ course_module: true,
299
+ course_class_group: true,
300
+ certificate: true,
304
301
  },
305
302
  },
306
303
  },
307
- certificate_template: {
308
- select: { id: true, slug: true },
304
+ }),
305
+ this.prisma.course_lesson_instructor.findMany({
306
+ where: {
307
+ course_lesson: {
308
+ course_module: {
309
+ course_id: id,
310
+ },
311
+ },
309
312
  },
310
- _count: {
311
- select: {
312
- course_enrollment: true,
313
- course_module: true,
314
- course_class_group: true,
315
- certificate: true,
313
+ select: {
314
+ instructor_id: true,
315
+ role: true,
316
+ instructor: {
317
+ select: {
318
+ id: true,
319
+ person: {
320
+ select: {
321
+ id: true,
322
+ name: true,
323
+ avatar_id: true,
324
+ },
325
+ },
326
+ },
316
327
  },
317
328
  },
318
- },
319
- });
329
+ }),
330
+ ]);
320
331
 
321
332
  if (!c) return null;
322
333
 
@@ -385,6 +396,7 @@ export class CourseService {
385
396
  },
386
397
  extrasById.get(id),
387
398
  projectLinksById.get(id),
399
+ lessonInstructors,
388
400
  );
389
401
  }
390
402
 
@@ -393,7 +405,6 @@ export class CourseService {
393
405
  categorySlugs,
394
406
  logoFileId,
395
407
  bannerFileId,
396
- instructorIds,
397
408
  certificateModel,
398
409
  operationsProjectId,
399
410
  ...data
@@ -414,22 +425,14 @@ export class CourseService {
414
425
  })
415
426
  : [];
416
427
 
417
- // Validate instructor IDs if provided
418
- let validInstructorIds: number[] = [];
419
- if (instructorIds?.length) {
420
- const instructors = await this.prisma.instructor.findMany({
421
- where: { id: { in: instructorIds } },
422
- select: { id: true },
423
- });
424
- const validIds = new Set(instructors.map((i) => i.id));
425
- validInstructorIds = instructorIds.filter((id) => validIds.has(id));
426
- }
427
-
428
428
  const c = await this.prisma.course.create({
429
429
  data: {
430
430
  name: data.name,
431
431
  slug: normalizedSlug,
432
432
  title: resolvedTitle,
433
+ ...(data.locale_id !== undefined && {
434
+ locale_id: data.locale_id,
435
+ }),
433
436
  ...(data.description !== undefined && {
434
437
  description: data.description,
435
438
  }),
@@ -472,14 +475,6 @@ export class CourseService {
472
475
  create: categories.map((item) => ({ category_id: item.id })),
473
476
  },
474
477
  }),
475
- ...(validInstructorIds.length > 0 && {
476
- course_instructor: {
477
- create: validInstructorIds.map((instructorId) => ({
478
- instructor_id: instructorId,
479
- role: 'lead',
480
- })),
481
- },
482
- }),
483
478
  },
484
479
  include: {
485
480
  course_category: {
@@ -501,22 +496,6 @@ export class CourseService {
501
496
  },
502
497
  orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
503
498
  },
504
- course_instructor: {
505
- include: {
506
- instructor: {
507
- select: {
508
- id: true,
509
- person: {
510
- select: {
511
- id: true,
512
- name: true,
513
- avatar_id: true,
514
- },
515
- },
516
- },
517
- },
518
- },
519
- },
520
499
  certificate_template: {
521
500
  select: { id: true, slug: true },
522
501
  },
@@ -566,7 +545,6 @@ export class CourseService {
566
545
  categorySlugs,
567
546
  logoFileId,
568
547
  bannerFileId,
569
- instructorIds,
570
548
  certificateModel,
571
549
  operationsProjectId,
572
550
  ...data
@@ -605,21 +583,6 @@ export class CourseService {
605
583
  })
606
584
  : [];
607
585
 
608
- // Validate instructor IDs if provided
609
- let validInstructorIds: number[] | undefined;
610
- if (instructorIds !== undefined) {
611
- if (instructorIds.length === 0) {
612
- validInstructorIds = [];
613
- } else {
614
- const instructors = await this.prisma.instructor.findMany({
615
- where: { id: { in: instructorIds } },
616
- select: { id: true },
617
- });
618
- const validIds = new Set(instructors.map((i) => i.id));
619
- validInstructorIds = instructorIds.filter((id) => validIds.has(id));
620
- }
621
- }
622
-
623
586
  if (categorySlugs !== undefined) {
624
587
  await this.prisma.course_category.deleteMany({
625
588
  where: { course_id: id },
@@ -632,6 +595,9 @@ export class CourseService {
632
595
  ...(data.name !== undefined && { name: data.name }),
633
596
  ...(resolvedSlug !== undefined && { slug: resolvedSlug }),
634
597
  ...(resolvedTitle !== undefined && { title: resolvedTitle }),
598
+ ...(data.locale_id !== undefined && {
599
+ locale_id: data.locale_id,
600
+ }),
635
601
  ...(data.description !== undefined && { description: data.description }),
636
602
  ...(data.level !== undefined && {
637
603
  level: this.normalizeLevel(data.level),
@@ -700,22 +666,6 @@ export class CourseService {
700
666
  },
701
667
  orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
702
668
  },
703
- course_instructor: {
704
- include: {
705
- instructor: {
706
- select: {
707
- id: true,
708
- person: {
709
- select: {
710
- id: true,
711
- name: true,
712
- avatar_id: true,
713
- },
714
- },
715
- },
716
- },
717
- },
718
- },
719
669
  certificate_template: {
720
670
  select: { id: true, slug: true },
721
671
  },
@@ -723,23 +673,6 @@ export class CourseService {
723
673
  },
724
674
  });
725
675
 
726
- // Handle instructor relationships
727
- if (validInstructorIds !== undefined) {
728
- await this.prisma.course_instructor.deleteMany({
729
- where: { course_id: id },
730
- });
731
-
732
- if (validInstructorIds.length > 0) {
733
- await this.prisma.course_instructor.createMany({
734
- data: validInstructorIds.map((instructorId) => ({
735
- course_id: id,
736
- instructor_id: instructorId,
737
- role: 'lead',
738
- })),
739
- });
740
- }
741
- }
742
-
743
676
  await this.syncCourseImages(id, {
744
677
  logoFileId,
745
678
  bannerFileId,
@@ -808,6 +741,18 @@ export class CourseService {
808
741
  },
809
742
  extras?: CourseExtraFields,
810
743
  project?: CourseProjectFields,
744
+ lessonInstructors?: Array<{
745
+ instructor_id: number;
746
+ role: string;
747
+ instructor?: {
748
+ id: number;
749
+ person?: {
750
+ id: number;
751
+ name: string;
752
+ avatar_id?: number | null;
753
+ } | null;
754
+ } | null;
755
+ }>,
811
756
  ) {
812
757
  const resolvedName = extras?.name ?? c.name ?? null;
813
758
  const resolvedTitle = this.normalizeOptionalText(c.title) ?? c.slug;
@@ -829,6 +774,7 @@ export class CourseService {
829
774
  name: resolvedName ?? '',
830
775
  slug: c.slug,
831
776
  code: c.code ?? null,
777
+ locale_id: c.locale_id ?? null,
832
778
  title: resolvedTitle,
833
779
  description: c.description ?? '',
834
780
  level: c.level,
@@ -894,14 +840,16 @@ export class CourseService {
894
840
  ? String(c.certificate_template_id)
895
841
  : null),
896
842
  progressByModule: metrics?.progressByModule ?? [],
897
- instructors: (c.course_instructor ?? []).map((ci: any) => ({
898
- id: ci.instructor_id,
899
- name: ci.instructor?.person?.name ?? `Instructor #${ci.instructor_id}`,
900
- avatarId: ci.instructor?.person?.avatar_id ?? null,
843
+ instructors: Array.from(
844
+ new Map(
845
+ (lessonInstructors ?? []).map((item) => [item.instructor_id, item]),
846
+ ).values(),
847
+ ).map((item) => ({
848
+ id: item.instructor_id,
849
+ name:
850
+ item.instructor?.person?.name ?? `Instructor #${item.instructor_id}`,
851
+ avatarId: item.instructor?.person?.avatar_id ?? null,
901
852
  })),
902
- instructorIds: (c.course_instructor ?? []).map(
903
- (ci: any) => ci.instructor_id,
904
- ),
905
853
  createdAt: c.created_at,
906
854
  };
907
855
  }
@@ -0,0 +1,14 @@
1
+ import { Type } from 'class-transformer';
2
+ import { IsInt, Min } from 'class-validator';
3
+
4
+ export class CreateCourseLessonFrameDto {
5
+ @Type(() => Number)
6
+ @IsInt()
7
+ @Min(0)
8
+ timeSeconds: number;
9
+
10
+ @Type(() => Number)
11
+ @IsInt()
12
+ @Min(1)
13
+ fileId: number;
14
+ }
@@ -7,11 +7,26 @@ import {
7
7
  IsNotEmpty,
8
8
  IsOptional,
9
9
  IsString,
10
+ Matches,
10
11
  MaxLength,
11
12
  Min,
12
13
  ValidateNested,
13
14
  } from 'class-validator';
14
15
 
16
+ const COURSE_LESSON_FILE_TYPES = [
17
+ 'lesson_audio',
18
+ 'student_download',
19
+ 'supplementary_material',
20
+ 'video_original',
21
+ ] as const;
22
+
23
+ const COURSE_LESSON_FILE_TYPE_REGEX =
24
+ /^(lesson_audio|student_download|supplementary_material|video_original|video_profile:\d+)$/;
25
+
26
+ export type CourseLessonFileType =
27
+ | (typeof COURSE_LESSON_FILE_TYPES)[number]
28
+ | `video_profile:${number}`;
29
+
15
30
  class CourseLessonResourceDto {
16
31
  @IsString()
17
32
  @IsNotEmpty()
@@ -23,13 +38,14 @@ class CourseLessonResourceDto {
23
38
  @IsOptional()
24
39
  fileId?: number;
25
40
 
26
- @IsString()
41
+ @Matches(COURSE_LESSON_FILE_TYPE_REGEX)
27
42
  @IsOptional()
28
- tipo?: string;
43
+ type?: CourseLessonFileType;
29
44
 
30
45
  @IsBoolean()
31
46
  @IsOptional()
32
- publico?: boolean;
47
+ is_public?: boolean;
48
+
33
49
  }
34
50
 
35
51
  export class CreateLessonVideoConversionDto {
@@ -106,11 +106,6 @@ export class CreateCourseDto {
106
106
  @IsOptional()
107
107
  certificateModel?: string | null;
108
108
 
109
- @IsArray()
110
- @IsInt({ each: true })
111
- @IsOptional()
112
- instructorIds?: number[];
113
-
114
109
  @IsInt()
115
110
  @IsOptional()
116
111
  logoFileId?: number | null;
@@ -119,6 +114,11 @@ export class CreateCourseDto {
119
114
  @IsOptional()
120
115
  bannerFileId?: number | null;
121
116
 
117
+ @Type(() => Number)
118
+ @IsInt()
119
+ @IsOptional()
120
+ locale_id?: number | null;
121
+
122
122
  @Type(() => Number)
123
123
  @IsInt()
124
124
  @IsOptional()
@@ -0,0 +1,16 @@
1
+ import { Type } from 'class-transformer';
2
+ import { IsInt, IsOptional, Min } from 'class-validator';
3
+
4
+ export class UpdateCourseLessonFrameDto {
5
+ @Type(() => Number)
6
+ @IsInt()
7
+ @Min(0)
8
+ @IsOptional()
9
+ timeSeconds?: number;
10
+
11
+ @Type(() => Number)
12
+ @IsInt()
13
+ @Min(1)
14
+ @IsOptional()
15
+ fileId?: number;
16
+ }
@@ -6,11 +6,26 @@ import {
6
6
  IsNotEmpty,
7
7
  IsOptional,
8
8
  IsString,
9
+ Matches,
9
10
  MaxLength,
10
11
  Min,
11
12
  ValidateNested,
12
13
  } from 'class-validator';
13
14
 
15
+ const COURSE_LESSON_FILE_TYPES = [
16
+ 'lesson_audio',
17
+ 'student_download',
18
+ 'supplementary_material',
19
+ 'video_original',
20
+ ] as const;
21
+
22
+ const COURSE_LESSON_FILE_TYPE_REGEX =
23
+ /^(lesson_audio|student_download|supplementary_material|video_original|video_profile:\d+)$/;
24
+
25
+ type CourseLessonFileType =
26
+ | (typeof COURSE_LESSON_FILE_TYPES)[number]
27
+ | `video_profile:${number}`;
28
+
14
29
  class CourseResourceDto {
15
30
  @IsString()
16
31
  @IsNotEmpty()
@@ -22,13 +37,13 @@ class CourseResourceDto {
22
37
  @IsOptional()
23
38
  fileId?: number;
24
39
 
25
- @IsString()
40
+ @Matches(COURSE_LESSON_FILE_TYPE_REGEX)
26
41
  @IsOptional()
27
- tipo?: string;
42
+ type?: CourseLessonFileType;
28
43
 
29
44
  @IsBoolean()
30
45
  @IsOptional()
31
- publico?: boolean;
46
+ is_public?: boolean;
32
47
  }
33
48
 
34
49
  export class UpdateCourseResourcesDto {
@@ -0,0 +1,20 @@
1
+ import { Type } from 'class-transformer';
2
+ import { IsArray, IsNumber, IsString, ValidateNested } from 'class-validator';
3
+
4
+ class TranscriptionSegmentItemDTO {
5
+ @IsNumber()
6
+ startSeconds: number;
7
+
8
+ @IsNumber()
9
+ endSeconds: number;
10
+
11
+ @IsString()
12
+ text: string;
13
+ }
14
+
15
+ export class UpdateTranscriptionSegmentsDTO {
16
+ @IsArray()
17
+ @ValidateNested({ each: true })
18
+ @Type(() => TranscriptionSegmentItemDTO)
19
+ segments: TranscriptionSegmentItemDTO[];
20
+ }
@@ -0,0 +1,30 @@
1
+ import { SettingService } from '@hed-hog/core';
2
+ import { Controller, forwardRef, Get, Inject } from '@nestjs/common';
3
+ import { Role } from '@hed-hog/api';
4
+
5
+ @Role()
6
+ @Controller('lms/settings')
7
+ export class LmsSettingController {
8
+ constructor(
9
+ @Inject(forwardRef(() => SettingService))
10
+ private readonly settingService: SettingService,
11
+ ) {}
12
+
13
+ @Get()
14
+ async getFeatureFlags() {
15
+ const v = await this.settingService.getSettingValues([
16
+ 'lms-video-conversion-enabled',
17
+ 'lms-image-extraction-enabled',
18
+ 'lms-audio-transcription-enabled',
19
+ 'lms-youtube-provider-enabled',
20
+ 'lms-vimeo-provider-enabled',
21
+ ]);
22
+ return {
23
+ videoConversionEnabled: v['lms-video-conversion-enabled'] !== false,
24
+ imageExtractionEnabled: v['lms-image-extraction-enabled'] !== false,
25
+ transcriptionEnabled: v['lms-audio-transcription-enabled'] !== false,
26
+ youtubeEnabled: v['lms-youtube-provider-enabled'] !== false,
27
+ vimeoEnabled: v['lms-vimeo-provider-enabled'] !== false,
28
+ };
29
+ }
30
+ }