@hed-hog/lms 0.0.331 → 0.0.347

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/class-group/class-group.controller.d.ts +8 -8
  2. package/dist/class-group/class-group.service.d.ts +8 -8
  3. package/dist/course/course.controller.d.ts +6 -1
  4. package/dist/course/course.controller.d.ts.map +1 -1
  5. package/dist/course/course.controller.js +19 -2
  6. package/dist/course/course.controller.js.map +1 -1
  7. package/dist/course/course.service.d.ts +6 -0
  8. package/dist/course/course.service.d.ts.map +1 -1
  9. package/dist/course/course.service.js +63 -28
  10. package/dist/course/course.service.js.map +1 -1
  11. package/dist/course/dto/create-course.dto.d.ts +1 -0
  12. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  13. package/dist/course/dto/create-course.dto.js +5 -0
  14. package/dist/course/dto/create-course.dto.js.map +1 -1
  15. package/dist/enterprise/enterprise.controller.d.ts +84 -12
  16. package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
  17. package/dist/enterprise/enterprise.controller.js +10 -0
  18. package/dist/enterprise/enterprise.controller.js.map +1 -1
  19. package/dist/enterprise/enterprise.service.d.ts +90 -12
  20. package/dist/enterprise/enterprise.service.d.ts.map +1 -1
  21. package/dist/enterprise/enterprise.service.js +413 -40
  22. package/dist/enterprise/enterprise.service.js.map +1 -1
  23. package/dist/enterprise/training/training-admin.controller.d.ts +9 -6
  24. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  25. package/dist/enterprise/training/training-admin.controller.js +10 -6
  26. package/dist/enterprise/training/training-admin.controller.js.map +1 -1
  27. package/dist/enterprise/training/training-admin.service.d.ts +11 -5
  28. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  29. package/dist/enterprise/training/training-admin.service.js +108 -52
  30. package/dist/enterprise/training/training-admin.service.js.map +1 -1
  31. package/dist/enterprise/training/training-viewer.controller.d.ts +3 -0
  32. package/dist/enterprise/training/training-viewer.controller.d.ts.map +1 -1
  33. package/dist/evaluation/evaluation.controller.d.ts +2 -2
  34. package/dist/evaluation/evaluation.service.d.ts +2 -2
  35. package/dist/instructor/dto/create-instructor-skill.dto.d.ts +0 -4
  36. package/dist/instructor/dto/create-instructor-skill.dto.d.ts.map +1 -1
  37. package/dist/instructor/dto/create-instructor-skill.dto.js +0 -21
  38. package/dist/instructor/dto/create-instructor-skill.dto.js.map +1 -1
  39. package/dist/instructor/dto/update-instructor-skill.dto.d.ts +0 -4
  40. package/dist/instructor/dto/update-instructor-skill.dto.d.ts.map +1 -1
  41. package/dist/instructor/dto/update-instructor-skill.dto.js +0 -22
  42. package/dist/instructor/dto/update-instructor-skill.dto.js.map +1 -1
  43. package/dist/instructor/instructor-skill.controller.d.ts +4 -4
  44. package/dist/instructor/instructor-skill.service.d.ts +4 -7
  45. package/dist/instructor/instructor-skill.service.d.ts.map +1 -1
  46. package/dist/instructor/instructor-skill.service.js +2 -89
  47. package/dist/instructor/instructor-skill.service.js.map +1 -1
  48. package/dist/instructor/instructor.controller.d.ts +21 -0
  49. package/dist/instructor/instructor.controller.d.ts.map +1 -1
  50. package/dist/instructor/instructor.controller.js +19 -0
  51. package/dist/instructor/instructor.controller.js.map +1 -1
  52. package/dist/instructor/instructor.service.d.ts +27 -0
  53. package/dist/instructor/instructor.service.d.ts.map +1 -1
  54. package/dist/instructor/instructor.service.js +79 -25
  55. package/dist/instructor/instructor.service.js.map +1 -1
  56. package/dist/lms.module.d.ts.map +1 -1
  57. package/dist/lms.module.js.map +1 -1
  58. package/dist/training/dto/create-training.dto.d.ts +1 -0
  59. package/dist/training/dto/create-training.dto.d.ts.map +1 -1
  60. package/dist/training/dto/create-training.dto.js +5 -0
  61. package/dist/training/dto/create-training.dto.js.map +1 -1
  62. package/dist/training/training.controller.d.ts +4 -0
  63. package/dist/training/training.controller.d.ts.map +1 -1
  64. package/dist/training/training.service.d.ts +8 -0
  65. package/dist/training/training.service.d.ts.map +1 -1
  66. package/dist/training/training.service.js +71 -6
  67. package/dist/training/training.service.js.map +1 -1
  68. package/hedhog/data/route.yaml +23 -1
  69. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +80 -33
  70. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +3 -3
  71. package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +591 -0
  72. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +6 -1
  73. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +39 -7
  74. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +1 -3
  75. package/hedhog/frontend/app/classes/page.tsx.ejs +34 -7
  76. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +3 -33
  77. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +9 -9
  78. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +109 -0
  79. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +40 -13
  80. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +76 -81
  81. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +60 -0
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/course-scheduled-classes-tab.tsx.ejs +406 -0
  83. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
  84. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
  85. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
  86. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
  87. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +243 -34
  88. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +185 -0
  89. package/hedhog/frontend/app/courses/page.tsx.ejs +6 -1
  90. package/hedhog/frontend/app/enterprise/_components/enterprise-activity-timeline.tsx.ejs +87 -0
  91. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +4 -0
  92. package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +31 -5
  93. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +79 -20
  94. package/hedhog/frontend/app/enterprise/_components/enterprise-company-identity-card.tsx.ejs +11 -2
  95. package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +201 -0
  96. package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +55 -24
  97. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +430 -296
  98. package/hedhog/frontend/app/enterprise/_components/enterprise-mocks.ts.ejs +277 -0
  99. package/hedhog/frontend/app/enterprise/_components/enterprise-overview-analytics.tsx.ejs +205 -0
  100. package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +97 -0
  101. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +82 -57
  102. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +4 -0
  103. package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +60 -22
  104. package/hedhog/frontend/app/enterprise/_components/enterprise-types.ts.ejs +54 -0
  105. package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +211 -0
  106. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -7
  107. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +31 -19
  108. package/hedhog/frontend/app/exams/page.tsx.ejs +6 -1
  109. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +51 -104
  110. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +625 -366
  111. package/hedhog/frontend/app/instructors/page.tsx.ejs +6 -1
  112. package/hedhog/frontend/app/paths/page.tsx.ejs +76 -8
  113. package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +6 -1
  114. package/hedhog/frontend/app/training/page.tsx.ejs +78 -9
  115. package/hedhog/frontend/messages/en.json +101 -10
  116. package/hedhog/frontend/messages/pt.json +115 -11
  117. package/hedhog/table/enterprise_student_license_event.yaml +30 -0
  118. package/hedhog/table/instructor_skill.yaml +0 -11
  119. package/hedhog/table/learning_path.yaml +4 -0
  120. package/package.json +6 -6
  121. package/src/course/course.controller.ts +18 -0
  122. package/src/course/course.service.ts +85 -26
  123. package/src/course/dto/create-course.dto.ts +4 -0
  124. package/src/enterprise/enterprise.controller.ts +5 -0
  125. package/src/enterprise/enterprise.service.ts +507 -29
  126. package/src/enterprise/training/training-admin.controller.ts +4 -0
  127. package/src/enterprise/training/training-admin.service.ts +115 -51
  128. package/src/instructor/dto/create-instructor-skill.dto.ts +0 -17
  129. package/src/instructor/dto/update-instructor-skill.dto.ts +0 -18
  130. package/src/instructor/instructor-skill.service.ts +2 -97
  131. package/src/instructor/instructor.controller.ts +16 -0
  132. package/src/instructor/instructor.service.ts +87 -10
  133. package/src/lms.module.ts +1 -0
  134. package/src/training/dto/create-training.dto.ts +4 -0
  135. package/src/training/training.service.ts +104 -5
