@hed-hog/lms 0.0.331 → 0.0.338
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/class-group/class-group.controller.d.ts +3 -3
- package/dist/class-group/class-group.service.d.ts +3 -3
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +12 -20
- package/dist/course/course.service.js.map +1 -1
- package/dist/enterprise/enterprise.controller.d.ts +72 -0
- package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
- package/dist/enterprise/enterprise.controller.js +10 -0
- package/dist/enterprise/enterprise.controller.js.map +1 -1
- package/dist/enterprise/enterprise.service.d.ts +78 -0
- package/dist/enterprise/enterprise.service.d.ts.map +1 -1
- package/dist/enterprise/enterprise.service.js +413 -40
- package/dist/enterprise/enterprise.service.js.map +1 -1
- package/dist/enterprise/training/training-admin.controller.d.ts +6 -3
- package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
- package/dist/enterprise/training/training-admin.controller.js +10 -6
- package/dist/enterprise/training/training-admin.controller.js.map +1 -1
- package/dist/enterprise/training/training-admin.service.d.ts +8 -2
- package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-admin.service.js +108 -52
- package/dist/enterprise/training/training-admin.service.js.map +1 -1
- package/dist/enterprise/training/training-viewer.controller.d.ts +3 -0
- package/dist/enterprise/training/training-viewer.controller.d.ts.map +1 -1
- package/dist/evaluation/evaluation.controller.d.ts +4 -4
- package/dist/evaluation/evaluation.service.d.ts +4 -4
- package/dist/instructor/dto/create-instructor-skill.dto.d.ts +0 -4
- package/dist/instructor/dto/create-instructor-skill.dto.d.ts.map +1 -1
- package/dist/instructor/dto/create-instructor-skill.dto.js +0 -21
- package/dist/instructor/dto/create-instructor-skill.dto.js.map +1 -1
- package/dist/instructor/dto/update-instructor-skill.dto.d.ts +0 -4
- package/dist/instructor/dto/update-instructor-skill.dto.d.ts.map +1 -1
- package/dist/instructor/dto/update-instructor-skill.dto.js +0 -22
- package/dist/instructor/dto/update-instructor-skill.dto.js.map +1 -1
- package/dist/instructor/instructor-skill.controller.d.ts +4 -4
- package/dist/instructor/instructor-skill.service.d.ts +4 -7
- package/dist/instructor/instructor-skill.service.d.ts.map +1 -1
- package/dist/instructor/instructor-skill.service.js +2 -89
- package/dist/instructor/instructor-skill.service.js.map +1 -1
- package/dist/instructor/instructor.controller.d.ts +20 -0
- package/dist/instructor/instructor.controller.d.ts.map +1 -1
- package/dist/instructor/instructor.controller.js +19 -0
- package/dist/instructor/instructor.controller.js.map +1 -1
- package/dist/instructor/instructor.service.d.ts +25 -0
- package/dist/instructor/instructor.service.d.ts.map +1 -1
- package/dist/instructor/instructor.service.js +70 -18
- package/dist/instructor/instructor.service.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js.map +1 -1
- package/hedhog/data/route.yaml +23 -1
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +42 -24
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +3 -3
- package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +591 -0
- package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/classes/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +3 -33
- package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +9 -9
- package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +109 -0
- package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +40 -13
- package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +76 -81
- package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +60 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-scheduled-classes-tab.tsx.ejs +406 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +242 -33
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +185 -0
- package/hedhog/frontend/app/courses/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-activity-timeline.tsx.ejs +87 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +4 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +31 -5
- package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +79 -20
- package/hedhog/frontend/app/enterprise/_components/enterprise-company-identity-card.tsx.ejs +11 -2
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +201 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +55 -24
- package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +430 -296
- package/hedhog/frontend/app/enterprise/_components/enterprise-mocks.ts.ejs +277 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-overview-analytics.tsx.ejs +205 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +97 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +82 -57
- package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +4 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +60 -22
- package/hedhog/frontend/app/enterprise/_components/enterprise-types.ts.ejs +54 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +211 -0
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -7
- package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/exams/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +51 -104
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +625 -366
- package/hedhog/frontend/app/instructors/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/paths/page.tsx.ejs +9 -4
- package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/training/page.tsx.ejs +9 -4
- package/hedhog/frontend/messages/en.json +101 -10
- package/hedhog/frontend/messages/pt.json +101 -10
- package/hedhog/table/enterprise_student_license_event.yaml +30 -0
- package/hedhog/table/instructor_skill.yaml +0 -11
- package/package.json +7 -7
- package/src/course/course.service.ts +12 -24
- package/src/enterprise/enterprise.controller.ts +5 -0
- package/src/enterprise/enterprise.service.ts +507 -29
- package/src/enterprise/training/training-admin.controller.ts +4 -0
- package/src/enterprise/training/training-admin.service.ts +115 -51
- package/src/instructor/dto/create-instructor-skill.dto.ts +0 -17
- package/src/instructor/dto/update-instructor-skill.dto.ts +0 -18
- package/src/instructor/instructor-skill.service.ts +2 -97
- package/src/instructor/instructor.controller.ts +16 -0
- package/src/instructor/instructor.service.ts +85 -10
- package/src/lms.module.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PrismaService } from '@hed-hog/api-prisma';
|
|
1
|
+
import { Prisma, PrismaService } from '@hed-hog/api-prisma';
|
|
2
2
|
import {
|
|
3
3
|
BadRequestException,
|
|
4
4
|
ConflictException,
|
|
@@ -11,6 +11,30 @@ import {
|
|
|
11
11
|
export class TrainingAdminService {
|
|
12
12
|
constructor(private readonly prisma: PrismaService) {}
|
|
13
13
|
|
|
14
|
+
private async recordLicenseEvent(params: {
|
|
15
|
+
enterpriseId: number;
|
|
16
|
+
personId: number;
|
|
17
|
+
eventType: 'assigned' | 'revoked' | 'status_changed';
|
|
18
|
+
previousStatus?: string | null;
|
|
19
|
+
nextStatus?: string | null;
|
|
20
|
+
}) {
|
|
21
|
+
const eventModel = (this.prisma as any).enterprise_student_license_event;
|
|
22
|
+
if (!eventModel) return;
|
|
23
|
+
try {
|
|
24
|
+
await eventModel.create({
|
|
25
|
+
data: {
|
|
26
|
+
enterprise_id: params.enterpriseId,
|
|
27
|
+
person_id: params.personId,
|
|
28
|
+
event_type: params.eventType,
|
|
29
|
+
previous_status: params.previousStatus ?? null,
|
|
30
|
+
next_status: params.nextStatus ?? null,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
} catch {
|
|
34
|
+
// Keep license operations working until the YAML table is applied.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
14
38
|
async getDashboard(userId: number, requestedEnterpriseId?: number) {
|
|
15
39
|
if (requestedEnterpriseId !== undefined) {
|
|
16
40
|
const allowed = await this.prisma.enterprise_user.findFirst({
|
|
@@ -311,6 +335,8 @@ export class TrainingAdminService {
|
|
|
311
335
|
userId: number,
|
|
312
336
|
options: {
|
|
313
337
|
enterpriseId?: number;
|
|
338
|
+
page?: number;
|
|
339
|
+
pageSize?: number;
|
|
314
340
|
search?: string;
|
|
315
341
|
status?: string;
|
|
316
342
|
deliveryMode?: string;
|
|
@@ -340,62 +366,71 @@ export class TrainingAdminService {
|
|
|
340
366
|
}
|
|
341
367
|
|
|
342
368
|
const enterpriseId = enterpriseUser.enterprise_id;
|
|
343
|
-
const { search, status, deliveryMode, instructorId } = options;
|
|
369
|
+
const { page = 1, pageSize = 10, search, status, deliveryMode, instructorId } = options;
|
|
344
370
|
const now = new Date();
|
|
345
371
|
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
],
|
|
361
|
-
},
|
|
362
|
-
]
|
|
363
|
-
: []),
|
|
364
|
-
],
|
|
365
|
-
},
|
|
366
|
-
orderBy: { start_date: 'desc' },
|
|
367
|
-
include: {
|
|
368
|
-
course: {
|
|
369
|
-
select: {
|
|
370
|
-
id: true,
|
|
371
|
-
title: true,
|
|
372
|
-
course_image: {
|
|
373
|
-
where: { image_type: { slug: { in: ['course-logo', 'course-banner'] } } },
|
|
374
|
-
orderBy: { is_primary: 'desc' as const },
|
|
375
|
-
select: {
|
|
376
|
-
file: { select: { location: true } },
|
|
377
|
-
image_type: { select: { slug: true } },
|
|
372
|
+
const where = {
|
|
373
|
+
AND: [
|
|
374
|
+
{ enterprise_class_group: { some: { enterprise_id: enterpriseId } } },
|
|
375
|
+
...(status ? [{ status: status as any }] : []),
|
|
376
|
+
...(deliveryMode ? [{ delivery_mode: deliveryMode as any }] : []),
|
|
377
|
+
...(instructorId !== undefined ? [{ instructor_id: instructorId }] : []),
|
|
378
|
+
...(search
|
|
379
|
+
? [
|
|
380
|
+
{
|
|
381
|
+
OR: [
|
|
382
|
+
{ title: { contains: search, mode: 'insensitive' as const } },
|
|
383
|
+
{ code: { contains: search, mode: 'insensitive' as const } },
|
|
384
|
+
{ course: { title: { contains: search, mode: 'insensitive' as const } } },
|
|
385
|
+
],
|
|
378
386
|
},
|
|
387
|
+
]
|
|
388
|
+
: []),
|
|
389
|
+
],
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const include = Prisma.validator<Prisma.course_class_groupInclude>()({
|
|
393
|
+
course: {
|
|
394
|
+
select: {
|
|
395
|
+
id: true,
|
|
396
|
+
title: true,
|
|
397
|
+
course_image: {
|
|
398
|
+
where: { image_type: { slug: { in: ['course-logo', 'course-banner'] } } },
|
|
399
|
+
orderBy: { is_primary: 'desc' as const },
|
|
400
|
+
select: {
|
|
401
|
+
file: { select: { location: true } },
|
|
402
|
+
image_type: { select: { slug: true } },
|
|
379
403
|
},
|
|
380
404
|
},
|
|
381
405
|
},
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
},
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
},
|
|
406
|
+
},
|
|
407
|
+
instructor: {
|
|
408
|
+
include: { person: { select: { name: true } } },
|
|
409
|
+
},
|
|
410
|
+
_count: {
|
|
411
|
+
select: {
|
|
412
|
+
course_enrollment: { where: { status: { not: 'cancelled' } } },
|
|
389
413
|
},
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
414
|
+
},
|
|
415
|
+
course_class_session: {
|
|
416
|
+
where: { session_date: { gte: now } },
|
|
417
|
+
orderBy: { session_date: 'asc' as const },
|
|
418
|
+
take: 1,
|
|
394
419
|
select: { session_date: true, start_time: true, location: true },
|
|
395
|
-
},
|
|
396
420
|
},
|
|
397
421
|
});
|
|
398
422
|
|
|
423
|
+
const [classGroups, total] = await this.prisma.$transaction([
|
|
424
|
+
this.prisma.course_class_group.findMany({
|
|
425
|
+
where,
|
|
426
|
+
orderBy: { start_date: 'desc' },
|
|
427
|
+
include,
|
|
428
|
+
skip: (page - 1) * pageSize,
|
|
429
|
+
take: pageSize,
|
|
430
|
+
}),
|
|
431
|
+
this.prisma.course_class_group.count({ where }),
|
|
432
|
+
]);
|
|
433
|
+
|
|
399
434
|
const cgIds = classGroups.map((cg) => cg.id);
|
|
400
435
|
|
|
401
436
|
const completedCounts =
|
|
@@ -453,7 +488,7 @@ export class TrainingAdminService {
|
|
|
453
488
|
};
|
|
454
489
|
});
|
|
455
490
|
|
|
456
|
-
return { data, total:
|
|
491
|
+
return { data, total, page, pageSize, lastPage: Math.ceil(total / pageSize) };
|
|
457
492
|
}
|
|
458
493
|
|
|
459
494
|
// ── Class-group detail endpoints ─────────────────────────────────────────────
|
|
@@ -830,17 +865,32 @@ export class TrainingAdminService {
|
|
|
830
865
|
if (existingStudent.status !== 'inactive') {
|
|
831
866
|
throw new ConflictException('This person already has an active license');
|
|
832
867
|
}
|
|
833
|
-
|
|
868
|
+
const updated = await this.prisma.enterprise_student.update({
|
|
834
869
|
where: { id: existingStudent.id },
|
|
835
870
|
data: { status: 'active' },
|
|
836
871
|
select: { id: true, person_id: true, status: true, created_at: true },
|
|
837
872
|
});
|
|
873
|
+
await this.recordLicenseEvent({
|
|
874
|
+
enterpriseId,
|
|
875
|
+
personId,
|
|
876
|
+
eventType: 'status_changed',
|
|
877
|
+
previousStatus: existingStudent.status,
|
|
878
|
+
nextStatus: 'active',
|
|
879
|
+
});
|
|
880
|
+
return updated;
|
|
838
881
|
}
|
|
839
882
|
|
|
840
|
-
|
|
883
|
+
const createdStudent = await this.prisma.enterprise_student.create({
|
|
841
884
|
data: { enterprise_id: enterpriseId, person_id: personId, status: 'pending' },
|
|
842
885
|
select: { id: true, person_id: true, status: true, created_at: true },
|
|
843
886
|
});
|
|
887
|
+
await this.recordLicenseEvent({
|
|
888
|
+
enterpriseId,
|
|
889
|
+
personId,
|
|
890
|
+
eventType: 'assigned',
|
|
891
|
+
nextStatus: 'pending',
|
|
892
|
+
});
|
|
893
|
+
return createdStudent;
|
|
844
894
|
}
|
|
845
895
|
|
|
846
896
|
async revokeLicense(userId: number, personId: number) {
|
|
@@ -852,15 +902,23 @@ export class TrainingAdminService {
|
|
|
852
902
|
|
|
853
903
|
const existing = await this.prisma.enterprise_student.findFirst({
|
|
854
904
|
where: { enterprise_id: enterpriseUser.enterprise_id, person_id: personId },
|
|
855
|
-
select: { id: true },
|
|
905
|
+
select: { id: true, status: true },
|
|
856
906
|
});
|
|
857
907
|
if (!existing) throw new NotFoundException('Student not found in your enterprise');
|
|
858
908
|
|
|
859
|
-
|
|
909
|
+
const updated = await this.prisma.enterprise_student.update({
|
|
860
910
|
where: { id: existing.id },
|
|
861
911
|
data: { status: 'inactive' },
|
|
862
912
|
select: { id: true },
|
|
863
913
|
});
|
|
914
|
+
await this.recordLicenseEvent({
|
|
915
|
+
enterpriseId: enterpriseUser.enterprise_id,
|
|
916
|
+
personId,
|
|
917
|
+
eventType: 'revoked',
|
|
918
|
+
previousStatus: existing.status,
|
|
919
|
+
nextStatus: 'inactive',
|
|
920
|
+
});
|
|
921
|
+
return updated;
|
|
864
922
|
}
|
|
865
923
|
|
|
866
924
|
// ── Enrollment methods ────────────────────────────────────────────────────────
|
|
@@ -1078,6 +1136,12 @@ export class TrainingAdminService {
|
|
|
1078
1136
|
},
|
|
1079
1137
|
select: { id: true },
|
|
1080
1138
|
});
|
|
1139
|
+
await this.recordLicenseEvent({
|
|
1140
|
+
enterpriseId,
|
|
1141
|
+
personId,
|
|
1142
|
+
eventType: 'assigned',
|
|
1143
|
+
nextStatus: 'active',
|
|
1144
|
+
});
|
|
1081
1145
|
}
|
|
1082
1146
|
|
|
1083
1147
|
const existingEnrollment = await this.prisma.course_enrollment.findFirst({
|
|
@@ -5,23 +5,6 @@ export class CreateInstructorSkillDto {
|
|
|
5
5
|
@MaxLength(255)
|
|
6
6
|
slug: string;
|
|
7
7
|
|
|
8
|
-
@IsString()
|
|
9
|
-
@MaxLength(255)
|
|
10
|
-
namePt: string;
|
|
11
|
-
|
|
12
|
-
@IsOptional()
|
|
13
|
-
@IsString()
|
|
14
|
-
@MaxLength(255)
|
|
15
|
-
nameEn?: string;
|
|
16
|
-
|
|
17
|
-
@IsOptional()
|
|
18
|
-
@IsString()
|
|
19
|
-
descriptionPt?: string;
|
|
20
|
-
|
|
21
|
-
@IsOptional()
|
|
22
|
-
@IsString()
|
|
23
|
-
descriptionEn?: string;
|
|
24
|
-
|
|
25
8
|
@IsOptional()
|
|
26
9
|
@IsEnum(['active', 'inactive'])
|
|
27
10
|
status?: 'active' | 'inactive';
|
|
@@ -6,24 +6,6 @@ export class UpdateInstructorSkillDto {
|
|
|
6
6
|
@MaxLength(255)
|
|
7
7
|
slug?: string;
|
|
8
8
|
|
|
9
|
-
@IsOptional()
|
|
10
|
-
@IsString()
|
|
11
|
-
@MaxLength(255)
|
|
12
|
-
namePt?: string;
|
|
13
|
-
|
|
14
|
-
@IsOptional()
|
|
15
|
-
@IsString()
|
|
16
|
-
@MaxLength(255)
|
|
17
|
-
nameEn?: string;
|
|
18
|
-
|
|
19
|
-
@IsOptional()
|
|
20
|
-
@IsString()
|
|
21
|
-
descriptionPt?: string;
|
|
22
|
-
|
|
23
|
-
@IsOptional()
|
|
24
|
-
@IsString()
|
|
25
|
-
descriptionEn?: string;
|
|
26
|
-
|
|
27
9
|
@IsOptional()
|
|
28
10
|
@IsEnum(['active', 'inactive'])
|
|
29
11
|
status?: 'active' | 'inactive';
|
|
@@ -11,10 +11,6 @@ export class InstructorSkillService {
|
|
|
11
11
|
return (this.prisma as any).instructor_skill;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
private get localeClient() {
|
|
15
|
-
return (this.prisma as any).instructor_skill_locale;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
async list(params: { page?: number; pageSize?: number; search?: string }) {
|
|
19
15
|
const page = Math.max(Number(params.page) || 1, 1);
|
|
20
16
|
const pageSize = Math.max(Number(params.pageSize) || 20, 1);
|
|
@@ -24,14 +20,7 @@ export class InstructorSkillService {
|
|
|
24
20
|
const where: any = {};
|
|
25
21
|
|
|
26
22
|
if (search) {
|
|
27
|
-
where.
|
|
28
|
-
{ slug: { contains: search, mode: 'insensitive' } },
|
|
29
|
-
{
|
|
30
|
-
instructor_skill_locale: {
|
|
31
|
-
some: { name: { contains: search, mode: 'insensitive' } },
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
];
|
|
23
|
+
where.slug = { contains: search, mode: 'insensitive' };
|
|
35
24
|
}
|
|
36
25
|
|
|
37
26
|
const [rows, total] = await Promise.all([
|
|
@@ -39,11 +28,6 @@ export class InstructorSkillService {
|
|
|
39
28
|
where,
|
|
40
29
|
skip,
|
|
41
30
|
take: pageSize,
|
|
42
|
-
include: {
|
|
43
|
-
instructor_skill_locale: {
|
|
44
|
-
select: { locale_id: true, name: true, description: true },
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
31
|
orderBy: { slug: 'asc' },
|
|
48
32
|
}),
|
|
49
33
|
this.skillClient.count({ where }),
|
|
@@ -60,11 +44,6 @@ export class InstructorSkillService {
|
|
|
60
44
|
async getAll() {
|
|
61
45
|
const rows = await this.skillClient.findMany({
|
|
62
46
|
where: { status: 'active' },
|
|
63
|
-
include: {
|
|
64
|
-
instructor_skill_locale: {
|
|
65
|
-
select: { locale_id: true, name: true },
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
47
|
orderBy: { slug: 'asc' },
|
|
69
48
|
});
|
|
70
49
|
|
|
@@ -79,8 +58,6 @@ export class InstructorSkillService {
|
|
|
79
58
|
},
|
|
80
59
|
});
|
|
81
60
|
|
|
82
|
-
await this.syncLocales(skill.id, dto);
|
|
83
|
-
|
|
84
61
|
return this.getSkillById(skill.id);
|
|
85
62
|
}
|
|
86
63
|
|
|
@@ -103,8 +80,6 @@ export class InstructorSkillService {
|
|
|
103
80
|
await this.skillClient.update({ where: { id }, data });
|
|
104
81
|
}
|
|
105
82
|
|
|
106
|
-
await this.syncLocales(id, dto);
|
|
107
|
-
|
|
108
83
|
return this.getSkillById(id);
|
|
109
84
|
}
|
|
110
85
|
|
|
@@ -124,11 +99,6 @@ export class InstructorSkillService {
|
|
|
124
99
|
private async getSkillById(id: number) {
|
|
125
100
|
const row = await this.skillClient.findUnique({
|
|
126
101
|
where: { id },
|
|
127
|
-
include: {
|
|
128
|
-
instructor_skill_locale: {
|
|
129
|
-
select: { locale_id: true, name: true, description: true },
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
102
|
});
|
|
133
103
|
|
|
134
104
|
if (!row) {
|
|
@@ -143,72 +113,7 @@ export class InstructorSkillService {
|
|
|
143
113
|
id: row.id,
|
|
144
114
|
slug: row.slug,
|
|
145
115
|
status: row.status,
|
|
146
|
-
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private async syncLocales(
|
|
151
|
-
skillId: number,
|
|
152
|
-
dto: CreateInstructorSkillDto | UpdateInstructorSkillDto,
|
|
153
|
-
) {
|
|
154
|
-
const localeIds = await this.resolveLocaleIds();
|
|
155
|
-
|
|
156
|
-
if (dto.namePt !== undefined && localeIds.pt !== null) {
|
|
157
|
-
const existing = await this.localeClient.findFirst({
|
|
158
|
-
where: { instructor_skill_id: skillId, locale_id: localeIds.pt },
|
|
159
|
-
select: { id: true },
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
if (existing) {
|
|
163
|
-
await this.localeClient.update({
|
|
164
|
-
where: { id: existing.id },
|
|
165
|
-
data: { name: dto.namePt, description: dto.descriptionPt ?? null },
|
|
166
|
-
});
|
|
167
|
-
} else {
|
|
168
|
-
await this.localeClient.create({
|
|
169
|
-
data: {
|
|
170
|
-
instructor_skill_id: skillId,
|
|
171
|
-
locale_id: localeIds.pt,
|
|
172
|
-
name: dto.namePt,
|
|
173
|
-
description: dto.descriptionPt ?? null,
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (dto.nameEn !== undefined && localeIds.en !== null) {
|
|
180
|
-
const existing = await this.localeClient.findFirst({
|
|
181
|
-
where: { instructor_skill_id: skillId, locale_id: localeIds.en },
|
|
182
|
-
select: { id: true },
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
if (existing) {
|
|
186
|
-
await this.localeClient.update({
|
|
187
|
-
where: { id: existing.id },
|
|
188
|
-
data: { name: dto.nameEn, description: dto.descriptionEn ?? null },
|
|
189
|
-
});
|
|
190
|
-
} else {
|
|
191
|
-
await this.localeClient.create({
|
|
192
|
-
data: {
|
|
193
|
-
instructor_skill_id: skillId,
|
|
194
|
-
locale_id: localeIds.en,
|
|
195
|
-
name: dto.nameEn,
|
|
196
|
-
description: dto.descriptionEn ?? null,
|
|
197
|
-
},
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private async resolveLocaleIds() {
|
|
204
|
-
const locales = await this.prisma.locale.findMany({
|
|
205
|
-
where: { code: { in: ['pt', 'en'] } },
|
|
206
|
-
select: { id: true, code: true },
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
pt: locales.find((l) => l.code === 'pt')?.id ?? null,
|
|
211
|
-
en: locales.find((l) => l.code === 'en')?.id ?? null,
|
|
116
|
+
name: row.slug,
|
|
212
117
|
};
|
|
213
118
|
}
|
|
214
119
|
}
|
|
@@ -53,6 +53,22 @@ export class InstructorController {
|
|
|
53
53
|
return this.instructorService.create(dto);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
@Get(':id/class-groups')
|
|
57
|
+
getInstructorClassGroups(
|
|
58
|
+
@Param('id', ParseIntPipe) id: number,
|
|
59
|
+
@Query('page', new ParseIntPipe({ optional: true })) page?: number,
|
|
60
|
+
@Query('pageSize', new ParseIntPipe({ optional: true })) pageSize?: number,
|
|
61
|
+
@Query('search') search?: string,
|
|
62
|
+
@Query('status') status?: string,
|
|
63
|
+
) {
|
|
64
|
+
return this.instructorService.getInstructorClassGroups(id, {
|
|
65
|
+
page,
|
|
66
|
+
pageSize,
|
|
67
|
+
search,
|
|
68
|
+
status,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
@Get(':id')
|
|
57
73
|
getById(@Param('id', ParseIntPipe) id: number) {
|
|
58
74
|
return this.instructorService.getById(id);
|
|
@@ -146,10 +146,6 @@ export class InstructorService {
|
|
|
146
146
|
select: {
|
|
147
147
|
id: true,
|
|
148
148
|
slug: true,
|
|
149
|
-
instructor_skill_locale: {
|
|
150
|
-
take: 1,
|
|
151
|
-
select: { name: true },
|
|
152
|
-
},
|
|
153
149
|
},
|
|
154
150
|
},
|
|
155
151
|
},
|
|
@@ -170,7 +166,7 @@ export class InstructorService {
|
|
|
170
166
|
existing.push({
|
|
171
167
|
id: a.instructor_skill.id,
|
|
172
168
|
slug: a.instructor_skill.slug,
|
|
173
|
-
name: a.instructor_skill.
|
|
169
|
+
name: a.instructor_skill.slug,
|
|
174
170
|
});
|
|
175
171
|
skillsMap.set(a.instructor_id, existing);
|
|
176
172
|
}
|
|
@@ -470,10 +466,6 @@ export class InstructorService {
|
|
|
470
466
|
select: {
|
|
471
467
|
id: true,
|
|
472
468
|
slug: true,
|
|
473
|
-
instructor_skill_locale: {
|
|
474
|
-
take: 1,
|
|
475
|
-
select: { name: true },
|
|
476
|
-
},
|
|
477
469
|
},
|
|
478
470
|
},
|
|
479
471
|
},
|
|
@@ -495,7 +487,7 @@ export class InstructorService {
|
|
|
495
487
|
.map((a) => ({
|
|
496
488
|
id: a.instructor_skill.id,
|
|
497
489
|
slug: a.instructor_skill.slug,
|
|
498
|
-
name: a.instructor_skill.
|
|
490
|
+
name: a.instructor_skill.slug,
|
|
499
491
|
}));
|
|
500
492
|
|
|
501
493
|
return {
|
|
@@ -924,4 +916,87 @@ export class InstructorService {
|
|
|
924
916
|
});
|
|
925
917
|
}
|
|
926
918
|
}
|
|
919
|
+
|
|
920
|
+
async getInstructorClassGroups(
|
|
921
|
+
instructorId: number,
|
|
922
|
+
options: {
|
|
923
|
+
page?: number;
|
|
924
|
+
pageSize?: number;
|
|
925
|
+
search?: string;
|
|
926
|
+
status?: string;
|
|
927
|
+
} = {},
|
|
928
|
+
) {
|
|
929
|
+
const { page = 1, pageSize = 6, search, status } = options;
|
|
930
|
+
|
|
931
|
+
const where: Prisma.course_class_groupWhereInput = {
|
|
932
|
+
instructor_id: instructorId,
|
|
933
|
+
...(status ? { status: status as any } : {}),
|
|
934
|
+
...(search
|
|
935
|
+
? {
|
|
936
|
+
OR: [
|
|
937
|
+
{ title: { contains: search, mode: 'insensitive' } },
|
|
938
|
+
{ code: { contains: search, mode: 'insensitive' } },
|
|
939
|
+
{ course: { title: { contains: search, mode: 'insensitive' } } },
|
|
940
|
+
],
|
|
941
|
+
}
|
|
942
|
+
: {}),
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
const [classGroups, total] = await this.prisma.$transaction([
|
|
946
|
+
this.prisma.course_class_group.findMany({
|
|
947
|
+
where,
|
|
948
|
+
orderBy: { start_date: 'desc' },
|
|
949
|
+
include: {
|
|
950
|
+
course: {
|
|
951
|
+
select: {
|
|
952
|
+
id: true,
|
|
953
|
+
title: true,
|
|
954
|
+
course_image: {
|
|
955
|
+
where: {
|
|
956
|
+
image_type: { slug: { in: ['course-logo', 'course-banner'] } },
|
|
957
|
+
},
|
|
958
|
+
orderBy: { is_primary: 'desc' },
|
|
959
|
+
select: {
|
|
960
|
+
file: { select: { id: true, location: true } },
|
|
961
|
+
image_type: { select: { slug: true } },
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
_count: {
|
|
967
|
+
select: {
|
|
968
|
+
course_enrollment: { where: { status: { not: 'cancelled' } } },
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
},
|
|
972
|
+
skip: (page - 1) * pageSize,
|
|
973
|
+
take: pageSize,
|
|
974
|
+
}),
|
|
975
|
+
this.prisma.course_class_group.count({ where }),
|
|
976
|
+
]);
|
|
977
|
+
|
|
978
|
+
const data = classGroups.map((cg) => {
|
|
979
|
+
const preferredImage =
|
|
980
|
+
cg.course?.course_image.find(
|
|
981
|
+
(ci) => ci.image_type?.slug === 'course-logo',
|
|
982
|
+
) ?? cg.course?.course_image[0];
|
|
983
|
+
|
|
984
|
+
return {
|
|
985
|
+
id: cg.id,
|
|
986
|
+
name: cg.title,
|
|
987
|
+
code: cg.code ?? '',
|
|
988
|
+
courseId: cg.course?.id ?? null,
|
|
989
|
+
courseName: cg.course?.title ?? null,
|
|
990
|
+
courseLogoFileId: preferredImage?.file?.id ?? null,
|
|
991
|
+
courseLogoUrl: preferredImage?.file?.location ?? null,
|
|
992
|
+
startDate: cg.start_date,
|
|
993
|
+
endDate: cg.end_date ?? null,
|
|
994
|
+
slots: cg._count.course_enrollment,
|
|
995
|
+
totalSlots: cg.capacity ?? null,
|
|
996
|
+
status: cg.status,
|
|
997
|
+
};
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
return { data, total, page, pageSize, lastPage: Math.ceil(total / pageSize) };
|
|
1001
|
+
}
|
|
927
1002
|
}
|
package/src/lms.module.ts
CHANGED