@hed-hog/lms 0.0.305 → 0.0.306
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.js +4 -4
- package/hedhog/data/dashboard_component.yaml +152 -152
- package/hedhog/data/dashboard_item.yaml +166 -166
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +317 -317
- package/hedhog/frontend/app/enterprise/_components/enterprise-class-create-sheet.tsx.ejs +1 -0
- package/hedhog/frontend/app/enterprise/_components/enterprise-mocks.ts.ejs +12 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-related-tab.tsx.ejs +44 -7
- package/hedhog/frontend/app/enterprise/_components/enterprise-types.ts.ejs +96 -96
- package/hedhog/frontend/app/page.tsx.ejs +5 -5
- package/hedhog/table/course.yaml +15 -15
- package/package.json +7 -7
- package/src/class-group/class-group.service.ts +413 -413
- package/src/class-group/dto/create-class-group.dto.ts +77 -77
- package/src/course/course.service.ts +165 -165
- package/src/course/dto/create-course.dto.ts +15 -15
- package/dist/enterprise/dto/add-enterprise-lead.dto.d.ts +0 -4
- package/dist/enterprise/dto/add-enterprise-lead.dto.d.ts.map +0 -1
- package/dist/enterprise/dto/add-enterprise-lead.dto.js +0 -22
- package/dist/enterprise/dto/add-enterprise-lead.dto.js.map +0 -1
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IsArray,
|
|
3
|
-
IsEnum,
|
|
4
|
-
IsInt,
|
|
5
|
-
IsISO8601,
|
|
6
|
-
IsNotEmpty,
|
|
7
|
-
IsOptional,
|
|
1
|
+
import {
|
|
2
|
+
IsArray,
|
|
3
|
+
IsEnum,
|
|
4
|
+
IsInt,
|
|
5
|
+
IsISO8601,
|
|
6
|
+
IsNotEmpty,
|
|
7
|
+
IsOptional,
|
|
8
8
|
IsString,
|
|
9
9
|
IsUrl,
|
|
10
|
-
Matches,
|
|
11
|
-
MaxLength,
|
|
12
|
-
Min,
|
|
13
|
-
ValidateNested,
|
|
14
|
-
} from 'class-validator';
|
|
15
|
-
import { Type } from 'class-transformer';
|
|
16
|
-
import {
|
|
17
|
-
SESSION_RECURRENCE_DAYS,
|
|
18
|
-
SESSION_RECURRENCE_FREQUENCIES,
|
|
19
|
-
} from './create-session.dto';
|
|
20
|
-
|
|
21
|
-
export class ClassGroupSessionRecurrenceDto {
|
|
22
|
-
@IsEnum(SESSION_RECURRENCE_FREQUENCIES)
|
|
23
|
-
frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
|
|
24
|
-
|
|
25
|
-
@Type(() => Number)
|
|
26
|
-
@IsInt()
|
|
27
|
-
@Min(1)
|
|
28
|
-
@IsOptional()
|
|
29
|
-
interval?: number;
|
|
30
|
-
|
|
31
|
-
@IsISO8601()
|
|
32
|
-
until: string;
|
|
33
|
-
|
|
34
|
-
@IsArray()
|
|
35
|
-
@IsEnum(SESSION_RECURRENCE_DAYS, { each: true })
|
|
36
|
-
@IsOptional()
|
|
37
|
-
daysOfWeek?: Array<'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class ClassGroupSessionTemplateDto {
|
|
41
|
-
@IsString()
|
|
42
|
-
@IsNotEmpty()
|
|
43
|
-
@MaxLength(255)
|
|
44
|
-
title: string;
|
|
45
|
-
|
|
46
|
-
@IsString()
|
|
47
|
-
@IsOptional()
|
|
48
|
-
description?: string;
|
|
49
|
-
|
|
50
|
-
@IsString()
|
|
51
|
-
@IsOptional()
|
|
52
|
-
location?: string;
|
|
53
|
-
|
|
54
|
-
@IsString()
|
|
55
|
-
@IsOptional()
|
|
56
|
-
@MaxLength(500)
|
|
57
|
-
meetingUrl?: string;
|
|
58
|
-
|
|
59
|
-
@IsString()
|
|
60
|
-
@IsOptional()
|
|
61
|
-
@Matches(/^#[0-9a-fA-F]{6}$/)
|
|
62
|
-
color?: string;
|
|
63
|
-
|
|
64
|
-
@ValidateNested()
|
|
65
|
-
@Type(() => ClassGroupSessionRecurrenceDto)
|
|
66
|
-
@IsOptional()
|
|
67
|
-
recurrence?: ClassGroupSessionRecurrenceDto;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export class CreateClassGroupDto {
|
|
10
|
+
Matches,
|
|
11
|
+
MaxLength,
|
|
12
|
+
Min,
|
|
13
|
+
ValidateNested,
|
|
14
|
+
} from 'class-validator';
|
|
15
|
+
import { Type } from 'class-transformer';
|
|
16
|
+
import {
|
|
17
|
+
SESSION_RECURRENCE_DAYS,
|
|
18
|
+
SESSION_RECURRENCE_FREQUENCIES,
|
|
19
|
+
} from './create-session.dto';
|
|
20
|
+
|
|
21
|
+
export class ClassGroupSessionRecurrenceDto {
|
|
22
|
+
@IsEnum(SESSION_RECURRENCE_FREQUENCIES)
|
|
23
|
+
frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
|
|
24
|
+
|
|
25
|
+
@Type(() => Number)
|
|
26
|
+
@IsInt()
|
|
27
|
+
@Min(1)
|
|
28
|
+
@IsOptional()
|
|
29
|
+
interval?: number;
|
|
30
|
+
|
|
31
|
+
@IsISO8601()
|
|
32
|
+
until: string;
|
|
33
|
+
|
|
34
|
+
@IsArray()
|
|
35
|
+
@IsEnum(SESSION_RECURRENCE_DAYS, { each: true })
|
|
36
|
+
@IsOptional()
|
|
37
|
+
daysOfWeek?: Array<'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class ClassGroupSessionTemplateDto {
|
|
41
|
+
@IsString()
|
|
42
|
+
@IsNotEmpty()
|
|
43
|
+
@MaxLength(255)
|
|
44
|
+
title: string;
|
|
45
|
+
|
|
46
|
+
@IsString()
|
|
47
|
+
@IsOptional()
|
|
48
|
+
description?: string;
|
|
49
|
+
|
|
50
|
+
@IsString()
|
|
51
|
+
@IsOptional()
|
|
52
|
+
location?: string;
|
|
53
|
+
|
|
54
|
+
@IsString()
|
|
55
|
+
@IsOptional()
|
|
56
|
+
@MaxLength(500)
|
|
57
|
+
meetingUrl?: string;
|
|
58
|
+
|
|
59
|
+
@IsString()
|
|
60
|
+
@IsOptional()
|
|
61
|
+
@Matches(/^#[0-9a-fA-F]{6}$/)
|
|
62
|
+
color?: string;
|
|
63
|
+
|
|
64
|
+
@ValidateNested()
|
|
65
|
+
@Type(() => ClassGroupSessionRecurrenceDto)
|
|
66
|
+
@IsOptional()
|
|
67
|
+
recurrence?: ClassGroupSessionRecurrenceDto;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class CreateClassGroupDto {
|
|
71
71
|
@IsString()
|
|
72
72
|
@IsNotEmpty()
|
|
73
73
|
@MaxLength(50)
|
|
@@ -128,12 +128,12 @@ export class CreateClassGroupDto {
|
|
|
128
128
|
@IsOptional()
|
|
129
129
|
location?: string;
|
|
130
130
|
|
|
131
|
-
@IsUrl({ require_tld: false })
|
|
132
|
-
@IsOptional()
|
|
133
|
-
virtualRoomUrl?: string;
|
|
134
|
-
|
|
135
|
-
@ValidateNested()
|
|
136
|
-
@Type(() => ClassGroupSessionTemplateDto)
|
|
137
|
-
@IsOptional()
|
|
138
|
-
sessionTemplate?: ClassGroupSessionTemplateDto;
|
|
139
|
-
}
|
|
131
|
+
@IsUrl({ require_tld: false })
|
|
132
|
+
@IsOptional()
|
|
133
|
+
virtualRoomUrl?: string;
|
|
134
|
+
|
|
135
|
+
@ValidateNested()
|
|
136
|
+
@Type(() => ClassGroupSessionTemplateDto)
|
|
137
|
+
@IsOptional()
|
|
138
|
+
sessionTemplate?: ClassGroupSessionTemplateDto;
|
|
139
|
+
}
|
|
@@ -5,21 +5,21 @@ import { UpdateCourseDto } from './dto/update-course.dto';
|
|
|
5
5
|
|
|
6
6
|
type CourseImageTypeSlug = 'course-logo' | 'course-banner';
|
|
7
7
|
|
|
8
|
-
type CourseExtraFields = {
|
|
9
|
-
code?: string | null;
|
|
10
|
-
is_featured?: boolean | null;
|
|
11
|
-
has_certificate?: boolean | null;
|
|
12
|
-
is_listed?: boolean | null;
|
|
13
|
-
offering_type?: 'scheduled' | 'on_demand' | 'blended' | null;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type PersistCourseExtrasInput = {
|
|
17
|
-
code?: string;
|
|
18
|
-
isFeatured?: boolean;
|
|
19
|
-
hasCertificate?: boolean;
|
|
20
|
-
isListed?: boolean;
|
|
21
|
-
offeringType?: 'scheduled' | 'on_demand' | 'blended';
|
|
22
|
-
};
|
|
8
|
+
type CourseExtraFields = {
|
|
9
|
+
code?: string | null;
|
|
10
|
+
is_featured?: boolean | null;
|
|
11
|
+
has_certificate?: boolean | null;
|
|
12
|
+
is_listed?: boolean | null;
|
|
13
|
+
offering_type?: 'scheduled' | 'on_demand' | 'blended' | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type PersistCourseExtrasInput = {
|
|
17
|
+
code?: string;
|
|
18
|
+
isFeatured?: boolean;
|
|
19
|
+
hasCertificate?: boolean;
|
|
20
|
+
isListed?: boolean;
|
|
21
|
+
offeringType?: 'scheduled' | 'on_demand' | 'blended';
|
|
22
|
+
};
|
|
23
23
|
|
|
24
24
|
@Injectable()
|
|
25
25
|
export class CourseService {
|
|
@@ -40,9 +40,9 @@ export class CourseService {
|
|
|
40
40
|
return undefined;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
private normalizeStatus(value?: string | null) {
|
|
44
|
-
if (!value) return undefined;
|
|
45
|
-
const normalized = String(value).trim().toLowerCase();
|
|
43
|
+
private normalizeStatus(value?: string | null) {
|
|
44
|
+
if (!value) return undefined;
|
|
45
|
+
const normalized = String(value).trim().toLowerCase();
|
|
46
46
|
if (
|
|
47
47
|
normalized === 'published' ||
|
|
48
48
|
normalized === 'active' ||
|
|
@@ -55,39 +55,39 @@ export class CourseService {
|
|
|
55
55
|
}
|
|
56
56
|
if (normalized === 'archived' || normalized === 'arquivado') {
|
|
57
57
|
return 'archived';
|
|
58
|
-
}
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private normalizeOfferingType(value?: string | null) {
|
|
63
|
-
if (!value) return undefined;
|
|
64
|
-
|
|
65
|
-
const normalized = String(value).trim().toLowerCase();
|
|
66
|
-
if (normalized === 'scheduled') {
|
|
67
|
-
return 'scheduled';
|
|
68
|
-
}
|
|
69
|
-
if (
|
|
70
|
-
normalized === 'on_demand' ||
|
|
71
|
-
normalized === 'ondemand' ||
|
|
72
|
-
normalized === 'on-demand'
|
|
73
|
-
) {
|
|
74
|
-
return 'on_demand';
|
|
75
|
-
}
|
|
76
|
-
if (normalized === 'blended') {
|
|
77
|
-
return 'blended';
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private normalizeOptionalText(value?: string | null) {
|
|
84
|
-
const normalized = value?.trim();
|
|
85
|
-
return normalized ? normalized : undefined;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private resolveCourseTitle(title: string | undefined, slug: string) {
|
|
89
|
-
return this.normalizeOptionalText(title) ?? slug;
|
|
90
|
-
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private normalizeOfferingType(value?: string | null) {
|
|
63
|
+
if (!value) return undefined;
|
|
64
|
+
|
|
65
|
+
const normalized = String(value).trim().toLowerCase();
|
|
66
|
+
if (normalized === 'scheduled') {
|
|
67
|
+
return 'scheduled';
|
|
68
|
+
}
|
|
69
|
+
if (
|
|
70
|
+
normalized === 'on_demand' ||
|
|
71
|
+
normalized === 'ondemand' ||
|
|
72
|
+
normalized === 'on-demand'
|
|
73
|
+
) {
|
|
74
|
+
return 'on_demand';
|
|
75
|
+
}
|
|
76
|
+
if (normalized === 'blended') {
|
|
77
|
+
return 'blended';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private normalizeOptionalText(value?: string | null) {
|
|
84
|
+
const normalized = value?.trim();
|
|
85
|
+
return normalized ? normalized : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private resolveCourseTitle(title: string | undefined, slug: string) {
|
|
89
|
+
return this.normalizeOptionalText(title) ?? slug;
|
|
90
|
+
}
|
|
91
91
|
|
|
92
92
|
async list(params: {
|
|
93
93
|
page?: number;
|
|
@@ -154,11 +154,11 @@ export class CourseService {
|
|
|
154
154
|
},
|
|
155
155
|
orderBy: [{ is_primary: 'desc' }, { order: 'asc' }],
|
|
156
156
|
},
|
|
157
|
-
_count: {
|
|
158
|
-
select: { course_enrollment: true, course_class_group: true },
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
}),
|
|
157
|
+
_count: {
|
|
158
|
+
select: { course_enrollment: true, course_class_group: true },
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
162
|
this.prisma.course.count({ where }),
|
|
163
163
|
]);
|
|
164
164
|
|
|
@@ -306,14 +306,14 @@ export class CourseService {
|
|
|
306
306
|
}, extrasById.get(id));
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
async create(dto: CreateCourseDto) {
|
|
310
|
-
const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
|
|
311
|
-
const normalizedSlug = data.slug.trim();
|
|
312
|
-
const resolvedCode = this.normalizeCourseCode(data.code ?? normalizedSlug);
|
|
313
|
-
const resolvedTitle = this.resolveCourseTitle(data.title, normalizedSlug);
|
|
314
|
-
|
|
315
|
-
const categories = categorySlugs?.length
|
|
316
|
-
? await this.prisma.category.findMany({
|
|
309
|
+
async create(dto: CreateCourseDto) {
|
|
310
|
+
const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
|
|
311
|
+
const normalizedSlug = data.slug.trim();
|
|
312
|
+
const resolvedCode = this.normalizeCourseCode(data.code ?? normalizedSlug);
|
|
313
|
+
const resolvedTitle = this.resolveCourseTitle(data.title, normalizedSlug);
|
|
314
|
+
|
|
315
|
+
const categories = categorySlugs?.length
|
|
316
|
+
? await this.prisma.category.findMany({
|
|
317
317
|
where: { slug: { in: categorySlugs } },
|
|
318
318
|
select: { id: true },
|
|
319
319
|
})
|
|
@@ -330,15 +330,15 @@ export class CourseService {
|
|
|
330
330
|
validInstructorIds = instructorIds.filter((id) => validIds.has(id));
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
const c = await this.prisma.course.create({
|
|
334
|
-
data: {
|
|
335
|
-
slug: normalizedSlug,
|
|
336
|
-
title: resolvedTitle,
|
|
337
|
-
...(data.description !== undefined && {
|
|
338
|
-
description: data.description,
|
|
339
|
-
}),
|
|
340
|
-
level: this.normalizeLevel(data.level) ?? 'beginner',
|
|
341
|
-
status: this.normalizeStatus(data.status) ?? 'draft',
|
|
333
|
+
const c = await this.prisma.course.create({
|
|
334
|
+
data: {
|
|
335
|
+
slug: normalizedSlug,
|
|
336
|
+
title: resolvedTitle,
|
|
337
|
+
...(data.description !== undefined && {
|
|
338
|
+
description: data.description,
|
|
339
|
+
}),
|
|
340
|
+
level: this.normalizeLevel(data.level) ?? 'beginner',
|
|
341
|
+
status: this.normalizeStatus(data.status) ?? 'draft',
|
|
342
342
|
...(data.requirements !== undefined && {
|
|
343
343
|
requirements: data.requirements,
|
|
344
344
|
}),
|
|
@@ -425,42 +425,42 @@ export class CourseService {
|
|
|
425
425
|
bannerFileId,
|
|
426
426
|
});
|
|
427
427
|
|
|
428
|
-
await this.persistCourseExtras(c.id, {
|
|
429
|
-
...data,
|
|
430
|
-
...(resolvedCode && { code: resolvedCode }),
|
|
431
|
-
offeringType: this.normalizeOfferingType(data.offeringType) ?? 'on_demand',
|
|
432
|
-
});
|
|
428
|
+
await this.persistCourseExtras(c.id, {
|
|
429
|
+
...data,
|
|
430
|
+
...(resolvedCode && { code: resolvedCode }),
|
|
431
|
+
offeringType: this.normalizeOfferingType(data.offeringType) ?? 'on_demand',
|
|
432
|
+
});
|
|
433
433
|
|
|
434
434
|
const extrasById = await this.getCourseExtras([c.id]);
|
|
435
435
|
|
|
436
436
|
return this.mapCourse(c, undefined, extrasById.get(c.id));
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
async update(id: number, dto: UpdateCourseDto) {
|
|
440
|
-
const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
|
|
441
|
-
let existingSlug: string | undefined;
|
|
442
|
-
|
|
443
|
-
if (
|
|
444
|
-
data.title !== undefined &&
|
|
445
|
-
this.normalizeOptionalText(data.title) === undefined &&
|
|
446
|
-
data.slug === undefined
|
|
447
|
-
) {
|
|
448
|
-
const currentCourse = await this.prisma.course.findUnique({
|
|
449
|
-
where: { id },
|
|
450
|
-
select: { slug: true },
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
existingSlug = currentCourse?.slug;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const resolvedSlug = data.slug !== undefined ? data.slug.trim() : undefined;
|
|
457
|
-
const resolvedTitle =
|
|
458
|
-
data.title !== undefined
|
|
459
|
-
? this.resolveCourseTitle(data.title, resolvedSlug ?? existingSlug ?? '')
|
|
460
|
-
: undefined;
|
|
461
|
-
|
|
462
|
-
const categories = categorySlugs?.length
|
|
463
|
-
? await this.prisma.category.findMany({
|
|
439
|
+
async update(id: number, dto: UpdateCourseDto) {
|
|
440
|
+
const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
|
|
441
|
+
let existingSlug: string | undefined;
|
|
442
|
+
|
|
443
|
+
if (
|
|
444
|
+
data.title !== undefined &&
|
|
445
|
+
this.normalizeOptionalText(data.title) === undefined &&
|
|
446
|
+
data.slug === undefined
|
|
447
|
+
) {
|
|
448
|
+
const currentCourse = await this.prisma.course.findUnique({
|
|
449
|
+
where: { id },
|
|
450
|
+
select: { slug: true },
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
existingSlug = currentCourse?.slug;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const resolvedSlug = data.slug !== undefined ? data.slug.trim() : undefined;
|
|
457
|
+
const resolvedTitle =
|
|
458
|
+
data.title !== undefined
|
|
459
|
+
? this.resolveCourseTitle(data.title, resolvedSlug ?? existingSlug ?? '')
|
|
460
|
+
: undefined;
|
|
461
|
+
|
|
462
|
+
const categories = categorySlugs?.length
|
|
463
|
+
? await this.prisma.category.findMany({
|
|
464
464
|
where: { slug: { in: categorySlugs } },
|
|
465
465
|
select: { id: true },
|
|
466
466
|
})
|
|
@@ -487,14 +487,14 @@ export class CourseService {
|
|
|
487
487
|
});
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
const c = await this.prisma.course.update({
|
|
491
|
-
where: { id },
|
|
492
|
-
data: {
|
|
493
|
-
...(resolvedSlug !== undefined && { slug: resolvedSlug }),
|
|
494
|
-
...(resolvedTitle !== undefined && { title: resolvedTitle }),
|
|
495
|
-
...(data.description !== undefined && { description: data.description }),
|
|
496
|
-
...(data.level !== undefined && {
|
|
497
|
-
level: this.normalizeLevel(data.level),
|
|
490
|
+
const c = await this.prisma.course.update({
|
|
491
|
+
where: { id },
|
|
492
|
+
data: {
|
|
493
|
+
...(resolvedSlug !== undefined && { slug: resolvedSlug }),
|
|
494
|
+
...(resolvedTitle !== undefined && { title: resolvedTitle }),
|
|
495
|
+
...(data.description !== undefined && { description: data.description }),
|
|
496
|
+
...(data.level !== undefined && {
|
|
497
|
+
level: this.normalizeLevel(data.level),
|
|
498
498
|
}),
|
|
499
499
|
...(data.status !== undefined && {
|
|
500
500
|
status: this.normalizeStatus(data.status),
|
|
@@ -594,7 +594,7 @@ export class CourseService {
|
|
|
594
594
|
bannerFileId,
|
|
595
595
|
});
|
|
596
596
|
|
|
597
|
-
await this.persistCourseExtras(id, data);
|
|
597
|
+
await this.persistCourseExtras(id, data);
|
|
598
598
|
|
|
599
599
|
const extrasById = await this.getCourseExtras([id]);
|
|
600
600
|
|
|
@@ -606,7 +606,7 @@ export class CourseService {
|
|
|
606
606
|
return { success: true };
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
-
private mapCourse(
|
|
609
|
+
private mapCourse(
|
|
610
610
|
c: any,
|
|
611
611
|
metrics?: {
|
|
612
612
|
lessonCount?: number;
|
|
@@ -620,16 +620,16 @@ export class CourseService {
|
|
|
620
620
|
}[];
|
|
621
621
|
},
|
|
622
622
|
extras?: CourseExtraFields,
|
|
623
|
-
) {
|
|
624
|
-
const rawCode = extras?.code ?? c.code ?? c.slug;
|
|
625
|
-
const resolvedTitle = this.normalizeOptionalText(c.title) ?? c.slug;
|
|
626
|
-
const courseImages = Array.isArray(c.course_image) ? c.course_image : [];
|
|
627
|
-
const classCount = c._count?.course_class_group ?? 0;
|
|
628
|
-
const offeringType =
|
|
629
|
-
this.normalizeOfferingType(extras?.offering_type) ??
|
|
630
|
-
(classCount > 0 ? 'scheduled' : 'on_demand');
|
|
631
|
-
|
|
632
|
-
const logoImage = courseImages.find(
|
|
623
|
+
) {
|
|
624
|
+
const rawCode = extras?.code ?? c.code ?? c.slug;
|
|
625
|
+
const resolvedTitle = this.normalizeOptionalText(c.title) ?? c.slug;
|
|
626
|
+
const courseImages = Array.isArray(c.course_image) ? c.course_image : [];
|
|
627
|
+
const classCount = c._count?.course_class_group ?? 0;
|
|
628
|
+
const offeringType =
|
|
629
|
+
this.normalizeOfferingType(extras?.offering_type) ??
|
|
630
|
+
(classCount > 0 ? 'scheduled' : 'on_demand');
|
|
631
|
+
|
|
632
|
+
const logoImage = courseImages.find(
|
|
633
633
|
(courseImage: any) => courseImage.image_type?.slug === 'course-logo',
|
|
634
634
|
);
|
|
635
635
|
const bannerImage = courseImages.find(
|
|
@@ -638,12 +638,12 @@ export class CourseService {
|
|
|
638
638
|
|
|
639
639
|
return {
|
|
640
640
|
id: c.id,
|
|
641
|
-
code:
|
|
642
|
-
rawCode?.toUpperCase().replace(/[^A-Z0-9-]+/g, '-').slice(0, 32) ??
|
|
643
|
-
'',
|
|
644
|
-
slug: c.slug,
|
|
645
|
-
title: resolvedTitle,
|
|
646
|
-
description: c.description ?? '',
|
|
641
|
+
code:
|
|
642
|
+
rawCode?.toUpperCase().replace(/[^A-Z0-9-]+/g, '-').slice(0, 32) ??
|
|
643
|
+
'',
|
|
644
|
+
slug: c.slug,
|
|
645
|
+
title: resolvedTitle,
|
|
646
|
+
description: c.description ?? '',
|
|
647
647
|
level: c.level,
|
|
648
648
|
status: c.status,
|
|
649
649
|
durationHours: c.duration_hours ?? 0,
|
|
@@ -666,14 +666,14 @@ export class CourseService {
|
|
|
666
666
|
(cc: any) => cc.category?.id ?? 0,
|
|
667
667
|
),
|
|
668
668
|
isFeatured: extras?.is_featured ?? c.is_featured ?? false,
|
|
669
|
-
hasCertificate: extras?.has_certificate ?? c.has_certificate ?? false,
|
|
670
|
-
isListed: extras?.is_listed ?? c.is_listed ?? false,
|
|
671
|
-
offeringType,
|
|
672
|
-
enrollmentCount: c._count?.course_enrollment ?? 0,
|
|
673
|
-
moduleCount: c._count?.course_module ?? 0,
|
|
674
|
-
classCount,
|
|
675
|
-
lessonCount: metrics?.lessonCount ?? 0,
|
|
676
|
-
sessionCount: metrics?.sessionCount ?? 0,
|
|
669
|
+
hasCertificate: extras?.has_certificate ?? c.has_certificate ?? false,
|
|
670
|
+
isListed: extras?.is_listed ?? c.is_listed ?? false,
|
|
671
|
+
offeringType,
|
|
672
|
+
enrollmentCount: c._count?.course_enrollment ?? 0,
|
|
673
|
+
moduleCount: c._count?.course_module ?? 0,
|
|
674
|
+
classCount,
|
|
675
|
+
lessonCount: metrics?.lessonCount ?? 0,
|
|
676
|
+
sessionCount: metrics?.sessionCount ?? 0,
|
|
677
677
|
averageCompletion: Math.round(metrics?.averageCompletion ?? 0),
|
|
678
678
|
certificatesIssued:
|
|
679
679
|
metrics?.certificatesIssued ?? c._count?.certificate ?? 0,
|
|
@@ -720,29 +720,29 @@ export class CourseService {
|
|
|
720
720
|
try {
|
|
721
721
|
const idsCsv = normalizedIds.join(',');
|
|
722
722
|
const rows = (await this.prisma.$queryRawUnsafe(
|
|
723
|
-
`
|
|
724
|
-
SELECT id, code, is_featured, has_certificate, is_listed, offering_type
|
|
725
|
-
FROM course
|
|
726
|
-
WHERE id IN (${idsCsv})
|
|
727
|
-
`,
|
|
723
|
+
`
|
|
724
|
+
SELECT id, code, is_featured, has_certificate, is_listed, offering_type
|
|
725
|
+
FROM course
|
|
726
|
+
WHERE id IN (${idsCsv})
|
|
727
|
+
`,
|
|
728
728
|
)) as Array<{
|
|
729
729
|
id: number;
|
|
730
730
|
code: string | null;
|
|
731
731
|
is_featured: boolean | null;
|
|
732
|
-
has_certificate: boolean | null;
|
|
733
|
-
is_listed: boolean | null;
|
|
734
|
-
offering_type: 'scheduled' | 'on_demand' | 'blended' | null;
|
|
735
|
-
}>;
|
|
736
|
-
|
|
737
|
-
for (const row of rows) {
|
|
738
|
-
extrasById.set(row.id, {
|
|
739
|
-
code: row.code,
|
|
740
|
-
is_featured: row.is_featured,
|
|
741
|
-
has_certificate: row.has_certificate,
|
|
742
|
-
is_listed: row.is_listed,
|
|
743
|
-
offering_type: row.offering_type,
|
|
744
|
-
});
|
|
745
|
-
}
|
|
732
|
+
has_certificate: boolean | null;
|
|
733
|
+
is_listed: boolean | null;
|
|
734
|
+
offering_type: 'scheduled' | 'on_demand' | 'blended' | null;
|
|
735
|
+
}>;
|
|
736
|
+
|
|
737
|
+
for (const row of rows) {
|
|
738
|
+
extrasById.set(row.id, {
|
|
739
|
+
code: row.code,
|
|
740
|
+
is_featured: row.is_featured,
|
|
741
|
+
has_certificate: row.has_certificate,
|
|
742
|
+
is_listed: row.is_listed,
|
|
743
|
+
offering_type: row.offering_type,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
746
|
} catch {
|
|
747
747
|
return extrasById;
|
|
748
748
|
}
|
|
@@ -772,15 +772,15 @@ export class CourseService {
|
|
|
772
772
|
sets.push(`has_certificate = $${params.length}`);
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
-
if (data.isListed !== undefined) {
|
|
776
|
-
params.push(data.isListed);
|
|
777
|
-
sets.push(`is_listed = $${params.length}`);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
if (data.offeringType !== undefined) {
|
|
781
|
-
params.push(data.offeringType);
|
|
782
|
-
sets.push(`offering_type = $${params.length}`);
|
|
783
|
-
}
|
|
775
|
+
if (data.isListed !== undefined) {
|
|
776
|
+
params.push(data.isListed);
|
|
777
|
+
sets.push(`is_listed = $${params.length}`);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if (data.offeringType !== undefined) {
|
|
781
|
+
params.push(data.offeringType);
|
|
782
|
+
sets.push(`offering_type = $${params.length}`);
|
|
783
|
+
}
|
|
784
784
|
|
|
785
785
|
if (sets.length === 0) {
|
|
786
786
|
return;
|
|
@@ -21,10 +21,10 @@ export class CreateCourseDto {
|
|
|
21
21
|
@MaxLength(255)
|
|
22
22
|
slug: string;
|
|
23
23
|
|
|
24
|
-
@IsString()
|
|
25
|
-
@MaxLength(255)
|
|
26
|
-
@IsOptional()
|
|
27
|
-
title?: string;
|
|
24
|
+
@IsString()
|
|
25
|
+
@MaxLength(255)
|
|
26
|
+
@IsOptional()
|
|
27
|
+
title?: string;
|
|
28
28
|
|
|
29
29
|
@IsString()
|
|
30
30
|
@IsOptional()
|
|
@@ -52,17 +52,17 @@ export class CreateCourseDto {
|
|
|
52
52
|
@IsOptional()
|
|
53
53
|
level?: 'beginner' | 'intermediate' | 'advanced';
|
|
54
54
|
|
|
55
|
-
@IsEnum(['draft', 'published', 'archived'])
|
|
56
|
-
@IsOptional()
|
|
57
|
-
status?: 'draft' | 'published' | 'archived';
|
|
58
|
-
|
|
59
|
-
@IsEnum(['scheduled', 'on_demand', 'blended'])
|
|
60
|
-
@IsOptional()
|
|
61
|
-
offeringType?: 'scheduled' | 'on_demand' | 'blended';
|
|
62
|
-
|
|
63
|
-
@IsBoolean()
|
|
64
|
-
@IsOptional()
|
|
65
|
-
isFeatured?: boolean;
|
|
55
|
+
@IsEnum(['draft', 'published', 'archived'])
|
|
56
|
+
@IsOptional()
|
|
57
|
+
status?: 'draft' | 'published' | 'archived';
|
|
58
|
+
|
|
59
|
+
@IsEnum(['scheduled', 'on_demand', 'blended'])
|
|
60
|
+
@IsOptional()
|
|
61
|
+
offeringType?: 'scheduled' | 'on_demand' | 'blended';
|
|
62
|
+
|
|
63
|
+
@IsBoolean()
|
|
64
|
+
@IsOptional()
|
|
65
|
+
isFeatured?: boolean;
|
|
66
66
|
|
|
67
67
|
@IsBoolean()
|
|
68
68
|
@IsOptional()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-enterprise-lead.dto.d.ts","sourceRoot":"","sources":["../../../src/enterprise/dto/add-enterprise-lead.dto.ts"],"names":[],"mappings":"AAEA,qBAAa,oBAAoB;IAG/B,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.AddEnterpriseLeadDto = void 0;
|
|
13
|
-
const class_validator_1 = require("class-validator");
|
|
14
|
-
class AddEnterpriseLeadDto {
|
|
15
|
-
}
|
|
16
|
-
exports.AddEnterpriseLeadDto = AddEnterpriseLeadDto;
|
|
17
|
-
__decorate([
|
|
18
|
-
(0, class_validator_1.IsInt)(),
|
|
19
|
-
(0, class_validator_1.IsNotEmpty)(),
|
|
20
|
-
__metadata("design:type", Number)
|
|
21
|
-
], AddEnterpriseLeadDto.prototype, "person_id", void 0);
|
|
22
|
-
//# sourceMappingURL=add-enterprise-lead.dto.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-enterprise-lead.dto.js","sourceRoot":"","sources":["../../../src/enterprise/dto/add-enterprise-lead.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAoD;AAEpD,MAAa,oBAAoB;CAIhC;AAJD,oDAIC;AADC;IAFC,IAAA,uBAAK,GAAE;IACP,IAAA,4BAAU,GAAE;;uDACK"}
|