@hed-hog/lms 0.0.319 → 0.0.320

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 (128) hide show
  1. package/dist/class-group/class-group.controller.d.ts +65 -2
  2. package/dist/class-group/class-group.controller.d.ts.map +1 -1
  3. package/dist/class-group/class-group.controller.js +35 -0
  4. package/dist/class-group/class-group.controller.js.map +1 -1
  5. package/dist/class-group/class-group.service.d.ts +67 -2
  6. package/dist/class-group/class-group.service.d.ts.map +1 -1
  7. package/dist/class-group/class-group.service.js +164 -13
  8. package/dist/class-group/class-group.service.js.map +1 -1
  9. package/dist/class-group/dto/create-class-group.dto.d.ts.map +1 -1
  10. package/dist/class-group/dto/create-class-group.dto.js +2 -1
  11. package/dist/class-group/dto/create-class-group.dto.js.map +1 -1
  12. package/dist/class-group/dto/material.dto.d.ts +18 -0
  13. package/dist/class-group/dto/material.dto.d.ts.map +1 -0
  14. package/dist/class-group/dto/material.dto.js +86 -0
  15. package/dist/class-group/dto/material.dto.js.map +1 -0
  16. package/dist/course/course.service.d.ts +2 -0
  17. package/dist/course/course.service.d.ts.map +1 -1
  18. package/dist/course/course.service.js +27 -2
  19. package/dist/course/course.service.js.map +1 -1
  20. package/dist/course/dto/create-course.dto.d.ts +2 -2
  21. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  22. package/dist/course/dto/create-course.dto.js.map +1 -1
  23. package/dist/enterprise/enterprise.controller.d.ts +7 -1
  24. package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
  25. package/dist/enterprise/enterprise.controller.js +72 -2
  26. package/dist/enterprise/enterprise.controller.js.map +1 -1
  27. package/dist/enterprise/enterprise.module.d.ts.map +1 -1
  28. package/dist/enterprise/enterprise.module.js +2 -1
  29. package/dist/enterprise/enterprise.module.js.map +1 -1
  30. package/dist/enterprise/enterprise.service.d.ts +3 -0
  31. package/dist/enterprise/enterprise.service.d.ts.map +1 -1
  32. package/dist/enterprise/enterprise.service.js +84 -1
  33. package/dist/enterprise/enterprise.service.js.map +1 -1
  34. package/dist/enterprise/training/enterprise-training.module.d.ts +3 -0
  35. package/dist/enterprise/training/enterprise-training.module.d.ts.map +1 -0
  36. package/dist/enterprise/training/enterprise-training.module.js +40 -0
  37. package/dist/enterprise/training/enterprise-training.module.js.map +1 -0
  38. package/dist/enterprise/training/training-admin.controller.d.ts +525 -0
  39. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -0
  40. package/dist/enterprise/training/training-admin.controller.js +385 -0
  41. package/dist/enterprise/training/training-admin.controller.js.map +1 -0
  42. package/dist/enterprise/training/training-admin.service.d.ts +582 -0
  43. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -0
  44. package/dist/enterprise/training/training-admin.service.js +2283 -0
  45. package/dist/enterprise/training/training-admin.service.js.map +1 -0
  46. package/dist/enterprise/training/training-instructor.controller.d.ts +260 -0
  47. package/dist/enterprise/training/training-instructor.controller.d.ts.map +1 -0
  48. package/dist/enterprise/training/training-instructor.controller.js +199 -0
  49. package/dist/enterprise/training/training-instructor.controller.js.map +1 -0
  50. package/dist/enterprise/training/training-instructor.service.d.ts +280 -0
  51. package/dist/enterprise/training/training-instructor.service.d.ts.map +1 -0
  52. package/dist/enterprise/training/training-instructor.service.js +1218 -0
  53. package/dist/enterprise/training/training-instructor.service.js.map +1 -0
  54. package/dist/enterprise/training/training-student.controller.d.ts +168 -0
  55. package/dist/enterprise/training/training-student.controller.d.ts.map +1 -0
  56. package/dist/enterprise/training/training-student.controller.js +104 -0
  57. package/dist/enterprise/training/training-student.controller.js.map +1 -0
  58. package/dist/enterprise/training/training-student.service.d.ts +185 -0
  59. package/dist/enterprise/training/training-student.service.d.ts.map +1 -0
  60. package/dist/enterprise/training/training-student.service.js +674 -0
  61. package/dist/enterprise/training/training-student.service.js.map +1 -0
  62. package/dist/enterprise/training/training-viewer.controller.d.ts +298 -0
  63. package/dist/enterprise/training/training-viewer.controller.d.ts.map +1 -0
  64. package/dist/enterprise/training/training-viewer.controller.js +178 -0
  65. package/dist/enterprise/training/training-viewer.controller.js.map +1 -0
  66. package/dist/evaluation/dto/create-evaluation-topic.dto.d.ts +18 -0
  67. package/dist/evaluation/dto/create-evaluation-topic.dto.d.ts.map +1 -0
  68. package/dist/evaluation/dto/create-evaluation-topic.dto.js +59 -0
  69. package/dist/evaluation/dto/create-evaluation-topic.dto.js.map +1 -0
  70. package/dist/evaluation/dto/update-evaluation-topic.dto.d.ts +6 -0
  71. package/dist/evaluation/dto/update-evaluation-topic.dto.d.ts.map +1 -0
  72. package/dist/evaluation/dto/update-evaluation-topic.dto.js +9 -0
  73. package/dist/evaluation/dto/update-evaluation-topic.dto.js.map +1 -0
  74. package/dist/evaluation/evaluation.controller.d.ts +66 -0
  75. package/dist/evaluation/evaluation.controller.d.ts.map +1 -1
  76. package/dist/evaluation/evaluation.controller.js +73 -0
  77. package/dist/evaluation/evaluation.controller.js.map +1 -1
  78. package/dist/evaluation/evaluation.service.d.ts +71 -0
  79. package/dist/evaluation/evaluation.service.d.ts.map +1 -1
  80. package/dist/evaluation/evaluation.service.js +121 -0
  81. package/dist/evaluation/evaluation.service.js.map +1 -1
  82. package/dist/instructor/instructor.service.js +6 -6
  83. package/dist/instructor/instructor.service.js.map +1 -1
  84. package/dist/lms.module.d.ts.map +1 -1
  85. package/dist/lms.module.js +3 -0
  86. package/dist/lms.module.js.map +1 -1
  87. package/hedhog/data/menu.yaml +19 -2
  88. package/hedhog/data/route.yaml +730 -0
  89. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +74 -8
  90. package/hedhog/frontend/app/_components/course-avatar.tsx.ejs +27 -47
  91. package/hedhog/frontend/app/classes/[id]/_components/event-summary-popover.tsx.ejs +15 -15
  92. package/hedhog/frontend/app/classes/[id]/_components/quick-create-session-popover.tsx.ejs +5 -5
  93. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +2141 -308
  94. package/hedhog/frontend/app/classes/page.tsx.ejs +8 -7
  95. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +21 -8
  96. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +10 -6
  97. package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +201 -0
  98. package/hedhog/frontend/app/evaluations/_components/evaluation-topic-types.ts.ejs +49 -0
  99. package/hedhog/frontend/app/evaluations/page.tsx.ejs +621 -1250
  100. package/hedhog/frontend/app/instructors/page.tsx.ejs +22 -20
  101. package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +1278 -0
  102. package/hedhog/frontend/messages/en.json +98 -7
  103. package/hedhog/frontend/messages/pt.json +98 -7
  104. package/hedhog/table/course_class_group_material.yaml +45 -0
  105. package/package.json +8 -8
  106. package/src/class-group/class-group.controller.ts +30 -0
  107. package/src/class-group/class-group.service.ts +176 -5
  108. package/src/class-group/dto/create-class-group.dto.ts +8 -8
  109. package/src/class-group/dto/material.dto.ts +69 -0
  110. package/src/course/course.service.ts +54 -21
  111. package/src/course/dto/create-course.dto.ts +8 -8
  112. package/src/enterprise/enterprise.controller.ts +62 -1
  113. package/src/enterprise/enterprise.module.ts +2 -1
  114. package/src/enterprise/enterprise.service.ts +84 -1
  115. package/src/enterprise/training/enterprise-training.module.ts +27 -0
  116. package/src/enterprise/training/training-admin.controller.ts +278 -0
  117. package/src/enterprise/training/training-admin.service.ts +2523 -0
  118. package/src/enterprise/training/training-instructor.controller.ts +141 -0
  119. package/src/enterprise/training/training-instructor.service.ts +1303 -0
  120. package/src/enterprise/training/training-student.controller.ts +65 -0
  121. package/src/enterprise/training/training-student.service.ts +762 -0
  122. package/src/enterprise/training/training-viewer.controller.ts +115 -0
  123. package/src/evaluation/dto/create-evaluation-topic.dto.ts +48 -0
  124. package/src/evaluation/dto/update-evaluation-topic.dto.ts +6 -0
  125. package/src/evaluation/evaluation.controller.ts +63 -1
  126. package/src/evaluation/evaluation.service.ts +150 -1
  127. package/src/instructor/instructor.service.ts +4 -4
  128. package/src/lms.module.ts +3 -0