@@ -1544,7 +1544,20 @@
1544
1544
  "description": {
1545
1545
  "label": "Descrição",
1546
1546
  "placeholder": "Descreva a formação...",
1547
- "required": true
1547
+ "required": false
1548
+ },
1549
+ "progressionMode": {
1550
+ "label": "Fluxo da Trilha",
1551
+ "placeholder": "Selecione o fluxo",
1552
+ "required": true,
1553
+ "options": {
1554
+ "sequential": "Exigir ordem dos passos",
1555
+ "free": "Liberar navegação entre passos"
1556
+ },
1557
+ "help": {
1558
+ "sequential": "O aluno precisa concluir cada etapa da trilha antes de avançar para a próxima.",
1559
+ "free": "O aluno pode abrir qualquer etapa da trilha a qualquer momento, sem bloqueio de sequência."
1560
+ }
1548
1561
  },
1549
1562
  "area": {
1550
1563
  "label": "Área",
@@ -2916,6 +2929,11 @@
2916
2929
  "subFree": "{count} vagas livres",
2917
2930
  "subFull": "Lotada"
2918
2931
  },
2932
+ "nextClass": "Próxima Aula",
2933
+ "noClass": "Sem aula",
2934
+ "createNext": "Crie a próxima aula",
2935
+ "calendar": "Calendário",
2936
+ "completed": "{count} concluída(s)",
2919
2937
  "avgAttendance": {
2920
2938
  "label": "Presença Média",
2921
2939
  "sub": "frequência geral"
@@ -3547,10 +3565,92 @@
3547
3565
  "studentRemoveError": "Falha ao remover aluno."
3548
3566
  },
