@hed-hog/lms 0.0.325 → 0.0.327
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.service.d.ts +3 -1
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +35 -5
- package/dist/course/course.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/instructor/dto/create-instructor-skill.dto.d.ts +9 -0
- package/dist/instructor/dto/create-instructor-skill.dto.d.ts.map +1 -0
- package/dist/instructor/dto/create-instructor-skill.dto.js +48 -0
- package/dist/instructor/dto/create-instructor-skill.dto.js.map +1 -0
- package/dist/instructor/dto/create-instructor.dto.d.ts +2 -0
- package/dist/instructor/dto/create-instructor.dto.d.ts.map +1 -1
- package/dist/instructor/dto/create-instructor.dto.js +12 -0
- package/dist/instructor/dto/create-instructor.dto.js.map +1 -1
- package/dist/instructor/dto/update-instructor-skill.dto.d.ts +9 -0
- package/dist/instructor/dto/update-instructor-skill.dto.d.ts.map +1 -0
- package/dist/instructor/dto/update-instructor-skill.dto.js +50 -0
- package/dist/instructor/dto/update-instructor-skill.dto.js.map +1 -0
- package/dist/instructor/dto/update-instructor.dto.d.ts +2 -0
- package/dist/instructor/dto/update-instructor.dto.d.ts.map +1 -1
- package/dist/instructor/dto/update-instructor.dto.js +12 -0
- package/dist/instructor/dto/update-instructor.dto.js.map +1 -1
- package/dist/instructor/instructor-skill.controller.d.ts +38 -0
- package/dist/instructor/instructor-skill.controller.d.ts.map +1 -0
- package/dist/instructor/instructor-skill.controller.js +89 -0
- package/dist/instructor/instructor-skill.controller.js.map +1 -0
- package/dist/instructor/instructor-skill.service.d.ts +48 -0
- package/dist/instructor/instructor-skill.service.d.ts.map +1 -0
- package/dist/instructor/instructor-skill.service.js +203 -0
- package/dist/instructor/instructor-skill.service.js.map +1 -0
- package/dist/instructor/instructor.controller.d.ts +26 -0
- package/dist/instructor/instructor.controller.d.ts.map +1 -1
- package/dist/instructor/instructor.module.d.ts.map +1 -1
- package/dist/instructor/instructor.module.js +4 -2
- package/dist/instructor/instructor.module.js.map +1 -1
- package/dist/instructor/instructor.service.d.ts +35 -0
- package/dist/instructor/instructor.service.d.ts.map +1 -1
- package/dist/instructor/instructor.service.js +132 -11
- package/dist/instructor/instructor.service.js.map +1 -1
- package/dist/training/training.service.d.ts +3 -1
- package/dist/training/training.service.d.ts.map +1 -1
- package/dist/training/training.service.js +34 -4
- package/dist/training/training.service.js.map +1 -1
- package/hedhog/data/integration_event_catalog.yaml +219 -0
- package/hedhog/data/menu.yaml +23 -6
- package/hedhog/data/route.yaml +45 -0
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +1 -1
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +547 -0
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +845 -239
- package/hedhog/frontend/app/instructors/_components/instructor-types.ts.ejs +9 -0
- package/hedhog/frontend/app/instructors/page.tsx.ejs +69 -20
- package/hedhog/table/instructor.yaml +5 -0
- package/hedhog/table/instructor_skill.yaml +26 -0
- package/hedhog/table/instructor_skill_assignment.yaml +22 -0
- package/package.json +6 -6
- package/src/course/course.service.ts +38 -4
- package/src/index.ts +2 -0
- package/src/instructor/dto/create-instructor-skill.dto.ts +28 -0
- package/src/instructor/dto/create-instructor.dto.ts +20 -8
- package/src/instructor/dto/update-instructor-skill.dto.ts +30 -0
- package/src/instructor/dto/update-instructor.dto.ts +18 -6
- package/src/instructor/instructor-skill.controller.ts +60 -0
- package/src/instructor/instructor-skill.service.ts +214 -0
- package/src/instructor/instructor.module.ts +4 -2
- package/src/instructor/instructor.service.ts +148 -0
- package/src/training/training.service.ts +38 -4
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
+
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
3
|
+
import { CreateInstructorSkillDto } from './dto/create-instructor-skill.dto';
|
|
4
|
+
import { UpdateInstructorSkillDto } from './dto/update-instructor-skill.dto';
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class InstructorSkillService {
|
|
8
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
9
|
+
|
|
10
|
+
private get skillClient() {
|
|
11
|
+
return (this.prisma as any).instructor_skill;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private get localeClient() {
|
|
15
|
+
return (this.prisma as any).instructor_skill_locale;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async list(params: { page?: number; pageSize?: number; search?: string }) {
|
|
19
|
+
const page = Math.max(Number(params.page) || 1, 1);
|
|
20
|
+
const pageSize = Math.max(Number(params.pageSize) || 20, 1);
|
|
21
|
+
const skip = (page - 1) * pageSize;
|
|
22
|
+
const search = params.search?.trim();
|
|
23
|
+
|
|
24
|
+
const where: any = {};
|
|
25
|
+
|
|
26
|
+
if (search) {
|
|
27
|
+
where.OR = [
|
|
28
|
+
{ slug: { contains: search, mode: 'insensitive' } },
|
|
29
|
+
{
|
|
30
|
+
instructor_skill_locale: {
|
|
31
|
+
some: { name: { contains: search, mode: 'insensitive' } },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const [rows, total] = await Promise.all([
|
|
38
|
+
this.skillClient.findMany({
|
|
39
|
+
where,
|
|
40
|
+
skip,
|
|
41
|
+
take: pageSize,
|
|
42
|
+
include: {
|
|
43
|
+
instructor_skill_locale: {
|
|
44
|
+
select: { locale_id: true, name: true, description: true },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
orderBy: { slug: 'asc' },
|
|
48
|
+
}),
|
|
49
|
+
this.skillClient.count({ where }),
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
data: (rows as any[]).map((row) => this.mapRow(row)),
|
|
54
|
+
total,
|
|
55
|
+
page,
|
|
56
|
+
pageSize,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getAll() {
|
|
61
|
+
const rows = await this.skillClient.findMany({
|
|
62
|
+
where: { status: 'active' },
|
|
63
|
+
include: {
|
|
64
|
+
instructor_skill_locale: {
|
|
65
|
+
select: { locale_id: true, name: true },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
orderBy: { slug: 'asc' },
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return (rows as any[]).map((row) => this.mapRow(row));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async create(dto: CreateInstructorSkillDto) {
|
|
75
|
+
const skill = await this.skillClient.create({
|
|
76
|
+
data: {
|
|
77
|
+
slug: dto.slug,
|
|
78
|
+
status: dto.status ?? 'active',
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await this.syncLocales(skill.id, dto);
|
|
83
|
+
|
|
84
|
+
return this.getSkillById(skill.id);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async update(id: number, dto: UpdateInstructorSkillDto) {
|
|
88
|
+
const skill = await this.skillClient.findUnique({
|
|
89
|
+
where: { id },
|
|
90
|
+
select: { id: true },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!skill) {
|
|
94
|
+
throw new NotFoundException('Instructor skill not found');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const data: any = {};
|
|
98
|
+
|
|
99
|
+
if (dto.slug !== undefined) data.slug = dto.slug;
|
|
100
|
+
if (dto.status !== undefined) data.status = dto.status;
|
|
101
|
+
|
|
102
|
+
if (Object.keys(data).length > 0) {
|
|
103
|
+
await this.skillClient.update({ where: { id }, data });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
await this.syncLocales(id, dto);
|
|
107
|
+
|
|
108
|
+
return this.getSkillById(id);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async delete(id: number) {
|
|
112
|
+
const skill = await this.skillClient.findUnique({
|
|
113
|
+
where: { id },
|
|
114
|
+
select: { id: true },
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!skill) {
|
|
118
|
+
throw new NotFoundException('Instructor skill not found');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await this.skillClient.delete({ where: { id } });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async getSkillById(id: number) {
|
|
125
|
+
const row = await this.skillClient.findUnique({
|
|
126
|
+
where: { id },
|
|
127
|
+
include: {
|
|
128
|
+
instructor_skill_locale: {
|
|
129
|
+
select: { locale_id: true, name: true, description: true },
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!row) {
|
|
135
|
+
throw new NotFoundException('Instructor skill not found');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return this.mapRow(row);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private mapRow(row: any) {
|
|
142
|
+
return {
|
|
143
|
+
id: row.id,
|
|
144
|
+
slug: row.slug,
|
|
145
|
+
status: row.status,
|
|
146
|
+
locales: row.instructor_skill_locale ?? [],
|
|
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,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { PrismaModule } from '@hed-hog/api-prisma';
|
|
2
2
|
import { forwardRef, Module } from '@nestjs/common';
|
|
3
|
+
import { InstructorSkillController } from './instructor-skill.controller';
|
|
4
|
+
import { InstructorSkillService } from './instructor-skill.service';
|
|
3
5
|
import { InstructorController } from './instructor.controller';
|
|
4
6
|
import { InstructorService } from './instructor.service';
|
|
5
7
|
|
|
6
8
|
@Module({
|
|
7
9
|
imports: [forwardRef(() => PrismaModule)],
|
|
8
|
-
controllers: [InstructorController],
|
|
9
|
-
providers: [InstructorService],
|
|
10
|
+
controllers: [InstructorController, InstructorSkillController],
|
|
11
|
+
providers: [InstructorService, InstructorSkillService],
|
|
10
12
|
exports: [forwardRef(() => InstructorService)],
|
|
11
13
|
})
|
|
12
14
|
export class InstructorModule {}
|
|
@@ -85,6 +85,17 @@ export class InstructorService {
|
|
|
85
85
|
id: true,
|
|
86
86
|
name: true,
|
|
87
87
|
avatar_id: true,
|
|
88
|
+
contact: {
|
|
89
|
+
where: {
|
|
90
|
+
contact_type: {
|
|
91
|
+
code: { in: ['EMAIL', 'PHONE', 'MOBILE', 'WHATSAPP'] },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
include: {
|
|
95
|
+
contact_type: { select: { code: true } },
|
|
96
|
+
},
|
|
97
|
+
orderBy: [{ is_primary: 'desc' as const }, { id: 'asc' as const }],
|
|
98
|
+
},
|
|
88
99
|
person_user: {
|
|
89
100
|
take: 1,
|
|
90
101
|
select: {
|
|
@@ -118,6 +129,48 @@ export class InstructorService {
|
|
|
118
129
|
this.prisma.instructor.count({ where }),
|
|
119
130
|
]);
|
|
120
131
|
|
|
132
|
+
const instructorIds = rows.map((r) => r.id);
|
|
133
|
+
let hourlyRateMap = new Map<number, number | null>();
|
|
134
|
+
let skillsMap = new Map<number, Array<{ id: number; slug: string; name: string }>>();
|
|
135
|
+
|
|
136
|
+
if (instructorIds.length > 0) {
|
|
137
|
+
const [rateRows, assignmentRows] = (await Promise.all([
|
|
138
|
+
this.prisma.$queryRaw(
|
|
139
|
+
Prisma.sql`SELECT id, hourly_rate FROM instructor WHERE id IN (${Prisma.join(instructorIds)})`,
|
|
140
|
+
),
|
|
141
|
+
(this.prisma as any).instructor_skill_assignment.findMany({
|
|
142
|
+
where: { instructor_id: { in: instructorIds } },
|
|
143
|
+
select: {
|
|
144
|
+
instructor_id: true,
|
|
145
|
+
instructor_skill: {
|
|
146
|
+
select: {
|
|
147
|
+
id: true,
|
|
148
|
+
slug: true,
|
|
149
|
+
instructor_skill_locale: {
|
|
150
|
+
take: 1,
|
|
151
|
+
select: { name: true },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
])) as [Array<{ id: number; hourly_rate: number | null }>, any[]];
|
|
158
|
+
|
|
159
|
+
for (const r of rateRows) {
|
|
160
|
+
hourlyRateMap.set(Number(r.id), r.hourly_rate !== null ? Number(r.hourly_rate) : null);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const a of assignmentRows) {
|
|
164
|
+
const existing = skillsMap.get(a.instructor_id) ?? [];
|
|
165
|
+
existing.push({
|
|
166
|
+
id: a.instructor_skill.id,
|
|
167
|
+
slug: a.instructor_skill.slug,
|
|
168
|
+
name: a.instructor_skill.instructor_skill_locale?.[0]?.name ?? a.instructor_skill.slug,
|
|
169
|
+
});
|
|
170
|
+
skillsMap.set(a.instructor_id, existing);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
121
174
|
return {
|
|
122
175
|
data: rows
|
|
123
176
|
.map((row) => {
|
|
@@ -127,12 +180,22 @@ export class InstructorService {
|
|
|
127
180
|
personId: row.person_id,
|
|
128
181
|
name: row.person?.name?.trim() || `Instructor #${row.id}`,
|
|
129
182
|
avatarId: row.person?.avatar_id ?? null,
|
|
183
|
+
email: this.getPrimaryContactValue(
|
|
184
|
+
row.person?.contact ?? [],
|
|
185
|
+
['EMAIL'],
|
|
186
|
+
),
|
|
187
|
+
phone: this.getPrimaryContactValue(
|
|
188
|
+
row.person?.contact ?? [],
|
|
189
|
+
['PHONE', 'MOBILE', 'WHATSAPP'],
|
|
190
|
+
),
|
|
130
191
|
userId: personUser?.user_id ?? null,
|
|
131
192
|
hasTrainingAccess: (personUser?.user?.role_user?.length ?? 0) > 0,
|
|
132
193
|
status: row.status,
|
|
194
|
+
hourlyRate: hourlyRateMap.get(row.id) ?? null,
|
|
133
195
|
qualificationSlugs: [...new Set(row.instructor_qualification_assignment
|
|
134
196
|
.map((assignment) => assignment.instructor_qualification.slug)
|
|
135
197
|
.sort())],
|
|
198
|
+
skills: skillsMap.get(row.id) ?? [],
|
|
136
199
|
};
|
|
137
200
|
})
|
|
138
201
|
.sort((left, right) => left.name.localeCompare(right.name)),
|
|
@@ -217,6 +280,12 @@ export class InstructorService {
|
|
|
217
280
|
dto.qualificationSlugs,
|
|
218
281
|
);
|
|
219
282
|
|
|
283
|
+
if (dto.hourlyRate !== undefined) {
|
|
284
|
+
await tx.$executeRaw`UPDATE instructor SET hourly_rate = ${dto.hourlyRate ?? null} WHERE id = ${instructor.id}`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await this.syncInstructorSkills(tx, instructor.id, dto.skillSlugs ?? []);
|
|
288
|
+
|
|
220
289
|
return this.getById(instructor.id, tx);
|
|
221
290
|
});
|
|
222
291
|
}
|
|
@@ -284,6 +353,14 @@ export class InstructorService {
|
|
|
284
353
|
);
|
|
285
354
|
}
|
|
286
355
|
|
|
356
|
+
if (dto.hourlyRate !== undefined) {
|
|
357
|
+
await tx.$executeRaw`UPDATE instructor SET hourly_rate = ${dto.hourlyRate ?? null} WHERE id = ${id}`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (dto.skillSlugs !== undefined) {
|
|
361
|
+
await this.syncInstructorSkills(tx, id, dto.skillSlugs);
|
|
362
|
+
}
|
|
363
|
+
|
|
287
364
|
return this.getById(id, tx);
|
|
288
365
|
});
|
|
289
366
|
}
|
|
@@ -379,6 +456,36 @@ export class InstructorService {
|
|
|
379
456
|
|
|
380
457
|
const personUser = instructor.person.person_user?.[0] ?? null;
|
|
381
458
|
|
|
459
|
+
const [rateRows, skillAssignments] = (await Promise.all([
|
|
460
|
+
(db as any).$queryRaw`SELECT id, hourly_rate FROM instructor WHERE id = ${id}`,
|
|
461
|
+
(db as any).instructor_skill_assignment.findMany({
|
|
462
|
+
where: { instructor_id: id },
|
|
463
|
+
select: {
|
|
464
|
+
instructor_skill: {
|
|
465
|
+
select: {
|
|
466
|
+
id: true,
|
|
467
|
+
slug: true,
|
|
468
|
+
instructor_skill_locale: {
|
|
469
|
+
take: 1,
|
|
470
|
+
select: { name: true },
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
}),
|
|
476
|
+
])) as [Array<{ id: number; hourly_rate: number | null }>, any[]];
|
|
477
|
+
|
|
478
|
+
const hourlyRate =
|
|
479
|
+
rateRows[0]?.hourly_rate !== null && rateRows[0]?.hourly_rate !== undefined
|
|
480
|
+
? Number(rateRows[0].hourly_rate)
|
|
481
|
+
: null;
|
|
482
|
+
|
|
483
|
+
const skills = skillAssignments.map((a) => ({
|
|
484
|
+
id: a.instructor_skill.id,
|
|
485
|
+
slug: a.instructor_skill.slug,
|
|
486
|
+
name: a.instructor_skill.instructor_skill_locale?.[0]?.name ?? a.instructor_skill.slug,
|
|
487
|
+
}));
|
|
488
|
+
|
|
382
489
|
return {
|
|
383
490
|
id: instructor.id,
|
|
384
491
|
personId: instructor.person.id,
|
|
@@ -393,9 +500,11 @@ export class InstructorService {
|
|
|
393
500
|
'WHATSAPP',
|
|
394
501
|
]),
|
|
395
502
|
status: instructor.status,
|
|
503
|
+
hourlyRate,
|
|
396
504
|
qualificationSlugs: [...new Set(instructor.instructor_qualification_assignment
|
|
397
505
|
.map((assignment) => assignment.instructor_qualification.slug)
|
|
398
506
|
.sort())],
|
|
507
|
+
skills,
|
|
399
508
|
};
|
|
400
509
|
}
|
|
401
510
|
|
|
@@ -773,4 +882,43 @@ export class InstructorService {
|
|
|
773
882
|
},
|
|
774
883
|
});
|
|
775
884
|
}
|
|
885
|
+
|
|
886
|
+
private async syncInstructorSkills(
|
|
887
|
+
db: DbClient,
|
|
888
|
+
instructorId: number,
|
|
889
|
+
slugs: string[],
|
|
890
|
+
) {
|
|
891
|
+
const normalizedSlugs = [...new Set((slugs ?? []).map((s) => s?.trim()).filter(Boolean))];
|
|
892
|
+
|
|
893
|
+
if (normalizedSlugs.length === 0) {
|
|
894
|
+
await (db as any).instructor_skill_assignment.deleteMany({
|
|
895
|
+
where: { instructor_id: instructorId },
|
|
896
|
+
});
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const skillRows = await (db as any).instructor_skill.findMany({
|
|
901
|
+
where: { slug: { in: normalizedSlugs } },
|
|
902
|
+
select: { id: true },
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
const skillIds = (skillRows as any[]).map((r) => r.id);
|
|
906
|
+
|
|
907
|
+
await (db as any).instructor_skill_assignment.deleteMany({
|
|
908
|
+
where: {
|
|
909
|
+
instructor_id: instructorId,
|
|
910
|
+
skill_id: { notIn: skillIds },
|
|
911
|
+
},
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
if (skillIds.length > 0) {
|
|
915
|
+
await (db as any).instructor_skill_assignment.createMany({
|
|
916
|
+
data: skillIds.map((skillId: number) => ({
|
|
917
|
+
instructor_id: instructorId,
|
|
918
|
+
skill_id: skillId,
|
|
919
|
+
})),
|
|
920
|
+
skipDuplicates: true,
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
}
|
|
776
924
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
-
import {
|
|
2
|
+
import { IntegrationDeveloperApiService } from '@hed-hog/core';
|
|
3
|
+
import { Inject, Injectable, forwardRef } from '@nestjs/common';
|
|
3
4
|
import { CreateTrainingDto, LearningPathItemDto } from './dto/create-training.dto';
|
|
4
5
|
import { UpdateTrainingDto } from './dto/update-training.dto';
|
|
5
6
|
|
|
6
7
|
@Injectable()
|
|
7
8
|
export class TrainingService {
|
|
8
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
private readonly prisma: PrismaService,
|
|
11
|
+
@Inject(forwardRef(() => IntegrationDeveloperApiService))
|
|
12
|
+
private readonly integrationApi: IntegrationDeveloperApiService,
|
|
13
|
+
) {}
|
|
9
14
|
|
|
10
15
|
async list(params: {
|
|
11
16
|
page?: number;
|
|
@@ -237,7 +242,17 @@ export class TrainingService {
|
|
|
237
242
|
},
|
|
238
243
|
});
|
|
239
244
|
|
|
240
|
-
|
|
245
|
+
const trainingResult = this.mapTraining(created);
|
|
246
|
+
|
|
247
|
+
await this.integrationApi.publishEvent({
|
|
248
|
+
eventName: 'lms.training.created',
|
|
249
|
+
sourceModule: 'lms',
|
|
250
|
+
aggregateType: 'training',
|
|
251
|
+
aggregateId: String(created.id),
|
|
252
|
+
payload: { id: created.id, title: dto.title, slug, status: normalizedStatus },
|
|
253
|
+
}).catch(() => null);
|
|
254
|
+
|
|
255
|
+
return trainingResult;
|
|
241
256
|
}
|
|
242
257
|
|
|
243
258
|
async update(id: number, dto: UpdateTrainingDto) {
|
|
@@ -314,11 +329,30 @@ export class TrainingService {
|
|
|
314
329
|
},
|
|
315
330
|
});
|
|
316
331
|
|
|
317
|
-
|
|
332
|
+
const updateTrainingResult = this.mapTraining(updated);
|
|
333
|
+
|
|
334
|
+
await this.integrationApi.publishEvent({
|
|
335
|
+
eventName: 'lms.training.updated',
|
|
336
|
+
sourceModule: 'lms',
|
|
337
|
+
aggregateType: 'training',
|
|
338
|
+
aggregateId: String(id),
|
|
339
|
+
payload: { id, title: dto.title, status: dto.status },
|
|
340
|
+
}).catch(() => null);
|
|
341
|
+
|
|
342
|
+
return updateTrainingResult;
|
|
318
343
|
}
|
|
319
344
|
|
|
320
345
|
async remove(id: number) {
|
|
321
346
|
await this.prisma.learning_path.delete({ where: { id } });
|
|
347
|
+
|
|
348
|
+
await this.integrationApi.publishEvent({
|
|
349
|
+
eventName: 'lms.training.deleted',
|
|
350
|
+
sourceModule: 'lms',
|
|
351
|
+
aggregateType: 'training',
|
|
352
|
+
aggregateId: String(id),
|
|
353
|
+
payload: { id },
|
|
354
|
+
}).catch(() => null);
|
|
355
|
+
|
|
322
356
|
return { success: true };
|
|
323
357
|
}
|
|
324
358
|
|