@hed-hog/lms 0.0.319 → 0.0.321
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 +64 -1
- package/dist/class-group/class-group.controller.d.ts.map +1 -1
- package/dist/class-group/class-group.controller.js +35 -0
- package/dist/class-group/class-group.controller.js.map +1 -1
- package/dist/class-group/class-group.service.d.ts +66 -1
- package/dist/class-group/class-group.service.d.ts.map +1 -1
- package/dist/class-group/class-group.service.js +164 -13
- package/dist/class-group/class-group.service.js.map +1 -1
- package/dist/class-group/dto/create-class-group.dto.d.ts.map +1 -1
- package/dist/class-group/dto/create-class-group.dto.js +2 -1
- package/dist/class-group/dto/create-class-group.dto.js.map +1 -1
- package/dist/class-group/dto/material.dto.d.ts +18 -0
- package/dist/class-group/dto/material.dto.d.ts.map +1 -0
- package/dist/class-group/dto/material.dto.js +86 -0
- package/dist/class-group/dto/material.dto.js.map +1 -0
- package/dist/course/course.service.d.ts +2 -0
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +27 -2
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course.dto.d.ts +2 -2
- package/dist/course/dto/create-course.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course.dto.js.map +1 -1
- package/dist/enterprise/enterprise.controller.d.ts +7 -1
- package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
- package/dist/enterprise/enterprise.controller.js +72 -2
- package/dist/enterprise/enterprise.controller.js.map +1 -1
- package/dist/enterprise/enterprise.module.d.ts.map +1 -1
- package/dist/enterprise/enterprise.module.js +2 -1
- package/dist/enterprise/enterprise.module.js.map +1 -1
- package/dist/enterprise/enterprise.service.d.ts +3 -0
- package/dist/enterprise/enterprise.service.d.ts.map +1 -1
- package/dist/enterprise/enterprise.service.js +84 -1
- package/dist/enterprise/enterprise.service.js.map +1 -1
- package/dist/enterprise/training/enterprise-training.module.d.ts +3 -0
- package/dist/enterprise/training/enterprise-training.module.d.ts.map +1 -0
- package/dist/enterprise/training/enterprise-training.module.js +40 -0
- package/dist/enterprise/training/enterprise-training.module.js.map +1 -0
- package/dist/enterprise/training/training-admin.controller.d.ts +525 -0
- package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -0
- package/dist/enterprise/training/training-admin.controller.js +385 -0
- package/dist/enterprise/training/training-admin.controller.js.map +1 -0
- package/dist/enterprise/training/training-admin.service.d.ts +582 -0
- package/dist/enterprise/training/training-admin.service.d.ts.map +1 -0
- package/dist/enterprise/training/training-admin.service.js +2283 -0
- package/dist/enterprise/training/training-admin.service.js.map +1 -0
- package/dist/enterprise/training/training-instructor.controller.d.ts +260 -0
- package/dist/enterprise/training/training-instructor.controller.d.ts.map +1 -0
- package/dist/enterprise/training/training-instructor.controller.js +199 -0
- package/dist/enterprise/training/training-instructor.controller.js.map +1 -0
- package/dist/enterprise/training/training-instructor.service.d.ts +280 -0
- package/dist/enterprise/training/training-instructor.service.d.ts.map +1 -0
- package/dist/enterprise/training/training-instructor.service.js +1218 -0
- package/dist/enterprise/training/training-instructor.service.js.map +1 -0
- package/dist/enterprise/training/training-student.controller.d.ts +168 -0
- package/dist/enterprise/training/training-student.controller.d.ts.map +1 -0
- package/dist/enterprise/training/training-student.controller.js +104 -0
- package/dist/enterprise/training/training-student.controller.js.map +1 -0
- package/dist/enterprise/training/training-student.service.d.ts +185 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -0
- package/dist/enterprise/training/training-student.service.js +674 -0
- package/dist/enterprise/training/training-student.service.js.map +1 -0
- package/dist/enterprise/training/training-viewer.controller.d.ts +298 -0
- package/dist/enterprise/training/training-viewer.controller.d.ts.map +1 -0
- package/dist/enterprise/training/training-viewer.controller.js +178 -0
- package/dist/enterprise/training/training-viewer.controller.js.map +1 -0
- package/dist/evaluation/dto/create-evaluation-topic.dto.d.ts +18 -0
- package/dist/evaluation/dto/create-evaluation-topic.dto.d.ts.map +1 -0
- package/dist/evaluation/dto/create-evaluation-topic.dto.js +59 -0
- package/dist/evaluation/dto/create-evaluation-topic.dto.js.map +1 -0
- package/dist/evaluation/dto/update-evaluation-topic.dto.d.ts +6 -0
- package/dist/evaluation/dto/update-evaluation-topic.dto.d.ts.map +1 -0
- package/dist/evaluation/dto/update-evaluation-topic.dto.js +9 -0
- package/dist/evaluation/dto/update-evaluation-topic.dto.js.map +1 -0
- package/dist/evaluation/evaluation.controller.d.ts +66 -0
- package/dist/evaluation/evaluation.controller.d.ts.map +1 -1
- package/dist/evaluation/evaluation.controller.js +73 -0
- package/dist/evaluation/evaluation.controller.js.map +1 -1
- package/dist/evaluation/evaluation.service.d.ts +71 -0
- package/dist/evaluation/evaluation.service.d.ts.map +1 -1
- package/dist/evaluation/evaluation.service.js +121 -0
- package/dist/evaluation/evaluation.service.js.map +1 -1
- package/dist/instructor/instructor.service.js +6 -6
- package/dist/instructor/instructor.service.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +3 -0
- package/dist/lms.module.js.map +1 -1
- package/hedhog/data/menu.yaml +19 -2
- package/hedhog/data/route.yaml +730 -0
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +74 -8
- package/hedhog/frontend/app/_components/course-avatar.tsx.ejs +10 -30
- package/hedhog/frontend/app/classes/[id]/_components/event-summary-popover.tsx.ejs +3 -3
- package/hedhog/frontend/app/classes/[id]/_components/quick-create-session-popover.tsx.ejs +1 -1
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +2141 -308
- package/hedhog/frontend/app/classes/page.tsx.ejs +8 -7
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +14 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +6 -2
- package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +201 -0
- package/hedhog/frontend/app/evaluations/_components/evaluation-topic-types.ts.ejs +49 -0
- package/hedhog/frontend/app/evaluations/page.tsx.ejs +483 -1112
- package/hedhog/frontend/app/instructors/page.tsx.ejs +22 -20
- package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +1278 -0
- package/hedhog/frontend/messages/en.json +98 -7
- package/hedhog/frontend/messages/pt.json +98 -7
- package/hedhog/table/course_class_group_material.yaml +45 -0
- package/package.json +8 -8
- package/src/class-group/class-group.controller.ts +30 -0
- package/src/class-group/class-group.service.ts +176 -5
- package/src/class-group/dto/create-class-group.dto.ts +2 -2
- package/src/class-group/dto/material.dto.ts +69 -0
- package/src/course/course.service.ts +41 -8
- package/src/course/dto/create-course.dto.ts +2 -2
- package/src/enterprise/enterprise.controller.ts +62 -1
- package/src/enterprise/enterprise.module.ts +2 -1
- package/src/enterprise/enterprise.service.ts +84 -1
- package/src/enterprise/training/enterprise-training.module.ts +27 -0
- package/src/enterprise/training/training-admin.controller.ts +278 -0
- package/src/enterprise/training/training-admin.service.ts +2523 -0
- package/src/enterprise/training/training-instructor.controller.ts +141 -0
- package/src/enterprise/training/training-instructor.service.ts +1303 -0
- package/src/enterprise/training/training-student.controller.ts +65 -0
- package/src/enterprise/training/training-student.service.ts +762 -0
- package/src/enterprise/training/training-viewer.controller.ts +115 -0
- package/src/evaluation/dto/create-evaluation-topic.dto.ts +48 -0
- package/src/evaluation/dto/update-evaluation-topic.dto.ts +6 -0
- package/src/evaluation/evaluation.controller.ts +63 -1
- package/src/evaluation/evaluation.service.ts +150 -1
- package/src/instructor/instructor.service.ts +4 -4
- package/src/lms.module.ts +3 -0
|
@@ -2372,6 +2372,11 @@
|
|
|
2372
2372
|
"label": "Lesson title",
|
|
2373
2373
|
"placeholder": "e.g. Live class session"
|
|
2374
2374
|
},
|
|
2375
|
+
"sessionAccess": {
|
|
2376
|
+
"label": "Lesson location / access link",
|
|
2377
|
+
"placeholder": "Room 201 or https://meet.google.com/...",
|
|
2378
|
+
"description": "Optional. When filled, it will be applied to every lesson generated for this class."
|
|
2379
|
+
},
|
|
2375
2380
|
"schedule": {
|
|
2376
2381
|
"label": "Schedule",
|
|
2377
2382
|
"placeholder": "7:00 PM - 10:00 PM"
|
|
@@ -2420,13 +2425,13 @@
|
|
|
2420
2425
|
"yearly": "year"
|
|
2421
2426
|
},
|
|
2422
2427
|
"days": {
|
|
2423
|
-
"MO": "
|
|
2424
|
-
"TU": "
|
|
2425
|
-
"WE": "
|
|
2426
|
-
"TH": "
|
|
2427
|
-
"FR": "
|
|
2428
|
-
"SA": "
|
|
2429
|
-
"SU": "
|
|
2428
|
+
"MO": "Mon",
|
|
2429
|
+
"TU": "Tue",
|
|
2430
|
+
"WE": "Wed",
|
|
2431
|
+
"TH": "Thu",
|
|
2432
|
+
"FR": "Fri",
|
|
2433
|
+
"SA": "Sat",
|
|
2434
|
+
"SU": "Sun"
|
|
2430
2435
|
}
|
|
2431
2436
|
}
|
|
2432
2437
|
},
|
|
@@ -2809,17 +2814,103 @@
|
|
|
2809
2814
|
"studentUpdated": "Student updated successfully!",
|
|
2810
2815
|
"lessonUpdated": "Lesson updated successfully!",
|
|
2811
2816
|
"lessonCreated": "Lesson created successfully!",
|
|
2817
|
+
"attendanceSaving": "Saving attendance...",
|
|
2812
2818
|
"attendanceSaved": "Attendance saved successfully!",
|
|
2813
2819
|
"error": "An error occurred. Please try again."
|
|
2814
2820
|
}
|
|
2815
2821
|
}
|
|
2816
2822
|
},
|
|
2823
|
+
"EvaluationTopicsPage": {
|
|
2824
|
+
"title": "Evaluation Topics",
|
|
2825
|
+
"description": "Manage evaluation topics grouped by target type.",
|
|
2826
|
+
"breadcrumbs": {
|
|
2827
|
+
"home": "Home",
|
|
2828
|
+
"lms": "LMS",
|
|
2829
|
+
"evaluations": "Evaluation Topics"
|
|
2830
|
+
},
|
|
2831
|
+
"newTopic": "New Topic",
|
|
2832
|
+
"kpi": {
|
|
2833
|
+
"total": "Total Topics",
|
|
2834
|
+
"totalDesc": "All evaluation topics",
|
|
2835
|
+
"active": "Active",
|
|
2836
|
+
"activeDesc": "Currently active topics",
|
|
2837
|
+
"inactive": "Inactive",
|
|
2838
|
+
"inactiveDesc": "Deactivated topics",
|
|
2839
|
+
"withRatings": "With Ratings",
|
|
2840
|
+
"withRatingsDesc": "Topics that have received ratings"
|
|
2841
|
+
},
|
|
2842
|
+
"filters": {
|
|
2843
|
+
"searchPlaceholder": "Search by name or description…",
|
|
2844
|
+
"clear": "Clear"
|
|
2845
|
+
},
|
|
2846
|
+
"tabs": {
|
|
2847
|
+
"all": "All"
|
|
2848
|
+
},
|
|
2849
|
+
"targetType": {
|
|
2850
|
+
"course": "Course",
|
|
2851
|
+
"course_lesson": "Lesson",
|
|
2852
|
+
"course_class_session": "Session",
|
|
2853
|
+
"question": "Question",
|
|
2854
|
+
"exam": "Exam"
|
|
2855
|
+
},
|
|
2856
|
+
"columns": {
|
|
2857
|
+
"name": "Name",
|
|
2858
|
+
"targetType": "Target Type",
|
|
2859
|
+
"order": "Order",
|
|
2860
|
+
"ratings": "Ratings",
|
|
2861
|
+
"status": "Status"
|
|
2862
|
+
},
|
|
2863
|
+
"status": {
|
|
2864
|
+
"active": "Active",
|
|
2865
|
+
"inactive": "Inactive"
|
|
2866
|
+
},
|
|
2867
|
+
"empty": {
|
|
2868
|
+
"title": "No topics found",
|
|
2869
|
+
"description": "Create an evaluation topic to get started."
|
|
2870
|
+
},
|
|
2871
|
+
"delete": {
|
|
2872
|
+
"title": "Delete Topic",
|
|
2873
|
+
"description": "Are you sure you want to delete \"{name}\"? This action cannot be undone.",
|
|
2874
|
+
"cancel": "Cancel",
|
|
2875
|
+
"confirm": "Delete",
|
|
2876
|
+
"deleting": "Deleting…",
|
|
2877
|
+
"success": "Topic deleted.",
|
|
2878
|
+
"error": "Failed to delete topic.",
|
|
2879
|
+
"disabledTooltip": "Cannot delete a topic that already has ratings."
|
|
2880
|
+
},
|
|
2881
|
+
"reorder": {
|
|
2882
|
+
"success": "Order saved.",
|
|
2883
|
+
"error": "Failed to save order."
|
|
2884
|
+
},
|
|
2885
|
+
"form": {
|
|
2886
|
+
"editTitle": "Edit Topic",
|
|
2887
|
+
"createTitle": "New Topic",
|
|
2888
|
+
"name": "Name",
|
|
2889
|
+
"namePlaceholder": "Enter topic name",
|
|
2890
|
+
"description": "Description",
|
|
2891
|
+
"descriptionPlaceholder": "Optional description",
|
|
2892
|
+
"targetType": "Target Type",
|
|
2893
|
+
"targetTypeReadonly": "Target type cannot be changed after creation.",
|
|
2894
|
+
"order": "Display Order",
|
|
2895
|
+
"orderPlaceholder": "e.g. 1",
|
|
2896
|
+
"isActive": "Active",
|
|
2897
|
+
"isActiveDesc": "Visible to evaluators",
|
|
2898
|
+
"cancel": "Cancel",
|
|
2899
|
+
"save": "Save Changes",
|
|
2900
|
+
"create": "Create Topic",
|
|
2901
|
+
"saving": "Saving…",
|
|
2902
|
+
"createdSuccess": "Topic created successfully.",
|
|
2903
|
+
"updatedSuccess": "Topic updated successfully.",
|
|
2904
|
+
"saveError": "Failed to save topic."
|
|
2905
|
+
}
|
|
2906
|
+
},
|
|
2817
2907
|
"EvaluationsPage": {
|
|
2818
2908
|
"title": "Evaluations",
|
|
2819
2909
|
"description": "Browse and filter all evaluation ratings submitted by students.",
|
|
2820
2910
|
"breadcrumbs": {
|
|
2821
2911
|
"home": "Home",
|
|
2822
2912
|
"lms": "LMS",
|
|
2913
|
+
"reports": "Reports",
|
|
2823
2914
|
"evaluations": "Evaluations"
|
|
2824
2915
|
},
|
|
2825
2916
|
"filters": {
|
|
@@ -2379,6 +2379,11 @@
|
|
|
2379
2379
|
"label": "Título das aulas",
|
|
2380
2380
|
"placeholder": "Ex.: Aula ao vivo da turma"
|
|
2381
2381
|
},
|
|
2382
|
+
"sessionAccess": {
|
|
2383
|
+
"label": "Local / link das aulas",
|
|
2384
|
+
"placeholder": "Sala 201 ou https://meet.google.com/...",
|
|
2385
|
+
"description": "Opcional. Quando preenchido, será aplicado a todas as aulas geradas para esta turma."
|
|
2386
|
+
},
|
|
2382
2387
|
"schedule": {
|
|
2383
2388
|
"label": "Horário",
|
|
2384
2389
|
"placeholder": "19:00 - 22:00"
|
|
@@ -2427,13 +2432,13 @@
|
|
|
2427
2432
|
"yearly": "ano"
|
|
2428
2433
|
},
|
|
2429
2434
|
"days": {
|
|
2430
|
-
"MO": "
|
|
2431
|
-
"TU": "
|
|
2432
|
-
"WE": "
|
|
2433
|
-
"TH": "
|
|
2434
|
-
"FR": "
|
|
2435
|
-
"SA": "
|
|
2436
|
-
"SU": "
|
|
2435
|
+
"MO": "Seg",
|
|
2436
|
+
"TU": "Ter",
|
|
2437
|
+
"WE": "Qua",
|
|
2438
|
+
"TH": "Qui",
|
|
2439
|
+
"FR": "Sex",
|
|
2440
|
+
"SA": "Sáb",
|
|
2441
|
+
"SU": "Dom"
|
|
2437
2442
|
}
|
|
2438
2443
|
}
|
|
2439
2444
|
},
|
|
@@ -2816,17 +2821,103 @@
|
|
|
2816
2821
|
"studentUpdated": "Aluno atualizado com sucesso!",
|
|
2817
2822
|
"lessonUpdated": "Aula atualizada com sucesso!",
|
|
2818
2823
|
"lessonCreated": "Aula criada com sucesso!",
|
|
2824
|
+
"attendanceSaving": "Salvando presença...",
|
|
2819
2825
|
"attendanceSaved": "Presença salva com sucesso!",
|
|
2820
2826
|
"error": "Ocorreu um erro. Tente novamente."
|
|
2821
2827
|
}
|
|
2822
2828
|
}
|
|
2823
2829
|
},
|
|
2830
|
+
"EvaluationTopicsPage": {
|
|
2831
|
+
"title": "Tópicos de Avaliação",
|
|
2832
|
+
"description": "Gerencie os tópicos de avaliação agrupados por tipo.",
|
|
2833
|
+
"breadcrumbs": {
|
|
2834
|
+
"home": "Início",
|
|
2835
|
+
"lms": "LMS",
|
|
2836
|
+
"evaluations": "Tópicos de Avaliação"
|
|
2837
|
+
},
|
|
2838
|
+
"newTopic": "Novo Tópico",
|
|
2839
|
+
"kpi": {
|
|
2840
|
+
"total": "Total de Tópicos",
|
|
2841
|
+
"totalDesc": "Todos os tópicos de avaliação",
|
|
2842
|
+
"active": "Ativos",
|
|
2843
|
+
"activeDesc": "Tópicos ativos no momento",
|
|
2844
|
+
"inactive": "Inativos",
|
|
2845
|
+
"inactiveDesc": "Tópicos desativados",
|
|
2846
|
+
"withRatings": "Com Avaliações",
|
|
2847
|
+
"withRatingsDesc": "Tópicos que já receberam avaliações"
|
|
2848
|
+
},
|
|
2849
|
+
"filters": {
|
|
2850
|
+
"searchPlaceholder": "Buscar por nome ou descrição…",
|
|
2851
|
+
"clear": "Limpar"
|
|
2852
|
+
},
|
|
2853
|
+
"tabs": {
|
|
2854
|
+
"all": "Todos"
|
|
2855
|
+
},
|
|
2856
|
+
"targetType": {
|
|
2857
|
+
"course": "Curso",
|
|
2858
|
+
"course_lesson": "Aula",
|
|
2859
|
+
"course_class_session": "Sessão",
|
|
2860
|
+
"question": "Questão",
|
|
2861
|
+
"exam": "Exame"
|
|
2862
|
+
},
|
|
2863
|
+
"columns": {
|
|
2864
|
+
"name": "Nome",
|
|
2865
|
+
"targetType": "Tipo",
|
|
2866
|
+
"order": "Ordem",
|
|
2867
|
+
"ratings": "Avaliações",
|
|
2868
|
+
"status": "Status"
|
|
2869
|
+
},
|
|
2870
|
+
"status": {
|
|
2871
|
+
"active": "Ativo",
|
|
2872
|
+
"inactive": "Inativo"
|
|
2873
|
+
},
|
|
2874
|
+
"empty": {
|
|
2875
|
+
"title": "Nenhum tópico encontrado",
|
|
2876
|
+
"description": "Crie um tópico de avaliação para começar."
|
|
2877
|
+
},
|
|
2878
|
+
"delete": {
|
|
2879
|
+
"title": "Excluir Tópico",
|
|
2880
|
+
"description": "Tem certeza que deseja excluir \"{name}\"? Esta ação não pode ser desfeita.",
|
|
2881
|
+
"cancel": "Cancelar",
|
|
2882
|
+
"confirm": "Excluir",
|
|
2883
|
+
"deleting": "Excluindo…",
|
|
2884
|
+
"success": "Tópico excluído.",
|
|
2885
|
+
"error": "Falha ao excluir o tópico.",
|
|
2886
|
+
"disabledTooltip": "Não é possível excluir um tópico que já possui avaliações."
|
|
2887
|
+
},
|
|
2888
|
+
"reorder": {
|
|
2889
|
+
"success": "Ordem salva.",
|
|
2890
|
+
"error": "Falha ao salvar a ordem."
|
|
2891
|
+
},
|
|
2892
|
+
"form": {
|
|
2893
|
+
"editTitle": "Editar Tópico",
|
|
2894
|
+
"createTitle": "Novo Tópico",
|
|
2895
|
+
"name": "Nome",
|
|
2896
|
+
"namePlaceholder": "Informe o nome do tópico",
|
|
2897
|
+
"description": "Descrição",
|
|
2898
|
+
"descriptionPlaceholder": "Descrição opcional",
|
|
2899
|
+
"targetType": "Tipo de Alvo",
|
|
2900
|
+
"targetTypeReadonly": "O tipo de alvo não pode ser alterado após a criação.",
|
|
2901
|
+
"order": "Ordem de Exibição",
|
|
2902
|
+
"orderPlaceholder": "ex.: 1",
|
|
2903
|
+
"isActive": "Ativo",
|
|
2904
|
+
"isActiveDesc": "Visível para avaliadores",
|
|
2905
|
+
"cancel": "Cancelar",
|
|
2906
|
+
"save": "Salvar Alterações",
|
|
2907
|
+
"create": "Criar Tópico",
|
|
2908
|
+
"saving": "Salvando…",
|
|
2909
|
+
"createdSuccess": "Tópico criado com sucesso.",
|
|
2910
|
+
"updatedSuccess": "Tópico atualizado com sucesso.",
|
|
2911
|
+
"saveError": "Falha ao salvar o tópico."
|
|
2912
|
+
}
|
|
2913
|
+
},
|
|
2824
2914
|
"EvaluationsPage": {
|
|
2825
2915
|
"title": "Avaliações",
|
|
2826
2916
|
"description": "Visualize e filtre todas as avaliações enviadas pelos alunos.",
|
|
2827
2917
|
"breadcrumbs": {
|
|
2828
2918
|
"home": "Início",
|
|
2829
2919
|
"lms": "LMS",
|
|
2920
|
+
"reports": "Relatórios",
|
|
2830
2921
|
"evaluations": "Avaliações"
|
|
2831
2922
|
},
|
|
2832
2923
|
"filters": {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
columns:
|
|
2
|
+
- type: pk
|
|
3
|
+
- name: course_class_group_id
|
|
4
|
+
type: fk
|
|
5
|
+
references:
|
|
6
|
+
table: course_class_group
|
|
7
|
+
column: id
|
|
8
|
+
onDelete: CASCADE
|
|
9
|
+
- name: course_class_session_id
|
|
10
|
+
type: fk
|
|
11
|
+
references:
|
|
12
|
+
table: course_class_session
|
|
13
|
+
column: id
|
|
14
|
+
onDelete: SET NULL
|
|
15
|
+
isNullable: true
|
|
16
|
+
- name: title
|
|
17
|
+
type: varchar
|
|
18
|
+
length: 255
|
|
19
|
+
- name: description
|
|
20
|
+
type: text
|
|
21
|
+
isNullable: true
|
|
22
|
+
- name: material_type
|
|
23
|
+
type: enum
|
|
24
|
+
values: [file, link]
|
|
25
|
+
- name: file_id
|
|
26
|
+
type: fk
|
|
27
|
+
isNullable: true
|
|
28
|
+
references:
|
|
29
|
+
table: file
|
|
30
|
+
column: id
|
|
31
|
+
onDelete: SET NULL
|
|
32
|
+
- name: url
|
|
33
|
+
type: varchar
|
|
34
|
+
length: 500
|
|
35
|
+
isNullable: true
|
|
36
|
+
- name: sort_order
|
|
37
|
+
type: int
|
|
38
|
+
default: 0
|
|
39
|
+
- type: created_at
|
|
40
|
+
- type: updated_at
|
|
41
|
+
|
|
42
|
+
indices:
|
|
43
|
+
- columns: [course_class_group_id]
|
|
44
|
+
- columns: [course_class_session_id]
|
|
45
|
+
- columns: [material_type]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/lms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.321",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
"@nestjs/jwt": "^11",
|
|
11
11
|
"@nestjs/mapped-types": "*",
|
|
12
12
|
"@hed-hog/api": "0.0.8",
|
|
13
|
-
"@hed-hog/contact": "0.0.319",
|
|
14
|
-
"@hed-hog/api-pagination": "0.0.7",
|
|
15
|
-
"@hed-hog/api-prisma": "0.0.6",
|
|
16
|
-
"@hed-hog/api-locale": "0.0.14",
|
|
17
|
-
"@hed-hog/finance": "0.0.319",
|
|
18
13
|
"@hed-hog/api-types": "0.0.1",
|
|
19
|
-
"@hed-hog/
|
|
20
|
-
"@hed-hog/
|
|
14
|
+
"@hed-hog/contact": "0.0.321",
|
|
15
|
+
"@hed-hog/api-locale": "0.0.14",
|
|
16
|
+
"@hed-hog/api-prisma": "0.0.6",
|
|
17
|
+
"@hed-hog/api-pagination": "0.0.7",
|
|
18
|
+
"@hed-hog/category": "0.0.321",
|
|
19
|
+
"@hed-hog/finance": "0.0.321",
|
|
20
|
+
"@hed-hog/core": "0.0.321"
|
|
21
21
|
},
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
SaveAttendanceDto,
|
|
20
20
|
UpdateStudentProfileDto,
|
|
21
21
|
} from './dto/enrollment.dto';
|
|
22
|
+
import { CreateMaterialDto } from './dto/material.dto';
|
|
22
23
|
import { UpdateClassGroupDto } from './dto/update-class-group.dto';
|
|
23
24
|
import { UpdateSessionDto } from './dto/update-session.dto';
|
|
24
25
|
|
|
@@ -186,4 +187,33 @@ export class ClassGroupController {
|
|
|
186
187
|
) {
|
|
187
188
|
return this.classGroupService.saveAttendance(id, sessionId, dto.attendance);
|
|
188
189
|
}
|
|
190
|
+
|
|
191
|
+
// ── Materials ───────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
@Get(':id/materials')
|
|
194
|
+
listMaterials(
|
|
195
|
+
@Param('id', ParseIntPipe) id: number,
|
|
196
|
+
@Query('sessionId') sessionId?: string,
|
|
197
|
+
) {
|
|
198
|
+
return this.classGroupService.listMaterials(
|
|
199
|
+
id,
|
|
200
|
+
sessionId ? Number(sessionId) : undefined,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@Post(':id/materials')
|
|
205
|
+
createMaterial(
|
|
206
|
+
@Param('id', ParseIntPipe) id: number,
|
|
207
|
+
@Body() dto: CreateMaterialDto,
|
|
208
|
+
) {
|
|
209
|
+
return this.classGroupService.createMaterial(id, dto);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@Delete(':id/materials/:materialId')
|
|
213
|
+
deleteMaterial(
|
|
214
|
+
@Param('id', ParseIntPipe) id: number,
|
|
215
|
+
@Param('materialId', ParseIntPipe) materialId: number,
|
|
216
|
+
) {
|
|
217
|
+
return this.classGroupService.deleteMaterial(id, materialId);
|
|
218
|
+
}
|
|
189
219
|
}
|
|
@@ -9,9 +9,9 @@ import { Prisma } from '@prisma/client';
|
|
|
9
9
|
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
|
|
10
10
|
import { randomUUID } from 'node:crypto';
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
CreateClassGroupDto,
|
|
13
|
+
type ClassGroupSessionRecurrenceDto,
|
|
14
|
+
type ClassGroupSessionTemplateDto,
|
|
15
15
|
} from './dto/create-class-group.dto';
|
|
16
16
|
import { CreateSessionDto, type SessionRecurrenceDto } from './dto/create-session.dto';
|
|
17
17
|
import { UpdateClassGroupDto } from './dto/update-class-group.dto';
|
|
@@ -73,6 +73,19 @@ export class ClassGroupService {
|
|
|
73
73
|
id: true,
|
|
74
74
|
title: true,
|
|
75
75
|
primary_color: true,
|
|
76
|
+
course_image: {
|
|
77
|
+
where: { image_type: { slug: 'course-logo' } },
|
|
78
|
+
orderBy: { order: 'asc' as const },
|
|
79
|
+
take: 1,
|
|
80
|
+
select: {
|
|
81
|
+
file_id: true,
|
|
82
|
+
file: {
|
|
83
|
+
select: {
|
|
84
|
+
id: true,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
76
89
|
},
|
|
77
90
|
},
|
|
78
91
|
_count: {
|
|
@@ -91,6 +104,7 @@ export class ClassGroupService {
|
|
|
91
104
|
person: {
|
|
92
105
|
select: {
|
|
93
106
|
name: true,
|
|
107
|
+
avatar_id: true,
|
|
94
108
|
},
|
|
95
109
|
},
|
|
96
110
|
},
|
|
@@ -112,6 +126,7 @@ export class ClassGroupService {
|
|
|
112
126
|
person: {
|
|
113
127
|
select: {
|
|
114
128
|
name: true,
|
|
129
|
+
avatar_id: true,
|
|
115
130
|
},
|
|
116
131
|
},
|
|
117
132
|
},
|
|
@@ -550,6 +565,8 @@ export class ClassGroupService {
|
|
|
550
565
|
item.instructorName?.trim?.() ??
|
|
551
566
|
item.professorName?.trim?.() ??
|
|
552
567
|
'';
|
|
568
|
+
const instructorAvatarId = item.instructor?.person?.avatar_id ?? null;
|
|
569
|
+
const courseLogo = item.course?.course_image?.[0] ?? null;
|
|
553
570
|
|
|
554
571
|
return {
|
|
555
572
|
id: item.id,
|
|
@@ -568,11 +585,13 @@ export class ClassGroupService {
|
|
|
568
585
|
virtualRoomUrl: item.virtual_room_url,
|
|
569
586
|
courseId: item.course_id,
|
|
570
587
|
instructorId: item.instructor_id ?? item.instructor?.id ?? null,
|
|
588
|
+
instructorAvatarId,
|
|
571
589
|
instructorName,
|
|
572
590
|
instructor: instructorName,
|
|
573
591
|
professorName: instructorName,
|
|
574
592
|
professor: instructorName,
|
|
575
593
|
courseTitle: item.course?.title ?? '',
|
|
594
|
+
logoFileId: courseLogo?.file?.id ?? courseLogo?.file_id ?? null,
|
|
576
595
|
primaryColor: item.course?.primary_color ?? null,
|
|
577
596
|
enrolledCount: item._count?.course_enrollment ?? 0,
|
|
578
597
|
sessionCount: item._count?.course_class_session ?? 0,
|
|
@@ -658,6 +677,17 @@ export class ClassGroupService {
|
|
|
658
677
|
}
|
|
659
678
|
}
|
|
660
679
|
|
|
680
|
+
private async assertClassGroupExists(classGroupId: number) {
|
|
681
|
+
const exists = await this.prisma.course_class_group.findUnique({
|
|
682
|
+
where: { id: classGroupId },
|
|
683
|
+
select: { id: true },
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
if (!exists) {
|
|
687
|
+
throw new NotFoundException('Class group not found');
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
661
691
|
// ── Students ────────────────────────────────────────────────────────────────
|
|
662
692
|
|
|
663
693
|
async listStudents(classGroupId: number, search?: string) {
|
|
@@ -677,6 +707,7 @@ export class ClassGroupService {
|
|
|
677
707
|
select: {
|
|
678
708
|
id: true,
|
|
679
709
|
name: true,
|
|
710
|
+
avatar_id: true,
|
|
680
711
|
contact: {
|
|
681
712
|
select: { value: true, contact_type: { select: { code: true } } },
|
|
682
713
|
},
|
|
@@ -698,6 +729,7 @@ export class ClassGroupService {
|
|
|
698
729
|
nome: e.person.name,
|
|
699
730
|
email,
|
|
700
731
|
telefone: phone,
|
|
732
|
+
avatarId: e.person.avatar_id,
|
|
701
733
|
matriculadoEm: e.enrolled_at,
|
|
702
734
|
progresso: e.progress_percent,
|
|
703
735
|
status: e.status,
|
|
@@ -728,15 +760,17 @@ export class ClassGroupService {
|
|
|
728
760
|
});
|
|
729
761
|
if (existing) {
|
|
730
762
|
if (existing.status === 'cancelled') {
|
|
731
|
-
|
|
763
|
+
const updated = await this.prisma.course_enrollment.update({
|
|
732
764
|
where: { id: existing.id },
|
|
733
765
|
data: { status: 'active', enrolled_at: new Date() },
|
|
734
766
|
});
|
|
767
|
+
await this.ensureEnterpriseStudent(classGroupId, personId);
|
|
768
|
+
return updated;
|
|
735
769
|
}
|
|
736
770
|
throw new ConflictException('Student already enrolled');
|
|
737
771
|
}
|
|
738
772
|
|
|
739
|
-
|
|
773
|
+
const enrollment = await this.prisma.course_enrollment.create({
|
|
740
774
|
data: {
|
|
741
775
|
person_id: personId,
|
|
742
776
|
course_class_group_id: classGroupId,
|
|
@@ -745,6 +779,28 @@ export class ClassGroupService {
|
|
|
745
779
|
enrolled_at: new Date(),
|
|
746
780
|
},
|
|
747
781
|
});
|
|
782
|
+
await this.ensureEnterpriseStudent(classGroupId, personId);
|
|
783
|
+
return enrollment;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
private async ensureEnterpriseStudent(classGroupId: number, personId: number) {
|
|
787
|
+
const enterpriseLinks = await this.prisma.enterprise_class_group.findMany({
|
|
788
|
+
where: { course_class_group_id: classGroupId },
|
|
789
|
+
select: { enterprise_id: true },
|
|
790
|
+
});
|
|
791
|
+
if (enterpriseLinks.length === 0) return;
|
|
792
|
+
|
|
793
|
+
for (const { enterprise_id } of enterpriseLinks) {
|
|
794
|
+
const exists = await this.prisma.enterprise_student.findFirst({
|
|
795
|
+
where: { enterprise_id, person_id: personId },
|
|
796
|
+
select: { id: true },
|
|
797
|
+
});
|
|
798
|
+
if (!exists) {
|
|
799
|
+
await this.prisma.enterprise_student.create({
|
|
800
|
+
data: { enterprise_id, person_id: personId, status: 'active' },
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
748
804
|
}
|
|
749
805
|
|
|
750
806
|
async createAndEnrollStudent(
|
|
@@ -894,6 +950,7 @@ export class ClassGroupService {
|
|
|
894
950
|
select: {
|
|
895
951
|
id: true,
|
|
896
952
|
name: true,
|
|
953
|
+
avatar_id: true,
|
|
897
954
|
contact: {
|
|
898
955
|
select: {
|
|
899
956
|
id: true,
|
|
@@ -923,6 +980,7 @@ export class ClassGroupService {
|
|
|
923
980
|
nome: enrollment.person.name,
|
|
924
981
|
email,
|
|
925
982
|
telefone: phone,
|
|
983
|
+
avatarId: enrollment.person.avatar_id,
|
|
926
984
|
matriculadoEm: enrollment.enrolled_at,
|
|
927
985
|
progresso: enrollment.progress_percent,
|
|
928
986
|
status: enrollment.status,
|
|
@@ -1120,6 +1178,7 @@ export class ClassGroupService {
|
|
|
1120
1178
|
return {
|
|
1121
1179
|
id: p.id,
|
|
1122
1180
|
nome: p.name,
|
|
1181
|
+
avatarId: p.avatar_id,
|
|
1123
1182
|
email:
|
|
1124
1183
|
p.contact.find((c) => this.isEmailCode(c.contact_type.code))
|
|
1125
1184
|
?.value ?? '',
|
|
@@ -1526,6 +1585,8 @@ export class ClassGroupService {
|
|
|
1526
1585
|
instructorId: sessionInstructor?.instructor_id ?? null,
|
|
1527
1586
|
instructorName:
|
|
1528
1587
|
sessionInstructor?.instructor?.person?.name ?? '',
|
|
1588
|
+
instructorAvatarId:
|
|
1589
|
+
sessionInstructor?.instructor?.person?.avatar_id ?? null,
|
|
1529
1590
|
recurrenceId: s.recurrence_id ?? null,
|
|
1530
1591
|
occurrenceIndex: s.occurrence_index ?? null,
|
|
1531
1592
|
isException: Boolean(s.is_exception),
|
|
@@ -1799,4 +1860,114 @@ export class ClassGroupService {
|
|
|
1799
1860
|
|
|
1800
1861
|
throw error;
|
|
1801
1862
|
}
|
|
1863
|
+
|
|
1864
|
+
// ── Materials ─────────────────────────────────────────────────────────────
|
|
1865
|
+
|
|
1866
|
+
async listMaterials(classGroupId: number, sessionId?: number) {
|
|
1867
|
+
await this.assertClassGroupExists(classGroupId);
|
|
1868
|
+
|
|
1869
|
+
const where: any = { course_class_group_id: classGroupId };
|
|
1870
|
+
if (sessionId !== undefined) {
|
|
1871
|
+
where.course_class_session_id = sessionId;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
return this.prisma.course_class_group_material.findMany({
|
|
1875
|
+
where,
|
|
1876
|
+
include: {
|
|
1877
|
+
file: {
|
|
1878
|
+
select: {
|
|
1879
|
+
id: true,
|
|
1880
|
+
filename: true,
|
|
1881
|
+
path: true,
|
|
1882
|
+
size: true,
|
|
1883
|
+
},
|
|
1884
|
+
},
|
|
1885
|
+
course_class_session: {
|
|
1886
|
+
select: {
|
|
1887
|
+
id: true,
|
|
1888
|
+
title: true,
|
|
1889
|
+
},
|
|
1890
|
+
},
|
|
1891
|
+
},
|
|
1892
|
+
orderBy: [{ sort_order: 'asc' }, { created_at: 'asc' }],
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
async createMaterial(
|
|
1897
|
+
classGroupId: number,
|
|
1898
|
+
dto: import('./dto/material.dto').CreateMaterialDto,
|
|
1899
|
+
) {
|
|
1900
|
+
await this.assertClassGroupExists(classGroupId);
|
|
1901
|
+
|
|
1902
|
+
if (dto.sessionId) {
|
|
1903
|
+
const session = await this.prisma.course_class_session.findFirst({
|
|
1904
|
+
where: { id: dto.sessionId, course_class_group_id: classGroupId },
|
|
1905
|
+
select: { id: true },
|
|
1906
|
+
});
|
|
1907
|
+
if (!session) {
|
|
1908
|
+
throw new NotFoundException('Session not found for this class group');
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
return this.prisma.course_class_group_material.create({
|
|
1913
|
+
data: {
|
|
1914
|
+
course_class_group_id: classGroupId,
|
|
1915
|
+
course_class_session_id: dto.sessionId ?? null,
|
|
1916
|
+
title: dto.title,
|
|
1917
|
+
description: dto.description ?? null,
|
|
1918
|
+
material_type: dto.materialType as any,
|
|
1919
|
+
file_id: dto.fileId ?? null,
|
|
1920
|
+
url: dto.url ?? null,
|
|
1921
|
+
sort_order: dto.sortOrder ?? 0,
|
|
1922
|
+
},
|
|
1923
|
+
include: {
|
|
1924
|
+
file: {
|
|
1925
|
+
select: {
|
|
1926
|
+
id: true,
|
|
1927
|
+
filename: true,
|
|
1928
|
+
path: true,
|
|
1929
|
+
size: true,
|
|
1930
|
+
},
|
|
1931
|
+
},
|
|
1932
|
+
},
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
async deleteMaterial(classGroupId: number, materialId: number) {
|
|
1937
|
+
await this.assertClassGroupExists(classGroupId);
|
|
1938
|
+
|
|
1939
|
+
const material = await this.prisma.course_class_group_material.findFirst({
|
|
1940
|
+
where: { id: materialId, course_class_group_id: classGroupId },
|
|
1941
|
+
select: { id: true },
|
|
1942
|
+
});
|
|
1943
|
+
|
|
1944
|
+
if (!material) {
|
|
1945
|
+
throw new NotFoundException('Material not found');
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
await this.prisma.course_class_group_material.delete({
|
|
1949
|
+
where: { id: materialId },
|
|
1950
|
+
});
|
|
1951
|
+
|
|
1952
|
+
return { success: true };
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
async countMaterialsBySession(classGroupId: number): Promise<Record<number, number>> {
|
|
1956
|
+
const counts = await this.prisma.course_class_group_material.groupBy({
|
|
1957
|
+
by: ['course_class_session_id'],
|
|
1958
|
+
where: {
|
|
1959
|
+
course_class_group_id: classGroupId,
|
|
1960
|
+
course_class_session_id: { not: null },
|
|
1961
|
+
},
|
|
1962
|
+
_count: { id: true },
|
|
1963
|
+
});
|
|
1964
|
+
|
|
1965
|
+
const result: Record<number, number> = {};
|
|
1966
|
+
for (const row of counts) {
|
|
1967
|
+
if (row.course_class_session_id !== null) {
|
|
1968
|
+
result[row.course_class_session_id] = row._count.id;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
return result;
|
|
1972
|
+
}
|
|
1802
1973
|
}
|