3549
3567
  "form": {
3568
+ "title": {
3569
+ "create": "Criar Novo Curso",
3570
+ "edit": "Editar Curso"
3571
+ },
3572
+ "description": {
3573
+ "create": "Preencha os dados para criar um novo curso. Após criar, você será redirecionado para a página do curso.",
3574
+ "edit": "Atualize as informações do curso abaixo."
3575
+ },
3576
+ "fields": {
3577
+ "internalName": {
3578
+ "label": "Nome Interno",
3579
+ "placeholder": "Ex: React Avançado",
3580
+ "description": "Nome interno do curso para uso administrativo"
3581
+ },
3582
+ "slug": {
3583
+ "label": "Slug",
3584
+ "placeholder": "Ex: react-avancado",
3585
+ "description": "Identificador único na URL (letras minúsculas, números e hífens)"
3586
+ },
3587
+ "commercialTitle": {
3588
+ "label": "Título Comercial",
3589
+ "placeholder": "Ex: React Avançado"
3590
+ },
3591
+ "description": {
3592
+ "label": "Descrição",
3593
+ "placeholder": "Descreva o conteúdo e objetivos do curso..."
3594
+ },
3595
+ "level": {
3596
+ "label": "Nível",
3597
+ "placeholder": "Selecione"
3598
+ },
3599
+ "status": {
3600
+ "label": "Status",
3601
+ "placeholder": "Selecione"
3602
+ },
3603
+ "categories": {
3604
+ "label": "Categorias",
3605
+ "description": "Selecione uma ou mais categorias",
3606
+ "createAction": "Criar categoria",
3607
+ "selectPlaceholder": "Selecione uma categoria",
3608
+ "removeAction": "Remover categoria",
3609
+ "searchPlaceholder": "Digite para buscar categoria...",
3610
+ "noResults": "Nenhuma categoria encontrada",
3611
+ "selectedCount": "categoria(s) selecionada(s)"
3612
+ },
3613
+ "primaryColor": {
3614
+ "label": "Cor Primária",
3615
+ "placeholder": "#1D4ED8"
3616
+ },
3617
+ "secondaryColor": {
3618
+ "label": "Cor Secundária",
3619
+ "placeholder": "#111827"
3620
+ },
3621
+ "logo": {
3622
+ "label": "Logo do Curso",
3623
+ "alt": "Logo do curso",
3624
+ "upload": "Carregar logo",
3625
+ "replace": "Substituir logo",
3626
+ "remove": "Remover",
3627
+ "description": "Imagem opcional para identificar o curso"
3628
+ }
3629
+ },
3630
+ "validation": {
3631
+ "internalNameMinLength": "Nome interno deve ter pelo menos 3 caracteres",
3632
+ "slugPattern": "Slug deve conter apenas letras minúsculas, números e hífens"
3633
+ },
3634
+ "actions": {
3635
+ "cancel": "Cancelar",
3636
+ "save": "Salvar Alterações",
3637
+ "create": "Criar Curso"
3638
+ },
3550
3639
  "toasts": {
3551
3640
  "courseCreated": "Curso criado com sucesso! Redirecionando...",
3552
3641
  "courseCreateError": "Não foi possível criar o curso."
3553
3642
  }
3643
+ },
3644
+ "levels": {
3645
+ "beginner": "Iniciante",
3646
+ "intermediate": "Intermediário",
3647
+ "advanced": "Avançado"
3648
+ },
3649
+ "status": {
3650
+ "active": "Ativo",
3651
+ "inactive": "Inativo",
3652
+ "draft": "Rascunho",
3653
+ "archived": "Arquivado"
3554
3654
  }
