@hed-hog/lms 0.0.338 → 0.0.349
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 +8 -8
- package/dist/class-group/class-group.service.d.ts +8 -8
- package/dist/course/course.controller.d.ts +6 -1
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +19 -2
- package/dist/course/course.controller.js.map +1 -1
- package/dist/course/course.service.d.ts +6 -0
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +51 -8
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course.dto.d.ts +1 -0
- package/dist/course/dto/create-course.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course.dto.js +5 -0
- package/dist/course/dto/create-course.dto.js.map +1 -1
- package/dist/enterprise/enterprise.controller.d.ts +12 -12
- package/dist/enterprise/enterprise.service.d.ts +12 -12
- package/dist/enterprise/training/training-admin.controller.d.ts +5 -5
- package/dist/enterprise/training/training-admin.service.d.ts +5 -5
- package/dist/evaluation/evaluation.controller.d.ts +2 -2
- package/dist/evaluation/evaluation.service.d.ts +2 -2
- package/dist/instructor/instructor.controller.d.ts +1 -0
- package/dist/instructor/instructor.controller.d.ts.map +1 -1
- package/dist/instructor/instructor.service.d.ts +2 -0
- package/dist/instructor/instructor.service.d.ts.map +1 -1
- package/dist/instructor/instructor.service.js +9 -7
- package/dist/instructor/instructor.service.js.map +1 -1
- package/dist/training/dto/create-training.dto.d.ts +1 -0
- package/dist/training/dto/create-training.dto.d.ts.map +1 -1
- package/dist/training/dto/create-training.dto.js +5 -0
- package/dist/training/dto/create-training.dto.js.map +1 -1
- package/dist/training/training.controller.d.ts +4 -0
- package/dist/training/training.controller.d.ts.map +1 -1
- package/dist/training/training.service.d.ts +8 -0
- package/dist/training/training.service.d.ts.map +1 -1
- package/dist/training/training.service.js +71 -6
- package/dist/training/training.service.js.map +1 -1
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +38 -9
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +33 -6
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +1 -3
- package/hedhog/frontend/app/classes/page.tsx.ejs +28 -6
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +1 -1
- package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +25 -18
- package/hedhog/frontend/app/paths/page.tsx.ejs +68 -5
- package/hedhog/frontend/app/training/page.tsx.ejs +70 -6
- package/hedhog/frontend/messages/pt.json +14 -1
- package/hedhog/table/learning_path.yaml +4 -0
- package/package.json +8 -8
- package/src/course/course.controller.ts +18 -0
- package/src/course/course.service.ts +73 -2
- package/src/course/dto/create-course.dto.ts +4 -0
- package/src/instructor/instructor.service.ts +2 -0
- package/src/training/dto/create-training.dto.ts +4 -0
- package/src/training/training.service.ts +104 -5
|
@@ -589,6 +589,7 @@ export default function QuestoesPage() {
|
|
|
589
589
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
590
590
|
const [questaoToDelete, setQuestaoToDelete] = useState<Questao | null>(null);
|
|
591
591
|
const [hasChanges, setHasChanges] = useState(false);
|
|
592
|
+
const [saving, setSaving] = useState(false);
|
|
592
593
|
const [buscaInput, setBuscaInput] = useState('');
|
|
593
594
|
const [debouncedBuscaInput, setDebouncedBuscaInput] = useState('');
|
|
594
595
|
const [filtroTipoInput, setFiltroTipoInput] = useState<
|
|
@@ -993,24 +994,29 @@ export default function QuestoesPage() {
|
|
|
993
994
|
}),
|
|
994
995
|
};
|
|
995
996
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
997
|
+
setSaving(true);
|
|
998
|
+
try {
|
|
999
|
+
if (editingQuestao) {
|
|
1000
|
+
await request({
|
|
1001
|
+
url: `/lms/exams/${examId}/questions/${editingQuestao.id}`,
|
|
1002
|
+
method: 'PATCH',
|
|
1003
|
+
data: payload,
|
|
1004
|
+
});
|
|
1005
|
+
toast.success(t('toasts.questionUpdated'));
|
|
1006
|
+
} else {
|
|
1007
|
+
await request({
|
|
1008
|
+
url: `/lms/exams/${examId}/questions`,
|
|
1009
|
+
method: 'POST',
|
|
1010
|
+
data: payload,
|
|
1011
|
+
});
|
|
1012
|
+
toast.success(t('toasts.questionCreated'));
|
|
1013
|
+
}
|
|
1011
1014
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1015
|
+
await refetchQuestions();
|
|
1016
|
+
setSheetOpen(false);
|
|
1017
|
+
} finally {
|
|
1018
|
+
setSaving(false);
|
|
1019
|
+
}
|
|
1014
1020
|
}
|
|
1015
1021
|
|
|
1016
1022
|
async function confirmDelete() {
|
|
@@ -1801,7 +1807,8 @@ export default function QuestoesPage() {
|
|
|
1801
1807
|
)}
|
|
1802
1808
|
|
|
1803
1809
|
<SheetFooter className="mt-auto gap-2 p-0 pt-4">
|
|
1804
|
-
<Button type="submit" className="w-full">
|
|
1810
|
+
<Button type="submit" className="w-full" disabled={saving}>
|
|
1811
|
+
{saving && <Loader2 className="mr-2 size-4 animate-spin" />}
|
|
1805
1812
|
{editingQuestao
|
|
1806
1813
|
? t('sheet.actions.update')
|
|
1807
1814
|
: t('sheet.actions.create')}
|
|
@@ -64,6 +64,7 @@ import {
|
|
|
64
64
|
TableRow,
|
|
65
65
|
} from '@/components/ui/table';
|
|
66
66
|
import { Textarea } from '@/components/ui/textarea';
|
|
67
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
67
68
|
import { usePersistedViewMode } from '@/hooks/use-persisted-view-mode';
|
|
68
69
|
import {
|
|
69
70
|
DndContext,
|
|
@@ -103,7 +104,6 @@ import {
|
|
|
103
104
|
import { useTranslations } from 'next-intl';
|
|
104
105
|
import { useRouter } from 'next/navigation';
|
|
105
106
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
106
|
-
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
107
107
|
import { Controller, useForm, useWatch } from 'react-hook-form';
|
|
108
108
|
import { toast } from 'sonner';
|
|
109
109
|
import { z } from 'zod';
|
|
@@ -114,6 +114,7 @@ interface Formacao {
|
|
|
114
114
|
id: number;
|
|
115
115
|
nome: string;
|
|
116
116
|
descricao: string;
|
|
117
|
+
progressionMode: 'sequencial' | 'livre';
|
|
117
118
|
area: string;
|
|
118
119
|
nivel: string;
|
|
119
120
|
prerequisitos: string;
|
|
@@ -135,6 +136,9 @@ type TrainingColorPayload = {
|
|
|
135
136
|
secondaryColor?: string | null;
|
|
136
137
|
primary_color?: string | null;
|
|
137
138
|
secondary_color?: string | null;
|
|
139
|
+
progressionMode?: string | null;
|
|
140
|
+
progressMode?: string | null;
|
|
141
|
+
progress_mode?: string | null;
|
|
138
142
|
};
|
|
139
143
|
|
|
140
144
|
interface CursoOption {
|
|
@@ -340,6 +344,14 @@ function normalizeLevelValue(value: string) {
|
|
|
340
344
|
return 'Iniciante';
|
|
341
345
|
}
|
|
342
346
|
|
|
347
|
+
function normalizeProgressModeValue(value?: string) {
|
|
348
|
+
const normalized = normalizeText(value ?? '');
|
|
349
|
+
|
|
350
|
+
if (['livre', 'free'].includes(normalized)) return 'livre';
|
|
351
|
+
|
|
352
|
+
return 'sequencial';
|
|
353
|
+
}
|
|
354
|
+
|
|
343
355
|
function normalizeStatusValue(value: string) {
|
|
344
356
|
const normalized = normalizeText(value);
|
|
345
357
|
|
|
@@ -391,6 +403,12 @@ function normalizeTrainingColorPayload<T extends TrainingColorPayload>(
|
|
|
391
403
|
|
|
392
404
|
return {
|
|
393
405
|
...payload,
|
|
406
|
+
progressionMode: normalizeProgressModeValue(
|
|
407
|
+
payload.progressionMode ??
|
|
408
|
+
payload.progressMode ??
|
|
409
|
+
payload.progress_mode ??
|
|
410
|
+
undefined
|
|
411
|
+
),
|
|
394
412
|
primaryColor: primaryColor ?? undefined,
|
|
395
413
|
secondaryColor: secondaryColor ?? undefined,
|
|
396
414
|
};
|
|
@@ -422,7 +440,8 @@ function buildCourseCodeFromTitle(title: string) {
|
|
|
422
440
|
|
|
423
441
|
const formacaoSchema = z.object({
|
|
424
442
|
nome: z.string().min(3, 'Minimo 3 caracteres'),
|
|
425
|
-
descricao: z.string().
|
|
443
|
+
descricao: z.string().optional(),
|
|
444
|
+
progressionMode: z.enum(['sequencial', 'livre']),
|
|
426
445
|
area: z.enum(['Tecnologia', 'Design', 'Gestao', 'Marketing', 'Financas']),
|
|
427
446
|
nivel: z.enum(['Iniciante', 'Intermediario', 'Avancado']),
|
|
428
447
|
prerequisitos: z.string().optional(),
|
|
@@ -728,6 +747,7 @@ export default function TrainingPage() {
|
|
|
728
747
|
defaultValues: {
|
|
729
748
|
nome: '',
|
|
730
749
|
descricao: '',
|
|
750
|
+
progressionMode: 'sequencial' as const,
|
|
731
751
|
area: 'Tecnologia',
|
|
732
752
|
nivel: 'Iniciante',
|
|
733
753
|
prerequisitos: '',
|
|
@@ -967,6 +987,7 @@ export default function TrainingPage() {
|
|
|
967
987
|
form.reset({
|
|
968
988
|
nome: '',
|
|
969
989
|
descricao: '',
|
|
990
|
+
progressionMode: 'sequencial',
|
|
970
991
|
area: 'Tecnologia',
|
|
971
992
|
nivel: 'Iniciante',
|
|
972
993
|
prerequisitos: '',
|
|
@@ -1022,6 +1043,9 @@ export default function TrainingPage() {
|
|
|
1022
1043
|
form.reset({
|
|
1023
1044
|
nome: fullFormacao.nome,
|
|
1024
1045
|
descricao: fullFormacao.descricao,
|
|
1046
|
+
progressionMode: normalizeProgressModeValue(
|
|
1047
|
+
fullFormacao.progressionMode
|
|
1048
|
+
),
|
|
1025
1049
|
area: normalizeAreaValue(fullFormacao.area),
|
|
1026
1050
|
nivel: normalizeLevelValue(fullFormacao.nivel),
|
|
1027
1051
|
prerequisitos: fullFormacao.prerequisitos,
|
|
@@ -1171,6 +1195,9 @@ export default function TrainingPage() {
|
|
|
1171
1195
|
}
|
|
1172
1196
|
|
|
1173
1197
|
async function onSubmit(data: FormacaoForm) {
|
|
1198
|
+
const toApiProgressMode = (value: FormacaoForm['progressionMode']) =>
|
|
1199
|
+
value === 'livre' ? 'free' : 'sequential';
|
|
1200
|
+
|
|
1174
1201
|
try {
|
|
1175
1202
|
const orderedItems = normalizeTrailOrder(
|
|
1176
1203
|
[...learningPathItems].sort((a, b) => a.order - b.order)
|
|
@@ -1196,6 +1223,7 @@ export default function TrainingPage() {
|
|
|
1196
1223
|
shortDescription?: string;
|
|
1197
1224
|
level?: 'beginner' | 'intermediate' | 'advanced';
|
|
1198
1225
|
status?: 'draft' | 'active' | 'archived';
|
|
1226
|
+
progressMode?: 'sequential' | 'free';
|
|
1199
1227
|
primaryColor?: string;
|
|
1200
1228
|
secondaryColor?: string;
|
|
1201
1229
|
items?: Array<{
|
|
@@ -1215,6 +1243,7 @@ export default function TrainingPage() {
|
|
|
1215
1243
|
if (dirty.status) {
|
|
1216
1244
|
payload.status = ptStatusToApi(data.status as Formacao['status']);
|
|
1217
1245
|
}
|
|
1246
|
+
payload.progressMode = toApiProgressMode(data.progressionMode);
|
|
1218
1247
|
if (dirty.primaryColor) payload.primaryColor = data.primaryColor;
|
|
1219
1248
|
if (dirty.secondaryColor) payload.secondaryColor = data.secondaryColor;
|
|
1220
1249
|
if (itemsChanged) {
|
|
@@ -1244,6 +1273,7 @@ export default function TrainingPage() {
|
|
|
1244
1273
|
shortDescription: data.prerequisitos?.trim() || undefined,
|
|
1245
1274
|
level: ptLevelToApi(data.nivel),
|
|
1246
1275
|
status: ptStatusToApi(data.status as Formacao['status']),
|
|
1276
|
+
progressMode: toApiProgressMode(data.progressionMode),
|
|
1247
1277
|
primaryColor: data.primaryColor,
|
|
1248
1278
|
secondaryColor: data.secondaryColor,
|
|
1249
1279
|
items: orderedItems.map((item, index) => ({
|
|
@@ -1899,8 +1929,7 @@ export default function TrainingPage() {
|
|
|
1899
1929
|
</Field>
|
|
1900
1930
|
<Field>
|
|
1901
1931
|
<FieldLabel htmlFor="descricao">
|
|
1902
|
-
{t('form.fields.description.label')}
|
|
1903
|
-
<span className="text-destructive">*</span>
|
|
1932
|
+
{t('form.fields.description.label')}
|
|
1904
1933
|
</FieldLabel>
|
|
1905
1934
|
<Textarea
|
|
1906
1935
|
id="descricao"
|
|
@@ -1908,8 +1937,42 @@ export default function TrainingPage() {
|
|
|
1908
1937
|
placeholder={t('form.fields.description.placeholder')}
|
|
1909
1938
|
{...form.register('descricao')}
|
|
1910
1939
|
/>
|
|
1940
|
+
</Field>
|
|
1941
|
+
<Field>
|
|
1942
|
+
<FieldLabel>
|
|
1943
|
+
{t('form.fields.progressionMode.label')}{' '}
|
|
1944
|
+
<span className="text-destructive">*</span>
|
|
1945
|
+
</FieldLabel>
|
|
1946
|
+
<Controller
|
|
1947
|
+
name="progressionMode"
|
|
1948
|
+
control={form.control}
|
|
1949
|
+
render={({ field }) => (
|
|
1950
|
+
<Select onValueChange={field.onChange} value={field.value}>
|
|
1951
|
+
<SelectTrigger>
|
|
1952
|
+
<SelectValue
|
|
1953
|
+
placeholder={t(
|
|
1954
|
+
'form.fields.progressionMode.placeholder'
|
|
1955
|
+
)}
|
|
1956
|
+
/>
|
|
1957
|
+
</SelectTrigger>
|
|
1958
|
+
<SelectContent>
|
|
1959
|
+
<SelectItem value="sequencial">
|
|
1960
|
+
{t('form.fields.progressionMode.options.sequential')}
|
|
1961
|
+
</SelectItem>
|
|
1962
|
+
<SelectItem value="livre">
|
|
1963
|
+
{t('form.fields.progressionMode.options.free')}
|
|
1964
|
+
</SelectItem>
|
|
1965
|
+
</SelectContent>
|
|
1966
|
+
</Select>
|
|
1967
|
+
)}
|
|
1968
|
+
/>
|
|
1969
|
+
<p className="text-xs text-muted-foreground">
|
|
1970
|
+
{watchedFormValues.progressionMode === 'livre'
|
|
1971
|
+
? t('form.fields.progressionMode.help.free')
|
|
1972
|
+
: t('form.fields.progressionMode.help.sequential')}
|
|
1973
|
+
</p>
|
|
1911
1974
|
<FieldError>
|
|
1912
|
-
{form.formState.errors.
|
|
1975
|
+
{form.formState.errors.progressionMode?.message}
|
|
1913
1976
|
</FieldError>
|
|
1914
1977
|
</Field>
|
|
1915
1978
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
TableRow,
|
|
64
64
|
} from '@/components/ui/table';
|
|
65
65
|
import { Textarea } from '@/components/ui/textarea';
|
|
66
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
66
67
|
import { usePersistedViewMode } from '@/hooks/use-persisted-view-mode';
|
|
67
68
|
import {
|
|
68
69
|
DndContext,
|
|
@@ -102,7 +103,6 @@ import {
|
|
|
102
103
|
import { useTranslations } from 'next-intl';
|
|
103
104
|
import { useRouter } from 'next/navigation';
|
|
104
105
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
105
|
-
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
106
106
|
import { Controller, useForm, useWatch } from 'react-hook-form';
|
|
107
107
|
import { toast } from 'sonner';
|
|
108
108
|
import { z } from 'zod';
|
|
@@ -113,6 +113,7 @@ interface Formacao {
|
|
|
113
113
|
id: number;
|
|
114
114
|
nome: string;
|
|
115
115
|
descricao: string;
|
|
116
|
+
progressionMode: 'sequencial' | 'livre';
|
|
116
117
|
area: string;
|
|
117
118
|
nivel: string;
|
|
118
119
|
prerequisitos: string;
|
|
@@ -344,6 +345,14 @@ function normalizeStatusValue(value: string) {
|
|
|
344
345
|
return 'rascunho';
|
|
345
346
|
}
|
|
346
347
|
|
|
348
|
+
function normalizeProgressModeValue(value?: string) {
|
|
349
|
+
const normalized = normalizeText(value ?? '');
|
|
350
|
+
|
|
351
|
+
if (['livre', 'free'].includes(normalized)) return 'livre';
|
|
352
|
+
|
|
353
|
+
return 'sequencial';
|
|
354
|
+
}
|
|
355
|
+
|
|
347
356
|
function slugifyText(value: string) {
|
|
348
357
|
return normalizeText(value)
|
|
349
358
|
.replace(/[^a-z0-9\s-]/g, '')
|
|
@@ -417,7 +426,8 @@ function buildCourseCodeFromTitle(title: string) {
|
|
|
417
426
|
|
|
418
427
|
const formacaoSchema = z.object({
|
|
419
428
|
nome: z.string().min(3, 'Minimo 3 caracteres'),
|
|
420
|
-
descricao: z.string().
|
|
429
|
+
descricao: z.string().optional(),
|
|
430
|
+
progressionMode: z.enum(['sequencial', 'livre']),
|
|
421
431
|
area: z.enum(['Tecnologia', 'Design', 'Gestao', 'Marketing', 'Financas']),
|
|
422
432
|
nivel: z.enum(['Iniciante', 'Intermediario', 'Avancado']),
|
|
423
433
|
prerequisitos: z.string().optional(),
|
|
@@ -721,6 +731,7 @@ export default function TrainingPage() {
|
|
|
721
731
|
defaultValues: {
|
|
722
732
|
nome: '',
|
|
723
733
|
descricao: '',
|
|
734
|
+
progressionMode: 'sequencial',
|
|
724
735
|
area: 'Tecnologia',
|
|
725
736
|
nivel: 'Iniciante',
|
|
726
737
|
prerequisitos: '',
|
|
@@ -872,6 +883,8 @@ export default function TrainingPage() {
|
|
|
872
883
|
...formacao,
|
|
873
884
|
nome: watchedFormValues.nome ?? formacao.nome,
|
|
874
885
|
descricao: watchedFormValues.descricao ?? formacao.descricao,
|
|
886
|
+
progressionMode:
|
|
887
|
+
watchedFormValues.progressionMode ?? formacao.progressionMode,
|
|
875
888
|
nivel: watchedFormValues.nivel ?? formacao.nivel,
|
|
876
889
|
status: watchedFormValues.status ?? formacao.status,
|
|
877
890
|
prerequisitos:
|
|
@@ -960,6 +973,7 @@ export default function TrainingPage() {
|
|
|
960
973
|
form.reset({
|
|
961
974
|
nome: '',
|
|
962
975
|
descricao: '',
|
|
976
|
+
progressionMode: 'sequencial',
|
|
963
977
|
area: 'Tecnologia',
|
|
964
978
|
nivel: 'Iniciante',
|
|
965
979
|
prerequisitos: '',
|
|
@@ -1015,6 +1029,9 @@ export default function TrainingPage() {
|
|
|
1015
1029
|
form.reset({
|
|
1016
1030
|
nome: fullFormacao.nome,
|
|
1017
1031
|
descricao: fullFormacao.descricao,
|
|
1032
|
+
progressionMode: normalizeProgressModeValue(
|
|
1033
|
+
fullFormacao.progressionMode
|
|
1034
|
+
),
|
|
1018
1035
|
area: normalizeAreaValue(fullFormacao.area),
|
|
1019
1036
|
nivel: normalizeLevelValue(fullFormacao.nivel),
|
|
1020
1037
|
prerequisitos: fullFormacao.prerequisitos,
|
|
@@ -1164,6 +1181,9 @@ export default function TrainingPage() {
|
|
|
1164
1181
|
}
|
|
1165
1182
|
|
|
1166
1183
|
async function onSubmit(data: FormacaoForm) {
|
|
1184
|
+
const toApiProgressMode = (value: FormacaoForm['progressionMode']) =>
|
|
1185
|
+
value === 'livre' ? 'free' : 'sequential';
|
|
1186
|
+
|
|
1167
1187
|
try {
|
|
1168
1188
|
const orderedItems = normalizeTrailOrder(
|
|
1169
1189
|
[...learningPathItems].sort((a, b) => a.order - b.order)
|
|
@@ -1189,6 +1209,7 @@ export default function TrainingPage() {
|
|
|
1189
1209
|
shortDescription?: string;
|
|
1190
1210
|
level?: 'beginner' | 'intermediate' | 'advanced';
|
|
1191
1211
|
status?: 'draft' | 'active' | 'archived';
|
|
1212
|
+
progressMode?: 'sequential' | 'free';
|
|
1192
1213
|
primaryColor?: string;
|
|
1193
1214
|
secondaryColor?: string;
|
|
1194
1215
|
items?: Array<{
|
|
@@ -1200,7 +1221,9 @@ export default function TrainingPage() {
|
|
|
1200
1221
|
} = {};
|
|
1201
1222
|
|
|
1202
1223
|
if (dirty.nome) payload.title = data.nome;
|
|
1203
|
-
if (dirty.descricao)
|
|
1224
|
+
if (dirty.descricao) {
|
|
1225
|
+
payload.description = data.descricao?.trim() || undefined;
|
|
1226
|
+
}
|
|
1204
1227
|
if (dirty.prerequisitos) {
|
|
1205
1228
|
payload.shortDescription = data.prerequisitos?.trim() || undefined;
|
|
1206
1229
|
}
|
|
@@ -1208,6 +1231,9 @@ export default function TrainingPage() {
|
|
|
1208
1231
|
if (dirty.status) {
|
|
1209
1232
|
payload.status = ptStatusToApi(data.status as Formacao['status']);
|
|
1210
1233
|
}
|
|
1234
|
+
if (dirty.progressionMode) {
|
|
1235
|
+
payload.progressMode = toApiProgressMode(data.progressionMode);
|
|
1236
|
+
}
|
|
1211
1237
|
if (dirty.primaryColor) payload.primaryColor = data.primaryColor;
|
|
1212
1238
|
if (dirty.secondaryColor) payload.secondaryColor = data.secondaryColor;
|
|
1213
1239
|
if (itemsChanged) {
|
|
@@ -1233,10 +1259,11 @@ export default function TrainingPage() {
|
|
|
1233
1259
|
} else {
|
|
1234
1260
|
const payload = {
|
|
1235
1261
|
title: data.nome,
|
|
1236
|
-
description: data.descricao,
|
|
1262
|
+
description: data.descricao?.trim() || undefined,
|
|
1237
1263
|
shortDescription: data.prerequisitos?.trim() || undefined,
|
|
1238
1264
|
level: ptLevelToApi(data.nivel),
|
|
1239
1265
|
status: ptStatusToApi(data.status as Formacao['status']),
|
|
1266
|
+
progressMode: toApiProgressMode(data.progressionMode),
|
|
1240
1267
|
primaryColor: data.primaryColor,
|
|
1241
1268
|
secondaryColor: data.secondaryColor,
|
|
1242
1269
|
items: orderedItems.map((item, index) => ({
|
|
@@ -1892,12 +1919,12 @@ export default function TrainingPage() {
|
|
|
1892
1919
|
</Field>
|
|
1893
1920
|
<Field>
|
|
1894
1921
|
<FieldLabel htmlFor="descricao">
|
|
1895
|
-
{t('form.fields.description.label')}
|
|
1896
|
-
<span className="text-destructive">*</span>
|
|
1922
|
+
{t('form.fields.description.label')}
|
|
1897
1923
|
</FieldLabel>
|
|
1898
1924
|
<Textarea
|
|
1899
1925
|
id="descricao"
|
|
1900
1926
|
rows={3}
|
|
1927
|
+
required={false}
|
|
1901
1928
|
placeholder={t('form.fields.description.placeholder')}
|
|
1902
1929
|
{...form.register('descricao')}
|
|
1903
1930
|
/>
|
|
@@ -1905,6 +1932,43 @@ export default function TrainingPage() {
|
|
|
1905
1932
|
{form.formState.errors.descricao?.message}
|
|
1906
1933
|
</FieldError>
|
|
1907
1934
|
</Field>
|
|
1935
|
+
<Field>
|
|
1936
|
+
<FieldLabel>
|
|
1937
|
+
{t('form.fields.progressionMode.label')}{' '}
|
|
1938
|
+
<span className="text-destructive">*</span>
|
|
1939
|
+
</FieldLabel>
|
|
1940
|
+
<Controller
|
|
1941
|
+
name="progressionMode"
|
|
1942
|
+
control={form.control}
|
|
1943
|
+
render={({ field }) => (
|
|
1944
|
+
<Select onValueChange={field.onChange} value={field.value}>
|
|
1945
|
+
<SelectTrigger>
|
|
1946
|
+
<SelectValue
|
|
1947
|
+
placeholder={t(
|
|
1948
|
+
'form.fields.progressionMode.placeholder'
|
|
1949
|
+
)}
|
|
1950
|
+
/>
|
|
1951
|
+
</SelectTrigger>
|
|
1952
|
+
<SelectContent>
|
|
1953
|
+
<SelectItem value="sequencial">
|
|
1954
|
+
{t('form.fields.progressionMode.options.sequential')}
|
|
1955
|
+
</SelectItem>
|
|
1956
|
+
<SelectItem value="livre">
|
|
1957
|
+
{t('form.fields.progressionMode.options.free')}
|
|
1958
|
+
</SelectItem>
|
|
1959
|
+
</SelectContent>
|
|
1960
|
+
</Select>
|
|
1961
|
+
)}
|
|
1962
|
+
/>
|
|
1963
|
+
<p className="text-xs text-muted-foreground">
|
|
1964
|
+
{watchedFormValues.progressionMode === 'livre'
|
|
1965
|
+
? t('form.fields.progressionMode.help.free')
|
|
1966
|
+
: t('form.fields.progressionMode.help.sequential')}
|
|
1967
|
+
</p>
|
|
1968
|
+
<FieldError>
|
|
1969
|
+
{form.formState.errors.progressionMode?.message}
|
|
1970
|
+
</FieldError>
|
|
1971
|
+
</Field>
|
|
1908
1972
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
1909
1973
|
<Field>
|
|
1910
1974
|
<FieldLabel>
|
|
@@ -1544,7 +1544,20 @@
|
|
|
1544
1544
|
"description": {
|
|
1545
1545
|
"label": "Descrição",
|
|
1546
1546
|
"placeholder": "Descreva a formação...",
|
|
1547
|
-
"required":
|
|
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",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/lms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.349",
|
|
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-
|
|
12
|
+
"@hed-hog/api-locale": "0.0.14",
|
|
13
13
|
"@hed-hog/api-types": "0.0.1",
|
|
14
|
-
"@hed-hog/api": "0.0.
|
|
14
|
+
"@hed-hog/api-prisma": "0.0.6",
|
|
15
15
|
"@hed-hog/api-pagination": "0.0.7",
|
|
16
|
-
"@hed-hog/
|
|
17
|
-
"@hed-hog/
|
|
18
|
-
"@hed-hog/
|
|
19
|
-
"@hed-hog/
|
|
20
|
-
"@hed-hog/finance": "0.0.
|
|
16
|
+
"@hed-hog/contact": "0.0.349",
|
|
17
|
+
"@hed-hog/api": "0.0.8",
|
|
18
|
+
"@hed-hog/core": "0.0.349",
|
|
19
|
+
"@hed-hog/category": "0.0.349",
|
|
20
|
+
"@hed-hog/finance": "0.0.349"
|
|
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();
|