@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.
- package/dist/course/course-audio-transcription.service.d.ts +29 -0
- package/dist/course/course-audio-transcription.service.d.ts.map +1 -0
- package/dist/course/course-audio-transcription.service.js +291 -0
- package/dist/course/course-audio-transcription.service.js.map +1 -0
- package/dist/course/course-lesson.controller.d.ts +10 -0
- package/dist/course/course-lesson.controller.d.ts.map +1 -0
- package/dist/course/course-lesson.controller.js +62 -0
- package/dist/course/course-lesson.controller.js.map +1 -0
- package/dist/course/course-structure.controller.d.ts +41 -15
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +50 -6
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +50 -15
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +238 -73
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-conversion.service.d.ts +20 -2
- package/dist/course/course-video-conversion.service.d.ts.map +1 -1
- package/dist/course/course-video-conversion.service.js +730 -10
- package/dist/course/course-video-conversion.service.js.map +1 -1
- package/dist/course/course.controller.d.ts +24 -8
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +5 -3
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +24 -8
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +112 -176
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-lesson-frame.dto.d.ts +5 -0
- package/dist/course/dto/create-course-lesson-frame.dto.d.ts.map +1 -0
- package/dist/course/dto/create-course-lesson-frame.dto.js +30 -0
- package/dist/course/dto/create-course-lesson-frame.dto.js.map +1 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +4 -2
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +10 -3
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course.dto.d.ts +1 -1
- package/dist/course/dto/create-course.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course.dto.js +6 -6
- package/dist/course/dto/create-course.dto.js.map +1 -1
- package/dist/course/dto/update-course-lesson-frame.dto.d.ts +5 -0
- package/dist/course/dto/update-course-lesson-frame.dto.d.ts.map +1 -0
- package/dist/course/dto/update-course-lesson-frame.dto.js +32 -0
- package/dist/course/dto/update-course-lesson-frame.dto.js.map +1 -0
- package/dist/course/dto/update-course-resources.dto.d.ts +4 -2
- package/dist/course/dto/update-course-resources.dto.d.ts.map +1 -1
- package/dist/course/dto/update-course-resources.dto.js +10 -3
- package/dist/course/dto/update-course-resources.dto.js.map +1 -1
- package/dist/course/dto/update-transcription-segments.dto.d.ts +10 -0
- package/dist/course/dto/update-transcription-segments.dto.d.ts.map +1 -0
- package/dist/course/dto/update-transcription-segments.dto.js +38 -0
- package/dist/course/dto/update-transcription-segments.dto.js.map +1 -0
- package/dist/course/lms-setting.controller.d.ts +13 -0
- package/dist/course/lms-setting.controller.d.ts.map +1 -0
- package/dist/course/lms-setting.controller.js +53 -0
- package/dist/course/lms-setting.controller.js.map +1 -0
- package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-admin.service.js +74 -33
- package/dist/enterprise/training/training-admin.service.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +6 -0
- package/dist/lms.module.js.map +1 -1
- package/hedhog/data/route.yaml +63 -0
- package/hedhog/data/setting_group.yaml +76 -0
- package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +10 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +7 -2
- package/hedhog/frontend/app/courses/[id]/_components/CourseDangerZoneCard.tsx.ejs +5 -2
- package/hedhog/frontend/app/courses/[id]/_components/CourseFlagsCard.tsx.ejs +7 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseMediaCard.tsx.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +95 -50
- package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +11 -36
- package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +19 -5
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +30 -10
- package/hedhog/frontend/app/courses/[id]/structure/_components/CourseInstructorsSummaryCard.tsx.ejs +95 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +29 -11
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +42 -31
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +10 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +51 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +25 -22
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +12 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +315 -229
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +3220 -534
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +20 -15
- package/hedhog/frontend/app/courses/[id]/structure/_components/icon-action-tooltip.tsx.ejs +35 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +76 -67
- package/hedhog/frontend/app/courses/[id]/structure/_components/search-filter.tsx.ejs +13 -10
- package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +18 -16
- package/hedhog/frontend/app/courses/[id]/structure/_components/sortable-tree-row.tsx.ejs +6 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +23 -11
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-helpers.ts.ejs +48 -9
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-course.tsx.ejs +11 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +121 -15
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +69 -14
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row.tsx.ejs +11 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +31 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +32 -6
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +57 -3
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +46 -6
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +14 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lesson-audio-files.ts.ejs +23 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-lms-settings-query.ts.ejs +34 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-transcription-segments.ts.ejs +76 -0
- package/hedhog/frontend/messages/en.json +39 -3
- package/hedhog/frontend/messages/pt.json +39 -3
- package/hedhog/table/course.yaml +8 -0
- package/hedhog/table/course_lesson_file.yaml +12 -4
- package/hedhog/table/course_lesson_transcription_segment.yaml +22 -0
- package/hedhog/table/course_lesson_video_frame.yaml +25 -0
- package/package.json +8 -8
- package/src/course/course-audio-transcription.service.ts +393 -0
- package/src/course/course-lesson.controller.ts +28 -0
- package/src/course/course-structure.controller.ts +49 -3
- package/src/course/course-structure.service.ts +294 -32
- package/src/course/course-video-conversion.service.ts +972 -6
- package/src/course/course.module.ts +5 -3
- package/src/course/course.service.ts +87 -139
- package/src/course/dto/create-course-lesson-frame.dto.ts +14 -0
- package/src/course/dto/create-course-structure-lesson.dto.ts +19 -3
- package/src/course/dto/create-course.dto.ts +5 -5
- package/src/course/dto/update-course-lesson-frame.dto.ts +16 -0
- package/src/course/dto/update-course-resources.dto.ts +18 -3
- package/src/course/dto/update-transcription-segments.dto.ts +20 -0
- package/src/course/lms-setting.controller.ts +30 -0
- package/src/enterprise/training/training-admin.service.ts +77 -24
- package/src/index.ts +2 -0
- package/src/lms.module.ts +6 -0
- 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
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
slug:
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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:
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
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
|
-
@
|
|
41
|
+
@Matches(COURSE_LESSON_FILE_TYPE_REGEX)
|
|
27
42
|
@IsOptional()
|
|
28
|
-
|
|
43
|
+
type?: CourseLessonFileType;
|
|
29
44
|
|
|
30
45
|
@IsBoolean()
|
|
31
46
|
@IsOptional()
|
|
32
|
-
|
|
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
|
-
@
|
|
40
|
+
@Matches(COURSE_LESSON_FILE_TYPE_REGEX)
|
|
26
41
|
@IsOptional()
|
|
27
|
-
|
|
42
|
+
type?: CourseLessonFileType;
|
|
28
43
|
|
|
29
44
|
@IsBoolean()
|
|
30
45
|
@IsOptional()
|
|
31
|
-
|
|
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
|
+
}
|