3555
3655
  },
3556
3656
  "InstructorsPage": {
@@ -3661,9 +3761,17 @@
3661
3761
  "classes": {
3662
3762
  "emptyTitle": "Nenhuma turma encontrada",
3663
3763
  "emptyDescription": "Este instrutor ainda não está vinculado a nenhuma turma.",
3764
+ "searchPlaceholder": "Buscar turma por nome, código ou curso...",
3765
+ "statusFilterPlaceholder": "Filtrar por status",
3766
+ "allStatuses": "Todos os status",
3664
3767
  "start": "Início:",
3665
3768
  "end": "Fim:",
3666
- "students": "{count} aluno(s)"
3769
+ "students": "{count} aluno(s)",
3770
+ "studentsOfCapacity": "{count} / {total} aluno(s)",
3771
+ "actions": {
3772
+ "editAriaLabel": "Editar turma",
3773
+ "openAriaLabel": "Abrir página da turma"
3774
+ }
3667
3775
  },
3668
3776
  "evaluations": {
3669
3777
  "comingSoonTitle": "Avaliações — funcionalidade em breve",
@@ -3713,23 +3821,17 @@
3713
3821
  "current": "Skills de Instrutor"
3714
3822
  },
3715
3823
  "filters": {
3716
- "searchPlaceholder": "Buscar por slug ou nome..."
3824
+ "searchPlaceholder": "Buscar por slug..."
3717
3825
  },
3718
3826
  "table": {
3719
3827
  "slug": "Slug",
3720
- "namePt": "Nome (PT)",
3721
- "nameEn": "Nome (EN)",
3722
3828
  "status": "Status"
3723
3829
  },
3724
3830
  "form": {
3725
3831
  "slug": "Slug",
3726
3832
  "slugPlaceholder": "ex.: javascript, react-js",
3727
- "namePt": "Nome (PT)",
3728
- "namePtPlaceholder": "Nome em português",
3729
- "nameEn": "Nome (EN)",
3730
- "nameEnPlaceholder": "Nome em inglês (opcional)",
3731
3833
  "status": "Status",
3732
- "statusPlaceholder": "Selecione o status",
3834
+ "statusPlaceholder": "Selecione um status",
3733
3835
  "validation": {
3734
3836
  "required": "Obrigatório",
3735
3837
  "max100": "Máximo 100 caracteres",
@@ -3942,7 +4044,8 @@
3942
4044
  },
3943
4045
  "actions": {
3944
4046
  "create": "Criar nova turma",
3945
- "add": "Adicionar"
4047
+ "add": "Adicionar",
4048
+ "edit": "Editar"
3946
4049
  },
3947
4050
  "empty": {
3948
4051
  "title": "Nenhuma turma encontrada.",
@@ -3950,6 +4053,7 @@
3950
4053
  },
3951
4054
  "table": {
3952
4055
  "class": "Turma",
4056
+ "instructor": "Instrutor",
3953
4057
  "status": "Status",
3954
4058
  "period": "Período",
3955
4059
  "capacity": "Capacidade"
@@ -0,0 +1,30 @@
1
+ columns:
2
+ - type: pk
3
+ - name: enterprise_id
4
+ type: fk
5
+ references:
6
+ table: enterprise
7
+ column: id
8
+ onDelete: CASCADE
9
+ - name: person_id
10
+ type: fk
11
+ references:
12
+ table: person
13
+ column: id
14
+ onDelete: CASCADE
15
+ - name: event_type
16
+ type: enum
17
+ values: [assigned, revoked, status_changed]
18
+ - name: previous_status
19
+ type: varchar
20
+ length: 32
21
+ isNullable: true
22
+ - name: next_status
23
+ type: varchar
24
+ length: 32
25
+ isNullable: true
26
+ - type: created_at
27
+
28
+ indices:
29
+ - columns: [enterprise_id, created_at]
30
+ - columns: [person_id, created_at]
@@ -2,17 +2,6 @@ columns:
2
2
  - type: pk
3
3
  - name: slug
4
4
  isUnique: true
5
- - name: name
6
- type: locale_varchar
7
- locale:
8
- pt: Nome
9
- en: Name
10
- - name: description
11
- type: locale_text
12
- isNullable: true
13
- locale:
14
- pt: Descrição
15
- en: Description
16
5
  - name: status
17
6
  type: enum
18
7
  values: [active, inactive]
@@ -24,6 +24,10 @@ columns:
24
24
  type: enum
25
25
  values: [draft, active, archived]
26
26
  default: draft
27
+ - name: progress_mode
28
+ type: enum
29
+ values: [sequential, free]
30
+ default: sequential
27
31
  - name: primary_color
28
32
  type: varchar
29
33
  length: 9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/lms",
3
- "version": "0.0.331",
3
+ "version": "0.0.347",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -9,15 +9,15 @@
9
9
  "@nestjs/core": "^11",
10
10
  "@nestjs/jwt": "^11",
11
11
  "@nestjs/mapped-types": "*",
12
+ "@hed-hog/api-prisma": "0.0.6",
12
13
  "@hed-hog/api-types": "0.0.1",
13
14
  "@hed-hog/api-locale": "0.0.14",
14
15
  "@hed-hog/api": "0.0.8",
15
- "@hed-hog/api-prisma": "0.0.6",
16
- "@hed-hog/contact": "0.0.331",
17
16
  "@hed-hog/api-pagination": "0.0.7",
18
- "@hed-hog/category": "0.0.331",
19
- "@hed-hog/finance": "0.0.331",
20
- "@hed-hog/core": "0.0.331"
17
+ "@hed-hog/category": "0.0.347",
18
+ "@hed-hog/contact": "0.0.347",
19
+ "@hed-hog/finance": "0.0.347",
20
+ "@hed-hog/core": "0.0.347"
21
21
  },
22
22
  "exports": {
23
23
  ".": {
@@ -28,6 +28,7 @@ export class CourseController {
28
28
  @Query('status') status?: string,
29
29
  @Query('level') level?: string,
30
30
  @Query('category') category?: string,
31
+ @Query('offeringTypes') offeringTypes?: string | string[],
31
32
  ) {
32
33
  return this.courseService.list({
33
34
  page: page ? Number(page) : 1,
@@ -36,9 +37,26 @@ export class CourseController {
36
37
  status,
37
38
  level,
38
39
  category,
40
+ offeringTypes: this.toArray(offeringTypes),
39
41
  });
40
42
  }
41
43
 
44
+ private toArray(value?: string | string[]) {
45
+ if (Array.isArray(value)) {
46
+ return value
47
+ .flatMap((item) => String(item).split(','))
48
+ .map((item) => item.trim())
49
+ .filter(Boolean);
50
+ }
51
+ if (typeof value === 'string') {
52
+ return value
53
+ .split(',')
54
+ .map((item) => item.trim())
55
+ .filter(Boolean);
56
+ }
57
+ return [];
58
+ }
59
+
42
60
  @Get('stats')
43
61
  stats() {
44
62
  return this.courseService.stats();
@@ -94,6 +94,35 @@ export class CourseService {
94
94
  return this.normalizeOptionalText(title) ?? slug;
95
95
  }
96
96
 
97
+ private async resolveCertificateTemplateId(value?: string | null) {
98
+ if (value === undefined) {
99
+ return undefined;
100
+ }
101
+
102
+ const normalized = String(value ?? '').trim();
103
+
104
+ if (!normalized) {
105
+ return null;
106
+ }
107
+
108
+ const numericId = Number(normalized);
109
+ const template = await this.prisma.certificate_template.findFirst({
110
+ where:
111
+ Number.isInteger(numericId) && numericId > 0
112
+ ? {
113
+ OR: [{ id: numericId }, { slug: normalized }],
114
+ }
115
+ : { slug: normalized },
116
+ select: { id: true },
117
+ });
118
+
119
+ if (!template) {
120
+ throw new BadRequestException('Certificate template not found');
121
+ }
122
+
123
+ return template.id;
124
+ }
125
+
97
126
  async list(params: {
98
127
  page?: number;
99
128
  pageSize?: number;
@@ -101,6 +130,7 @@ export class CourseService {
101
130
  status?: string;
102
131
  level?: string;
103
132
  category?: string;
133
+ offeringTypes?: string[];
104
134
  }) {
105
135
  const page = Math.max(Number(params.page) || 1, 1);
106
136
  const pageSize = Math.max(Number(params.pageSize) || 12, 1);
@@ -128,6 +158,9 @@ export class CourseService {
128
158
  some: { category: { slug: params.category } },
129
159
  };
130
160
  }
161
+ if (params.offeringTypes && params.offeringTypes.length > 0) {
162
+ where.offering_type = { in: params.offeringTypes };
163
+ }
131
164
 
132
165
  const [courses, total] = await Promise.all([
133
166
  this.prisma.course.findMany({
@@ -244,6 +277,9 @@ export class CourseService {
244
277
  },
245
278
  },
246
279
  },
280
+ certificate_template: {
281
+ select: { id: true, slug: true },
282
+ },
247
283
  _count: {
248
284
  select: {
249
285
  course_enrollment: true,
@@ -318,9 +354,18 @@ export class CourseService {
318
354
  }
319
355
 
320
356
  async create(dto: CreateCourseDto) {
321
- const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
357
+ const {
358
+ categorySlugs,
359
+ logoFileId,
360
+ bannerFileId,
361
+ instructorIds,
362
+ certificateModel,
363
+ ...data
364
+ } = dto;
322
365
  const normalizedSlug = data.slug.trim();
323
366
  const resolvedTitle = this.resolveCourseTitle(data.title, normalizedSlug);
367
+ const certificateTemplateId =
368
+ await this.resolveCertificateTemplateId(certificateModel);
324
369
 
325
370
  const categories = categorySlugs?.length
326
371
  ? await this.prisma.category.findMany({
@@ -365,6 +410,9 @@ export class CourseService {
365
410
  ...(data.certificateWorkload !== undefined && {
366
411
  certificate_workload: data.certificateWorkload,
367
412
  }),
413
+ ...(certificateTemplateId !== undefined && {
414
+ certificate_template_id: certificateTemplateId,
415
+ }),
368
416
  ...(data.primaryColor !== undefined && {
369
417
  primary_color: data.primaryColor,
370
418
  }),
@@ -427,6 +475,9 @@ export class CourseService {
427
475
  },
428
476
  },
429
477
  },
478
+ certificate_template: {
479
+ select: { id: true, slug: true },
480
+ },
430
481
  _count: { select: { course_enrollment: true } },
431
482
  },
432
483
  });
@@ -457,7 +508,14 @@ export class CourseService {
457
508
  }
458
509
 
459
510
  async update(id: number, dto: UpdateCourseDto) {
460
- const { categorySlugs, logoFileId, bannerFileId, instructorIds, ...data } = dto;
511
+ const {
512
+ categorySlugs,
513
+ logoFileId,
514
+ bannerFileId,
515
+ instructorIds,
516
+ certificateModel,
517
+ ...data
518
+ } = dto;
461
519
  let existingSlug: string | undefined;
462
520
 
463
521
  if (
@@ -478,6 +536,8 @@ export class CourseService {
478
536
  data.title !== undefined
479
537
  ? this.resolveCourseTitle(data.title, resolvedSlug ?? existingSlug ?? '')
480
538
  : undefined;
539
+ const certificateTemplateId =
540
+ await this.resolveCertificateTemplateId(certificateModel);
481
541
 
482
542
  const categories = categorySlugs?.length
483
543
  ? await this.prisma.category.findMany({
@@ -535,6 +595,9 @@ export class CourseService {
535
595
  ...(data.certificateWorkload !== undefined && {
536
596
  certificate_workload: data.certificateWorkload,
537
597
  }),
598
+ ...(certificateTemplateId !== undefined && {
599
+ certificate_template_id: certificateTemplateId,
600
+ }),
538
601
  ...(data.primaryColor !== undefined && {
539
602
  primary_color: data.primaryColor,
540
603
  }),
@@ -589,6 +652,9 @@ export class CourseService {
589
652
  },
590
653
  },
591
654
  },
655
+ certificate_template: {
656
+ select: { id: true, slug: true },
657
+ },
592
658
  _count: { select: { course_enrollment: true } },
593
659
  },
594
660
  });
@@ -731,6 +797,11 @@ export class CourseService {
731
797
  averageCompletion: Math.round(metrics?.averageCompletion ?? 0),
732
798
  certificatesIssued:
733
799
  metrics?.certificatesIssued ?? c._count?.certificate ?? 0,
800
+ certificateModel:
801
+ c.certificate_template?.slug ??
802
+ (c.certificate_template_id
803
+ ? String(c.certificate_template_id)
804
+ : null),
734
805
  progressByModule: metrics?.progressByModule ?? [],
735
806
  instructors: (c.course_instructor ?? []).map((ci: any) => ({
736
807
  id: ci.instructor_id,
@@ -808,51 +879,39 @@ export class CourseService {
808
879
  id: number,
809
880
  data: PersistCourseExtrasInput,
810
881
  ) {
811
- const sets: string[] = [];
812
- const params: Array<string | boolean | number> = [];
882
+ const updateData: Record<string, string | boolean> = {};
813
883
 
814
884
  if (data.name !== undefined) {
815
- params.push(data.name);
816
- sets.push(`name = $${params.length}`);
885
+ updateData.name = data.name;
817
886
  }
818
887
 
819
888
  if (data.isFeatured !== undefined) {
820
- params.push(data.isFeatured);
821
- sets.push(`is_featured = $${params.length}`);
889
+ updateData.is_featured = data.isFeatured;
822
890
  }
823
891
 
824
892
  if (data.hasCertificate !== undefined) {
825
- params.push(data.hasCertificate);
826
- sets.push(`has_certificate = $${params.length}`);
893
+ updateData.has_certificate = data.hasCertificate;
827
894
  }
828
895
 
829
896
  if (data.isListed !== undefined) {
830
- params.push(data.isListed);
831
- sets.push(`is_listed = $${params.length}`);
897
+ updateData.is_listed = data.isListed;
832
898
  }
833
899
 
834
900
  if (data.offeringType !== undefined) {
835
- params.push(data.offeringType);
836
- sets.push(`offering_type = $${params.length}`);
901
+ updateData.offering_type = data.offeringType;
837
902
  }
838
903
 
839
- if (sets.length === 0) {
904
+ if (Object.keys(updateData).length === 0) {
840
905
  return;
841
906
  }
842
907
 
843
- params.push(id);
844
-
845
908
  try {
846
- await this.prisma.$executeRawUnsafe(
847
- `
848
- UPDATE course
849
- SET ${sets.join(', ')}, updated_at = NOW()
850
- WHERE id = $${params.length}
851
- `,
852
- ...params,
853
- );
909
+ await this.prisma.course.update({
910
+ where: { id },
911
+ data: updateData as any,
912
+ });
854
913
  } catch {
855
- // Columns may not exist yet in environments with outdated schema.
914
+ // Some environments may still be behind the current course schema.
856
915
  }
857
916
  }
858
917
 
@@ -99,6 +99,10 @@ export class CreateCourseDto {
99
99
  @IsOptional()
100
100
  categorySlugs?: string[];
101
101
 
102
+ @IsString()
103
+ @IsOptional()
104
+ certificateModel?: string | null;
105
+
102
106
  @IsArray()
103
107
  @IsInt({ each: true })
104
108
  @IsOptional()
@@ -126,6 +126,11 @@ export class EnterpriseController {
126
126
  return { profiles };
127
127
  }
128
128
 
129
+ @Get(':id/overview')
130
+ getOverview(@Param('id', ParseIntPipe) id: number) {
131
+ return this.enterpriseService.getOverview(id);
132
+ }
133
+
129
134
  @Get(':id')
130
135
  getById(@Param('id', ParseIntPipe) id: number) {
131
136
  return this.enterpriseService.getById(id);