@@ -1488,7 +1488,8 @@ export default function TurmasPage() {
1488
1488
 
1489
1489
  try {
1490
1490
  const payload = {
1491
- slug: data.nomeInterno.trim(),
1491
+ name: data.nomeInterno.trim(),
1492
+ slug: data.slug.trim().toLowerCase(),
1492
1493
  title: data.tituloComercial,
1493
1494
  description: data.descricao,
1494
1495
  level: toApiCourseLevel(data.nivel),
@@ -2628,7 +2629,7 @@ export default function TurmasPage() {
2628
2629
  <FieldLabel>
2629
2630
  {t('form.recurrence.customDialog.repeatOn')}
2630
2631
  </FieldLabel>
2631
- <div className="flex flex-wrap gap-2">
2632
+ <div className="grid grid-cols-4 gap-2 sm:grid-cols-7">
2632
2633
  {recurrenceDayOptions.map((day) => {
2633
2634
  const active = (
2634
2635
  watchedFormValues.sessionRecurrenceDaysOfWeek ?? []
@@ -2639,8 +2640,8 @@ export default function TurmasPage() {
2639
2640
  key={day.value}
2640
2641
  type="button"
2641
2642
  variant={active ? 'default' : 'outline'}
2642
- size="icon"
2643
- className="rounded-full"
2643
+ size="sm"
2644
+ className="h-9"
2644
2645
  onClick={() => toggleCustomRecurrenceDay(day.value)}
2645
2646
  >
2646
2647
  {day.label}
@@ -3106,7 +3107,7 @@ export default function TurmasPage() {
3106
3107
  <FieldLabel>
3107
3108
  {t('form.recurrence.customDialog.repeatOn')}
3108
3109
  </FieldLabel>
3109
- <div className="flex flex-wrap gap-2">
3110
+ <div className="grid grid-cols-4 gap-2 sm:grid-cols-7">
3110
3111
  {recurrenceDayOptions.map((day) => {
3111
3112
  const active = (
3112
3113
  watchedFormValues.sessionRecurrenceDaysOfWeek ?? []
@@ -3117,8 +3118,8 @@ export default function TurmasPage() {
3117
3118
  key={day.value}
3118
3119
  type="button"
3119
3120
  variant={active ? 'default' : 'outline'}
3120
- size="icon"
3121
- className="rounded-full"
3121
+ size="sm"
3122
+ className="h-9"
3122
3123
  onClick={() => toggleCustomRecurrenceDay(day.value)}
3123
3124
  >
3124
3125
  {day.label}
@@ -40,8 +40,9 @@ import {
40
40
  ShortcutsHelpTrigger,
41
41
  } from './structure/_components/shortcuts-help';
42
42
  import { useStructureStore } from './structure/_components/store';
43
- import { TreeDisplaySettingsPopover } from './structure/_components/tree-display-settings-popover';
44
- import { useCourseStructureShortcuts } from './structure/_components/use-course-structure-shortcuts';
43
+ import { TreeDisplaySettingsPopover } from './structure/_components/tree-display-settings-popover';
44
+ import { useCourseStructureShortcuts } from './structure/_components/use-course-structure-shortcuts';
45
+ import { CourseAvatar } from '../../_components/course-avatar';
45
46
  import {
46
47
  useDuplicateLessonMutation,
47
48
  useDuplicateSessionMutation,
@@ -54,11 +55,13 @@ interface Props {
54
55
  params: Promise<{ id: string }>;
55
56
  }
56
57
 
57
- type ApiCourseSummary = {
58
- name?: string;
59
- enrollmentCount?: number;
60
- averageCompletion?: number;
61
- lessonCount?: number;
58
+ type ApiCourseSummary = {
59
+ name?: string;
60
+ title?: string;
61
+ logoFileId?: number | null;
62
+ enrollmentCount?: number;
63
+ averageCompletion?: number;
64
+ lessonCount?: number;
62
65
  sessionCount?: number;
63
66
  };
64
67
 
@@ -260,7 +263,17 @@ export default function CourseStructurePage({ params }: Props) {
260
263
  { label: 'Cursos', href: '/lms/courses' },
261
264
  { label: course.name },
262
265
  ]}
263
- title={course.name}
266
+ title={
267
+ <span className="flex min-w-0 items-center gap-3">
268
+ <CourseAvatar
269
+ fileId={courseSummary?.logoFileId}
270
+ title={courseSummary?.title ?? course.name}
271
+ className="size-10 rounded-lg"
272
+ iconSize="size-5"
273
+ />
274
+ <span className="min-w-0 truncate">{course.name}</span>
275
+ </span>
276
+ }
264
277
  extraContent={metricsBadges}
265
278
  actions={
266
279
  <>
@@ -843,12 +843,16 @@ export function EditorCourse() {
843
843
  onValueChange={setActiveTab}
844
844
  className="flex flex-col flex-1 min-h-0"
845
845
  >
846
- <TabsList>
847
- {(['estrutura', 'sobre', 'midia', 'extra'] as const).map((tab) => (
848
- <TabsTrigger key={tab} value={tab}>
849
- {tab === 'estrutura'
850
- ? 'Estrutura'
851
- : tab === 'sobre'
846
+ <TabsList className="mx-3 mt-3 h-auto shrink-0 grid w-[calc(100%-1.5rem)] grid-cols-4 rounded-lg bg-muted/80 p-1">
847
+ {(['estrutura', 'sobre', 'midia', 'extra'] as const).map((tab) => (
848
+ <TabsTrigger
849
+ key={tab}
850
+ value={tab}
851
+ className="h-8 px-2 text-xs font-medium"
852
+ >
853
+ {tab === 'estrutura'
854
+ ? 'Estrutura'
855
+ : tab === 'sobre'
852
856
  ? 'Sobre'
853
857
  : tab === 'midia'
854
858
  ? 'Mídia'
@@ -0,0 +1,201 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import { Input } from '@/components/ui/input';
5
+ import { Label } from '@/components/ui/label';
6
+ import {
7
+ Select,
8
+ SelectContent,
9
+ SelectItem,
10
+ SelectTrigger,
11
+ SelectValue,
12
+ } from '@/components/ui/select';
13
+ import {
14
+ Sheet,
15
+ SheetContent,
16
+ SheetFooter,
17
+ SheetHeader,
18
+ SheetTitle,
19
+ } from '@/components/ui/sheet';
20
+ import { Switch } from '@/components/ui/switch';
21
+ import { Textarea } from '@/components/ui/textarea';
22
+ import { useApp } from '@hed-hog/next-app-provider';
23
+ import { useTranslations } from 'next-intl';
24
+ import { useEffect, useState } from 'react';
25
+ import { toast } from 'sonner';
26
+ import type {
27
+ EvaluationTargetType,
28
+ EvaluationTopicItem,
29
+ } from './evaluation-topic-types';
30
+ import { TARGET_TYPE_OPTIONS } from './evaluation-topic-types';
31
+
32
+ interface EvaluationTopicFormSheetProps {
33
+ open: boolean;
34
+ onOpenChange: (open: boolean) => void;
35
+ editingItem: EvaluationTopicItem | null;
36
+ onSuccess: () => void;
37
+ }
38
+
39
+ export function EvaluationTopicFormSheet({
40
+ open,
41
+ onOpenChange,
42
+ editingItem,
43
+ onSuccess,
44
+ }: EvaluationTopicFormSheetProps) {
45
+ const t = useTranslations('lms.EvaluationTopicsPage');
46
+ const { request } = useApp();
47
+
48
+ const [name, setName] = useState('');
49
+ const [description, setDescription] = useState('');
50
+ const [targetType, setTargetType] = useState<EvaluationTargetType>('course');
51
+ const [isActive, setIsActive] = useState(true);
52
+ const [saving, setSaving] = useState(false);
53
+
54
+ useEffect(() => {
55
+ if (open) {
56
+ if (editingItem) {
57
+ setName(editingItem.name);
58
+ setDescription(editingItem.description ?? '');
59
+ setTargetType(editingItem.targetType);
60
+ setIsActive(editingItem.isActive);
61
+ } else {
62
+ setName('');
63
+ setDescription('');
64
+ setTargetType('course');
65
+ setIsActive(true);
66
+ }
67
+ }
68
+ }, [open, editingItem]);
69
+
70
+ async function handleSubmit(e: React.FormEvent) {
71
+ e.preventDefault();
72
+ if (!name.trim()) return;
73
+
74
+ setSaving(true);
75
+ try {
76
+ const body: Record<string, unknown> = {
77
+ name: name.trim(),
78
+ description: description.trim() || null,
79
+ targetType,
80
+ isActive,
81
+ };
82
+
83
+ if (editingItem) {
84
+ await request({
85
+ url: `/lms/evaluations/topics/${editingItem.id}`,
86
+ method: 'PATCH',
87
+ data: body,
88
+ });
89
+ toast.success(t('form.updatedSuccess'));
90
+ } else {
91
+ await request({
92
+ url: '/lms/evaluations/topics',
93
+ method: 'POST',
94
+ data: body,
95
+ });
96
+ toast.success(t('form.createdSuccess'));
97
+ }
98
+ onSuccess();
99
+ onOpenChange(false);
100
+ } catch {
101
+ toast.error(t('form.saveError'));
102
+ } finally {
103
+ setSaving(false);
104
+ }
105
+ }
106
+
107
+ return (
108
+ <Sheet open={open} onOpenChange={onOpenChange}>
109
+ <SheetContent side="right" className="w-full sm:max-w-md">
110
+ <SheetHeader>
111
+ <SheetTitle>
112
+ {editingItem ? t('form.editTitle') : t('form.createTitle')}
113
+ </SheetTitle>
114
+ </SheetHeader>
115
+
116
+ <form onSubmit={handleSubmit} className="mt-6 flex flex-col gap-5 px-2">
117
+ {/* Name */}
118
+ <div className="flex flex-col gap-1.5">
119
+ <Label htmlFor="topic-name">{t('form.name')}</Label>
120
+ <Input
121
+ id="topic-name"
122
+ value={name}
123
+ onChange={(e) => setName(e.target.value)}
124
+ placeholder={t('form.namePlaceholder')}
125
+ required
126
+ maxLength={255}
127
+ />
128
+ </div>
129
+
130
+ {/* Description */}
131
+ <div className="flex flex-col gap-1.5">
132
+ <Label htmlFor="topic-description">{t('form.description')}</Label>
133
+ <Textarea
134
+ id="topic-description"
135
+ value={description}
136
+ onChange={(e) => setDescription(e.target.value)}
137
+ placeholder={t('form.descriptionPlaceholder')}
138
+ rows={3}
139
+ className="resize-none"
140
+ />
141
+ </div>
142
+
143
+ {/* Target Type */}
144
+ <div className="flex flex-col gap-1.5">
145
+ <Label>{t('form.targetType')}</Label>
146
+ <Select
147
+ value={targetType}
148
+ onValueChange={(v) => setTargetType(v as EvaluationTargetType)}
149
+ disabled={!!editingItem}
150
+ >
151
+ <SelectTrigger className="w-full">
152
+ <SelectValue />
153
+ </SelectTrigger>
154
+ <SelectContent>
155
+ {TARGET_TYPE_OPTIONS.map((opt) => (
156
+ <SelectItem key={opt.value} value={opt.value}>
157
+ {t(`targetType.${opt.labelKey}`)}
158
+ </SelectItem>
159
+ ))}
160
+ </SelectContent>
161
+ </Select>
162
+ {editingItem && (
163
+ <p className="text-xs text-muted-foreground">
164
+ {t('form.targetTypeReadonly')}
165
+ </p>
166
+ )}
167
+ </div>
168
+
169
+ {/* Is Active */}
170
+ <div className="flex items-center justify-between rounded-lg border border-border/50 px-4 py-3">
171
+ <div>
172
+ <p className="text-sm font-medium">{t('form.isActive')}</p>
173
+ <p className="text-xs text-muted-foreground">
174
+ {t('form.isActiveDesc')}
175
+ </p>
176
+ </div>
177
+ <Switch checked={isActive} onCheckedChange={setIsActive} />
178
+ </div>
179
+
180
+ <SheetFooter className="mt-2 flex gap-2">
181
+ <Button
182
+ type="button"
183
+ variant="outline"
184
+ onClick={() => onOpenChange(false)}
185
+ disabled={saving}
186
+ >
187
+ {t('form.cancel')}
188
+ </Button>
189
+ <Button type="submit" disabled={saving || !name.trim()}>
190
+ {saving
191
+ ? t('form.saving')
192
+ : editingItem
193
+ ? t('form.save')
194
+ : t('form.create')}
195
+ </Button>
196
+ </SheetFooter>
197
+ </form>
198
+ </SheetContent>
199
+ </Sheet>
200
+ );
201
+ }
@@ -0,0 +1,49 @@
1
+ export type EvaluationTargetType =
2
+ | 'course'
3
+ | 'course_lesson'
4
+ | 'course_class_session'
5
+ | 'question'
6
+ | 'exam';
7
+
8
+ export interface EvaluationTopicItem {
9
+ id: number;
10
+ name: string;
11
+ description: string | null;
12
+ targetType: EvaluationTargetType;
13
+ order: number | null;
14
+ isActive: boolean;
15
+ ratingCount: number;
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ }
19
+
20
+ export type ApiEvaluationTopicList = {
21
+ data: EvaluationTopicItem[];
22
+ total: number;
23
+ page: number;
24
+ pageSize: number;
25
+ lastPage: number;
26
+ };
27
+
28
+ export const TARGET_TYPE_OPTIONS: {
29
+ value: EvaluationTargetType;
30
+ labelKey: string;
31
+ }[] = [
32
+ { value: 'course', labelKey: 'course' },
33
+ { value: 'course_lesson', labelKey: 'course_lesson' },
34
+ { value: 'course_class_session', labelKey: 'course_class_session' },
35
+ { value: 'question', labelKey: 'question' },
36
+ { value: 'exam', labelKey: 'exam' },
37
+ ];
38
+
39
+ export const TARGET_TYPE_COLORS: Record<EvaluationTargetType, string> = {
40
+ course:
41
+ 'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-950 dark:text-blue-300 dark:border-blue-800',
42
+ course_lesson:
43
+ 'bg-green-100 text-green-700 border-green-200 dark:bg-green-950 dark:text-green-300 dark:border-green-800',
44
+ course_class_session:
45
+ 'bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-950 dark:text-purple-300 dark:border-purple-800',
46
+ question:
47
+ 'bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-950 dark:text-orange-300 dark:border-orange-800',
48
+ exam: 'bg-red-100 text-red-700 border-red-200 dark:bg-red-950 dark:text-red-300 dark:border-red-800',
49
+ };