@hed-hog/lms 0.0.355 → 0.0.358
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/course/course-audio-transcription.service.d.ts.map +1 -1
- package/dist/course/course-audio-transcription.service.js +15 -7
- package/dist/course/course-audio-transcription.service.js.map +1 -1
- package/dist/course/course-operations-integration.service.d.ts +31 -0
- package/dist/course/course-operations-integration.service.d.ts.map +1 -1
- package/dist/course/course-operations-integration.service.js +286 -22
- package/dist/course/course-operations-integration.service.js.map +1 -1
- package/dist/course/course-operations.controller.d.ts +10 -0
- package/dist/course/course-operations.controller.d.ts.map +1 -0
- package/dist/course/course-operations.controller.js +67 -0
- package/dist/course/course-operations.controller.js.map +1 -0
- package/dist/course/course-structure.controller.d.ts +15 -1
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.service.d.ts +25 -1
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +160 -24
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +4 -1
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +4 -2
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +61 -2
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +3 -0
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +15 -0
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course-structure-session.dto.d.ts +1 -0
- package/dist/course/dto/create-course-structure-session.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-session.dto.js +5 -0
- package/dist/course/dto/create-course-structure-session.dto.js.map +1 -1
- package/dist/course/dto/update-course-operations-config.dto.d.ts +6 -0
- package/dist/course/dto/update-course-operations-config.dto.d.ts.map +1 -0
- package/dist/course/dto/update-course-operations-config.dto.js +33 -0
- package/dist/course/dto/update-course-operations-config.dto.js.map +1 -0
- package/dist/course/lms-operations-task.subscriber.d.ts +13 -0
- package/dist/course/lms-operations-task.subscriber.d.ts.map +1 -0
- package/dist/course/lms-operations-task.subscriber.js +57 -0
- package/dist/course/lms-operations-task.subscriber.js.map +1 -0
- package/dist/enterprise/enterprise.service.js +1 -1
- package/dist/enterprise/enterprise.service.js.map +1 -1
- package/dist/enterprise/training/training-student.controller.d.ts +0 -95
- package/dist/enterprise/training/training-student.controller.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.controller.js +1 -34
- package/dist/enterprise/training/training-student.controller.js.map +1 -1
- package/dist/enterprise/training/training-student.service.d.ts +63 -0
- package/dist/enterprise/training/training-student.service.d.ts.map +1 -1
- package/dist/enterprise/training/training-student.service.js +320 -4
- package/dist/enterprise/training/training-student.service.js.map +1 -1
- package/dist/instructor/instructor.service.d.ts.map +1 -1
- package/dist/instructor/instructor.service.js +12 -3
- package/dist/instructor/instructor.service.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +2 -0
- package/dist/lms.module.js.map +1 -1
- package/dist/platforma/platforma.controller.d.ts +287 -0
- package/dist/platforma/platforma.controller.d.ts.map +1 -0
- package/dist/platforma/platforma.controller.js +147 -0
- package/dist/platforma/platforma.controller.js.map +1 -0
- package/hedhog/data/route.yaml +102 -9
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +10 -13
- package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +26 -4
- package/hedhog/frontend/app/_lib/editor/types.ts.ejs +6 -0
- package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +447 -31
- package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +59 -47
- package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +201 -35
- package/hedhog/frontend/app/certificates/models/TemplateEditorPage.tsx.ejs +36 -5
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +17 -6
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-operations-tab.tsx.ejs +382 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +45 -8
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +177 -67
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +25 -60
- package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/store.ts.ejs +11 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-context-menu.tsx.ejs +22 -0
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +79 -64
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +31 -14
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +4 -4
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +39 -27
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +24 -2
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +41 -6
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-query.ts.ejs +2 -2
- package/hedhog/frontend/app/courses/page.tsx.ejs +80 -103
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +1 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +18 -6
- package/hedhog/frontend/app/exams/[id]/page.tsx.ejs +5 -4
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +16 -10
- package/hedhog/frontend/app/paths/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/training/page.tsx.ejs +1 -1
- package/hedhog/frontend/messages/en.json +7 -2
- package/hedhog/frontend/messages/pt.json +7 -2
- package/hedhog/table/course_lesson.yaml +2 -2
- package/hedhog/table/course_module.yaml +3 -0
- package/package.json +8 -8
- package/src/course/course-audio-transcription.service.ts +21 -8
- package/src/course/course-operations-integration.service.ts +460 -22
- package/src/course/course-operations.controller.ts +45 -0
- package/src/course/course-structure.service.ts +209 -4
- package/src/course/course.module.ts +4 -1
- package/src/course/course.service.ts +67 -1
- package/src/course/dto/create-course-structure-lesson.dto.ts +17 -0
- package/src/course/dto/create-course-structure-session.dto.ts +13 -1
- package/src/course/dto/update-course-operations-config.dto.ts +16 -0
- package/src/course/lms-operations-task.subscriber.ts +44 -0
- package/src/enterprise/enterprise.service.ts +1 -1
- package/src/enterprise/training/training-student.controller.ts +3 -27
- package/src/enterprise/training/training-student.service.ts +350 -2
- package/src/instructor/instructor.service.ts +12 -3
- package/src/lms.module.ts +2 -0
- package/src/platforma/platforma.controller.ts +92 -0
|
@@ -34,7 +34,7 @@ export interface CourseStructureCacheData {
|
|
|
34
34
|
course: Course | null;
|
|
35
35
|
sessions: Session[];
|
|
36
36
|
lessons: Lesson[];
|
|
37
|
-
instructors: { id: number; name: string }[];
|
|
37
|
+
instructors: { id: number; name: string; avatarId?: number | null }[];
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -45,7 +45,7 @@ export interface CourseStructureQueryResult {
|
|
|
45
45
|
course: Course | null;
|
|
46
46
|
sessions: Session[];
|
|
47
47
|
lessons: Lesson[];
|
|
48
|
-
instructors: { id: number; name: string }[];
|
|
48
|
+
instructors: { id: number; name: string; avatarId?: number | null }[];
|
|
49
49
|
isLoading: boolean;
|
|
50
50
|
isFetching: boolean;
|
|
51
51
|
isError: boolean;
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
import { useTranslations } from 'next-intl';
|
|
74
74
|
import { useRouter } from 'next/navigation';
|
|
75
75
|
import { useEffect, useMemo, useState } from 'react';
|
|
76
|
-
import { useForm
|
|
76
|
+
import { useForm } from 'react-hook-form';
|
|
77
77
|
import { toast } from 'sonner';
|
|
78
78
|
import { CourseAvatar } from '../_components/course-avatar';
|
|
79
79
|
import {
|
|
@@ -98,7 +98,7 @@ interface Curso {
|
|
|
98
98
|
primaryColor?: string | null;
|
|
99
99
|
secondaryColor?: string | null;
|
|
100
100
|
nivel: 'iniciante' | 'intermediario' | 'avancado';
|
|
101
|
-
status: '
|
|
101
|
+
status: 'publicado' | 'rascunho' | 'arquivado';
|
|
102
102
|
tipoCurso: 'on_demand' | 'agendado' | 'hibrido';
|
|
103
103
|
categorias: string[];
|
|
104
104
|
destaque: boolean;
|
|
@@ -214,11 +214,12 @@ function toPtStatus(status?: string | null): CourseSheetFormValues['status'] {
|
|
|
214
214
|
const normalizedStatus = normalizeEnumValue(status);
|
|
215
215
|
|
|
216
216
|
if (
|
|
217
|
+
normalizedStatus === 'publicado' ||
|
|
217
218
|
normalizedStatus === 'ativo' ||
|
|
218
219
|
normalizedStatus === 'active' ||
|
|
219
220
|
normalizedStatus === 'published'
|
|
220
221
|
) {
|
|
221
|
-
return '
|
|
222
|
+
return 'publicado';
|
|
222
223
|
}
|
|
223
224
|
if (normalizedStatus === 'rascunho' || normalizedStatus === 'draft') {
|
|
224
225
|
return 'rascunho';
|
|
@@ -264,7 +265,7 @@ function toApiLevel(level: CourseSheetFormValues['nivel']) {
|
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
function toApiStatus(status: CourseSheetFormValues['status']) {
|
|
267
|
-
if (status === '
|
|
268
|
+
if (status === 'publicado') return 'published';
|
|
268
269
|
if (status === 'rascunho') return 'draft';
|
|
269
270
|
return 'archived';
|
|
270
271
|
}
|
|
@@ -296,7 +297,7 @@ const NIVEL_COLOR: Record<string, string> = {
|
|
|
296
297
|
};
|
|
297
298
|
|
|
298
299
|
const STATUS_VARIANT: Record<string, 'default' | 'secondary' | 'outline'> = {
|
|
299
|
-
|
|
300
|
+
publicado: 'default',
|
|
300
301
|
rascunho: 'secondary',
|
|
301
302
|
arquivado: 'outline',
|
|
302
303
|
};
|
|
@@ -347,7 +348,6 @@ export default function CursosPage() {
|
|
|
347
348
|
const [newCategorySlugTouched, setNewCategorySlugTouched] = useState(false);
|
|
348
349
|
|
|
349
350
|
// Data
|
|
350
|
-
const [editingCurso, setEditingCurso] = useState<Curso | null>(null);
|
|
351
351
|
const [cursoToDelete, setCursoToDelete] = useState<Curso | null>(null);
|
|
352
352
|
const [cachedCourseList, setCachedCourseList] =
|
|
353
353
|
useState<ApiCourseList | null>(null);
|
|
@@ -379,7 +379,6 @@ export default function CursosPage() {
|
|
|
379
379
|
resolver: zodResolver(getCourseSheetSchema(t)),
|
|
380
380
|
defaultValues: DEFAULT_COURSE_FORM_VALUES,
|
|
381
381
|
});
|
|
382
|
-
const watchedFormValues = useWatch({ control: form.control });
|
|
383
382
|
|
|
384
383
|
const {
|
|
385
384
|
data: courseList,
|
|
@@ -407,7 +406,7 @@ export default function CursosPage() {
|
|
|
407
406
|
...(filtroStatusInput !== 'todos'
|
|
408
407
|
? {
|
|
409
408
|
status:
|
|
410
|
-
filtroStatusInput === '
|
|
409
|
+
filtroStatusInput === 'publicado'
|
|
411
410
|
? 'published'
|
|
412
411
|
: filtroStatusInput === 'rascunho'
|
|
413
412
|
? 'draft'
|
|
@@ -528,29 +527,6 @@ export default function CursosPage() {
|
|
|
528
527
|
() => (effectiveCourseList?.data ?? []).map((item) => mapApiCourse(item)),
|
|
529
528
|
[effectiveCourseList]
|
|
530
529
|
);
|
|
531
|
-
const previewCurso = useMemo(() => {
|
|
532
|
-
if (!sheetOpen || !editingCurso) return null;
|
|
533
|
-
|
|
534
|
-
return {
|
|
535
|
-
...editingCurso,
|
|
536
|
-
nomeInterno: watchedFormValues.nomeInterno ?? editingCurso.nomeInterno,
|
|
537
|
-
tituloComercial:
|
|
538
|
-
watchedFormValues.tituloComercial ?? editingCurso.tituloComercial,
|
|
539
|
-
descricao: watchedFormValues.descricao ?? editingCurso.descricao,
|
|
540
|
-
primaryColor: watchedFormValues.primaryColor ?? editingCurso.primaryColor,
|
|
541
|
-
secondaryColor:
|
|
542
|
-
watchedFormValues.secondaryColor ?? editingCurso.secondaryColor,
|
|
543
|
-
nivel:
|
|
544
|
-
(watchedFormValues.nivel as Curso['nivel'] | undefined) ??
|
|
545
|
-
editingCurso.nivel,
|
|
546
|
-
status:
|
|
547
|
-
(watchedFormValues.status as Curso['status'] | undefined) ??
|
|
548
|
-
editingCurso.status,
|
|
549
|
-
tipoCurso: watchedFormValues.offeringType ?? editingCurso.tipoCurso,
|
|
550
|
-
categorias: watchedFormValues.categorias ?? editingCurso.categorias,
|
|
551
|
-
} satisfies Curso;
|
|
552
|
-
}, [editingCurso, sheetOpen, watchedFormValues]);
|
|
553
|
-
|
|
554
530
|
const categoryOptions = useMemo<CourseCategoryOption[]>(
|
|
555
531
|
() =>
|
|
556
532
|
(categoryListData?.data ?? [])
|
|
@@ -572,13 +548,20 @@ export default function CursosPage() {
|
|
|
572
548
|
[categoryOptions]
|
|
573
549
|
);
|
|
574
550
|
|
|
575
|
-
const paginatedCursos =
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
551
|
+
const paginatedCursos = cursos;
|
|
552
|
+
const selectedCourses = useMemo(
|
|
553
|
+
() => cursos.filter((curso) => selectedIds.has(curso.id)),
|
|
554
|
+
[cursos, selectedIds]
|
|
555
|
+
);
|
|
556
|
+
const selectedArchivedIds = useMemo(
|
|
557
|
+
() =>
|
|
558
|
+
selectedCourses
|
|
559
|
+
.filter((curso) => curso.status === 'arquivado')
|
|
560
|
+
.map((curso) => curso.id),
|
|
561
|
+
[selectedCourses]
|
|
562
|
+
);
|
|
563
|
+
const hasSelectedNonArchived =
|
|
564
|
+
selectedCourses.length > selectedArchivedIds.length;
|
|
582
565
|
const totalPages = Math.max(1, effectiveCourseList?.lastPage ?? 1);
|
|
583
566
|
const safePage = Math.min(currentPage, totalPages);
|
|
584
567
|
function clearFilters() {
|
|
@@ -604,12 +587,18 @@ export default function CursosPage() {
|
|
|
604
587
|
|
|
605
588
|
function getStatusLabel(curso: Curso) {
|
|
606
589
|
return t(
|
|
607
|
-
`status.${curso.status === '
|
|
590
|
+
`status.${curso.status === 'publicado' ? 'published' : curso.status === 'rascunho' ? 'draft' : 'archived'}`
|
|
608
591
|
);
|
|
609
592
|
}
|
|
610
593
|
|
|
611
594
|
function openDeleteDialog(curso: Curso, e: React.MouseEvent) {
|
|
612
595
|
e.stopPropagation();
|
|
596
|
+
|
|
597
|
+
if (curso.status !== 'arquivado') {
|
|
598
|
+
toast.error(t('toasts.deleteOnlyArchived'));
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
613
602
|
setCursoToDelete(curso);
|
|
614
603
|
setDeleteDialogOpen(true);
|
|
615
604
|
}
|
|
@@ -632,32 +621,13 @@ export default function CursosPage() {
|
|
|
632
621
|
// ── CRUD ──────────────────────────────────────────────────────────────────
|
|
633
622
|
|
|
634
623
|
function openCreateSheet() {
|
|
635
|
-
setEditingCurso(null);
|
|
636
624
|
form.reset(DEFAULT_COURSE_FORM_VALUES);
|
|
637
625
|
setSheetOpen(true);
|
|
638
626
|
}
|
|
639
627
|
|
|
640
|
-
function
|
|
628
|
+
function goToCourseDetails(curso: Curso, e?: React.MouseEvent) {
|
|
641
629
|
e?.stopPropagation();
|
|
642
|
-
|
|
643
|
-
form.reset({
|
|
644
|
-
nomeInterno: curso.nomeInterno,
|
|
645
|
-
slug: curso.slug,
|
|
646
|
-
code: curso.code || '',
|
|
647
|
-
tituloComercial: curso.tituloComercial,
|
|
648
|
-
descricao: curso.descricao,
|
|
649
|
-
primaryColor: curso.primaryColor || '#1D4ED8',
|
|
650
|
-
secondaryColor: curso.secondaryColor || '#111827',
|
|
651
|
-
nivel: curso.nivel,
|
|
652
|
-
status: curso.status,
|
|
653
|
-
offeringType: curso.tipoCurso,
|
|
654
|
-
categorias: curso.categorias,
|
|
655
|
-
operationsProjectId: curso.operationsProjectId
|
|
656
|
-
? String(curso.operationsProjectId)
|
|
657
|
-
: '',
|
|
658
|
-
logoFileId: curso.logoFileId ?? null,
|
|
659
|
-
});
|
|
660
|
-
setSheetOpen(true);
|
|
630
|
+
router.push(`/lms/courses/${curso.id}`);
|
|
661
631
|
}
|
|
662
632
|
|
|
663
633
|
async function onSubmit(data: CourseSheetFormValues) {
|
|
@@ -683,27 +653,15 @@ export default function CursosPage() {
|
|
|
683
653
|
logoFileId: data.logoFileId ?? null,
|
|
684
654
|
};
|
|
685
655
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
});
|
|
692
|
-
toast.success(t('toasts.courseUpdated'));
|
|
693
|
-
} else {
|
|
694
|
-
const response = await request<ApiCourse>({
|
|
695
|
-
url: '/lms/courses',
|
|
696
|
-
method: 'POST',
|
|
697
|
-
data: payload,
|
|
698
|
-
});
|
|
656
|
+
const response = await request<ApiCourse>({
|
|
657
|
+
url: '/lms/courses',
|
|
658
|
+
method: 'POST',
|
|
659
|
+
data: payload,
|
|
660
|
+
});
|
|
699
661
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
() => router.push(`/lms/courses/${response.data.id}`),
|
|
704
|
-
400
|
|
705
|
-
);
|
|
706
|
-
}
|
|
662
|
+
toast.success(t('toasts.courseCreated'));
|
|
663
|
+
if (response.data?.id) {
|
|
664
|
+
setTimeout(() => router.push(`/lms/courses/${response.data.id}`), 400);
|
|
707
665
|
}
|
|
708
666
|
|
|
709
667
|
await refetchCourses();
|
|
@@ -715,6 +673,11 @@ export default function CursosPage() {
|
|
|
715
673
|
|
|
716
674
|
async function confirmDelete() {
|
|
717
675
|
if (!cursoToDelete) return;
|
|
676
|
+
if (cursoToDelete.status !== 'arquivado') {
|
|
677
|
+
toast.error(t('toasts.deleteOnlyArchived'));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
|
|
718
681
|
await request({
|
|
719
682
|
url: `/lms/courses/${cursoToDelete.id}`,
|
|
720
683
|
method: 'DELETE',
|
|
@@ -935,7 +898,7 @@ export default function CursosPage() {
|
|
|
935
898
|
placeholder: t('table.headers.status'),
|
|
936
899
|
options: [
|
|
937
900
|
{ value: 'todos', label: t('filters.allStatuses') },
|
|
938
|
-
{ value: '
|
|
901
|
+
{ value: 'publicado', label: t('status.published') },
|
|
939
902
|
{ value: 'rascunho', label: t('status.draft') },
|
|
940
903
|
{ value: 'arquivado', label: t('status.archived') },
|
|
941
904
|
],
|
|
@@ -1043,13 +1006,24 @@ export default function CursosPage() {
|
|
|
1043
1006
|
variant="secondary"
|
|
1044
1007
|
className="gap-1.5 text-destructive hover:text-destructive"
|
|
1045
1008
|
onClick={() => {
|
|
1009
|
+
if (selectedArchivedIds.length === 0) {
|
|
1010
|
+
toast.error(t('toasts.deleteOnlyArchived'));
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
if (hasSelectedNonArchived) {
|
|
1015
|
+
toast.warning(t('toasts.bulkDeleteArchivedOnly'));
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1046
1018
|
Promise.all(
|
|
1047
|
-
|
|
1019
|
+
selectedArchivedIds.map((id) =>
|
|
1048
1020
|
request({ url: `/lms/courses/${id}`, method: 'DELETE' })
|
|
1049
1021
|
)
|
|
1050
1022
|
).then(async () => {
|
|
1051
1023
|
toast.success(
|
|
1052
|
-
t('toasts.coursesRemoved', {
|
|
1024
|
+
t('toasts.coursesRemoved', {
|
|
1025
|
+
count: selectedArchivedIds.length,
|
|
1026
|
+
})
|
|
1053
1027
|
);
|
|
1054
1028
|
setSelectedIds(new Set());
|
|
1055
1029
|
await refetchCourses();
|
|
@@ -1240,20 +1214,21 @@ export default function CursosPage() {
|
|
|
1240
1214
|
<DropdownMenuItem
|
|
1241
1215
|
onClick={(e) => {
|
|
1242
1216
|
e.stopPropagation();
|
|
1243
|
-
|
|
1217
|
+
goToCourseDetails(curso, e);
|
|
1244
1218
|
}}
|
|
1245
1219
|
>
|
|
1246
1220
|
<Eye className="mr-2 size-4" />{' '}
|
|
1247
1221
|
{t('table.actions.viewDetails')}
|
|
1248
1222
|
</DropdownMenuItem>
|
|
1249
1223
|
<DropdownMenuItem
|
|
1250
|
-
onClick={(e) =>
|
|
1224
|
+
onClick={(e) => goToCourseDetails(curso, e)}
|
|
1251
1225
|
>
|
|
1252
1226
|
<Pencil className="mr-2 size-4" />{' '}
|
|
1253
1227
|
{t('table.actions.edit')}
|
|
1254
1228
|
</DropdownMenuItem>
|
|
1255
1229
|
<DropdownMenuSeparator />
|
|
1256
1230
|
<DropdownMenuItem
|
|
1231
|
+
disabled={curso.status !== 'arquivado'}
|
|
1257
1232
|
className="text-destructive focus:text-destructive"
|
|
1258
1233
|
onClick={(e) => openDeleteDialog(curso, e)}
|
|
1259
1234
|
>
|
|
@@ -1283,18 +1258,24 @@ export default function CursosPage() {
|
|
|
1283
1258
|
<div className="mt-3 flex items-center gap-3 border-t border-border/50 pt-2.5 text-xs text-muted-foreground">
|
|
1284
1259
|
<span className="flex items-center gap-1">
|
|
1285
1260
|
<Layers className="size-3.5 shrink-0" />
|
|
1286
|
-
<span className="font-medium text-foreground">
|
|
1287
|
-
|
|
1261
|
+
<span className="font-medium text-foreground">
|
|
1262
|
+
{curso.quantidadeSessoes}
|
|
1263
|
+
</span>{' '}
|
|
1264
|
+
sess.
|
|
1288
1265
|
</span>
|
|
1289
1266
|
<span className="flex items-center gap-1">
|
|
1290
1267
|
<BookOpen className="size-3.5 shrink-0" />
|
|
1291
|
-
<span className="font-medium text-foreground">
|
|
1292
|
-
|
|
1268
|
+
<span className="font-medium text-foreground">
|
|
1269
|
+
{curso.quantidadeAulas}
|
|
1270
|
+
</span>{' '}
|
|
1271
|
+
aulas
|
|
1293
1272
|
</span>
|
|
1294
1273
|
<span className="flex items-center gap-1">
|
|
1295
1274
|
<Paperclip className="size-3.5 shrink-0" />
|
|
1296
|
-
<span className="font-medium text-foreground">
|
|
1297
|
-
|
|
1275
|
+
<span className="font-medium text-foreground">
|
|
1276
|
+
{curso.quantidadeRecursos}
|
|
1277
|
+
</span>{' '}
|
|
1278
|
+
rec.
|
|
1298
1279
|
</span>
|
|
1299
1280
|
</div>
|
|
1300
1281
|
</CardContent>
|
|
@@ -1433,21 +1414,20 @@ export default function CursosPage() {
|
|
|
1433
1414
|
</DropdownMenuTrigger>
|
|
1434
1415
|
<DropdownMenuContent align="end" className="w-48">
|
|
1435
1416
|
<DropdownMenuItem
|
|
1436
|
-
onClick={() =>
|
|
1437
|
-
router.push(`/lms/courses/${curso.id}`)
|
|
1438
|
-
}
|
|
1417
|
+
onClick={(e) => goToCourseDetails(curso, e)}
|
|
1439
1418
|
>
|
|
1440
1419
|
<Eye className="mr-2 size-4" />{' '}
|
|
1441
1420
|
{t('table.actions.viewDetails')}
|
|
1442
1421
|
</DropdownMenuItem>
|
|
1443
1422
|
<DropdownMenuItem
|
|
1444
|
-
onClick={() =>
|
|
1423
|
+
onClick={(e) => goToCourseDetails(curso, e)}
|
|
1445
1424
|
>
|
|
1446
1425
|
<Pencil className="mr-2 size-4" />{' '}
|
|
1447
1426
|
{t('table.actions.edit')}
|
|
1448
1427
|
</DropdownMenuItem>
|
|
1449
1428
|
<DropdownMenuSeparator />
|
|
1450
1429
|
<DropdownMenuItem
|
|
1430
|
+
disabled={curso.status !== 'arquivado'}
|
|
1451
1431
|
className="text-destructive focus:text-destructive"
|
|
1452
1432
|
onClick={(e) => openDeleteDialog(curso, e)}
|
|
1453
1433
|
>
|
|
@@ -1485,21 +1465,18 @@ export default function CursosPage() {
|
|
|
1485
1465
|
</div>
|
|
1486
1466
|
)}
|
|
1487
1467
|
|
|
1488
|
-
{/* ── Create
|
|
1468
|
+
{/* ── Create Sheet ───────────────────────────────────────────────────── */}
|
|
1489
1469
|
<CourseFormSheet
|
|
1490
|
-
key=
|
|
1470
|
+
key="new"
|
|
1491
1471
|
open={sheetOpen}
|
|
1492
|
-
onOpenChange={
|
|
1493
|
-
|
|
1494
|
-
if (!open) setEditingCurso(null);
|
|
1495
|
-
}}
|
|
1496
|
-
editing={!!editingCurso}
|
|
1472
|
+
onOpenChange={setSheetOpen}
|
|
1473
|
+
editing={false}
|
|
1497
1474
|
saving={saving}
|
|
1498
1475
|
form={form}
|
|
1499
1476
|
onSubmit={onSubmit}
|
|
1500
1477
|
categories={categoryOptions}
|
|
1501
1478
|
onCreateCategory={openCreateCategorySheet}
|
|
1502
|
-
initialLogoFileId={
|
|
1479
|
+
initialLogoFileId={null}
|
|
1503
1480
|
t={t}
|
|
1504
1481
|
/>
|
|
1505
1482
|
|
|
@@ -23,7 +23,7 @@ function toApiLevel(level: CourseSheetFormValues['nivel']) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function toApiStatus(status: CourseSheetFormValues['status']) {
|
|
26
|
-
if (status === '
|
|
26
|
+
if (status === 'publicado') return 'published';
|
|
27
27
|
if (status === 'rascunho') return 'draft';
|
|
28
28
|
return 'archived';
|
|
29
29
|
}
|
|
@@ -54,7 +54,7 @@ function toPtLevel(level?: string | null): CourseSheetFormValues['nivel'] {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function toPtStatus(status?: string | null): CourseSheetFormValues['status'] {
|
|
57
|
-
if (status === 'published' || status === 'active') return '
|
|
57
|
+
if (status === 'published' || status === 'active') return 'publicado';
|
|
58
58
|
if (status === 'draft') return 'rascunho';
|
|
59
59
|
if (status === 'archived') return 'arquivado';
|
|
60
60
|
return 'rascunho';
|
|
@@ -67,7 +67,7 @@ function toApiLevel(level: CourseSheetFormValues['nivel']) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function toApiStatus(status: CourseSheetFormValues['status']) {
|
|
70
|
-
if (status === '
|
|
70
|
+
if (status === 'publicado') return 'published';
|
|
71
71
|
if (status === 'rascunho') return 'draft';
|
|
72
72
|
return 'archived';
|
|
73
73
|
}
|
|
@@ -588,12 +588,24 @@ export default function EnterprisePage() {
|
|
|
588
588
|
|
|
589
589
|
{/* CRM */}
|
|
590
590
|
{account.crmAccountName && (
|
|
591
|
-
<
|
|
592
|
-
<
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
591
|
+
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
592
|
+
<Avatar className="h-4 w-4 shrink-0 rounded-full">
|
|
593
|
+
<AvatarImage
|
|
594
|
+
src={getPersonAvatarUrl(account.crmAccount?.avatarId ?? null)}
|
|
595
|
+
alt={account.crmAccountName}
|
|
596
|
+
className="object-cover"
|
|
597
|
+
/>
|
|
598
|
+
<AvatarFallback className="bg-slate-100 text-[8px] font-semibold uppercase text-slate-700">
|
|
599
|
+
{getInitials(account.crmAccountName)}
|
|
600
|
+
</AvatarFallback>
|
|
601
|
+
</Avatar>
|
|
602
|
+
<p className="truncate">
|
|
603
|
+
<span className="font-medium">
|
|
604
|
+
{t.has('labels.crm') ? t('labels.crm') : 'CRM'}:
|
|
605
|
+
</span>{' '}
|
|
606
|
+
{account.crmAccountName}
|
|
607
|
+
</p>
|
|
608
|
+
</div>
|
|
597
609
|
)}
|
|
598
610
|
|
|
599
611
|
{/* Stats grid */}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { redirect } from 'next/navigation';
|
|
2
2
|
|
|
3
3
|
type ExamPageProps = {
|
|
4
|
-
params: {
|
|
4
|
+
params: Promise<{
|
|
5
5
|
id: string;
|
|
6
|
-
}
|
|
6
|
+
}>;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export default function ExamPage({ params }: ExamPageProps) {
|
|
10
|
-
|
|
9
|
+
export default async function ExamPage({ params }: ExamPageProps) {
|
|
10
|
+
const { id } = await params;
|
|
11
|
+
redirect(`/lms/exams/${id}/questions`);
|
|
11
12
|
}
|
|
@@ -325,7 +325,7 @@ export function InstructorFormSheet({
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
const params = new URLSearchParams({
|
|
328
|
-
page: String(turmasPage),
|
|
328
|
+
page: String(Math.max(turmasPage, 1)),
|
|
329
329
|
pageSize: String(TURMAS_PAGE_SIZE),
|
|
330
330
|
});
|
|
331
331
|
|
|
@@ -350,12 +350,9 @@ export function InstructorFormSheet({
|
|
|
350
350
|
placeholderData: (prev) => prev,
|
|
351
351
|
});
|
|
352
352
|
const turmasData = turmasResult?.data ?? [];
|
|
353
|
-
const turmasLastPage = turmasResult?.lastPage ?? 1;
|
|
353
|
+
const turmasLastPage = Math.max(turmasResult?.lastPage ?? 1, 1);
|
|
354
354
|
|
|
355
|
-
const {
|
|
356
|
-
data: avaliacoesResult,
|
|
357
|
-
isLoading: loadingAvaliacoes,
|
|
358
|
-
} = useQuery<{
|
|
355
|
+
const { data: avaliacoesResult, isLoading: loadingAvaliacoes } = useQuery<{
|
|
359
356
|
data: EvaluationItem[];
|
|
360
357
|
total: number;
|
|
361
358
|
lastPage: number;
|
|
@@ -399,6 +396,11 @@ export function InstructorFormSheet({
|
|
|
399
396
|
useEffect(() => {
|
|
400
397
|
if (turmasPage > turmasLastPage) {
|
|
401
398
|
setTurmasPage(turmasLastPage);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (turmasPage < 1) {
|
|
403
|
+
setTurmasPage(1);
|
|
402
404
|
}
|
|
403
405
|
}, [turmasLastPage, turmasPage]);
|
|
404
406
|
|
|
@@ -1170,7 +1172,7 @@ export function InstructorFormSheet({
|
|
|
1170
1172
|
variant="outline"
|
|
1171
1173
|
size="icon"
|
|
1172
1174
|
disabled={turmasPage <= 1}
|
|
1173
|
-
onClick={() => setTurmasPage((p) => p - 1)}
|
|
1175
|
+
onClick={() => setTurmasPage((p) => Math.max(p - 1, 1))}
|
|
1174
1176
|
>
|
|
1175
1177
|
<ChevronLeft className="h-4 w-4" />
|
|
1176
1178
|
</Button>
|
|
@@ -1182,7 +1184,9 @@ export function InstructorFormSheet({
|
|
|
1182
1184
|
variant="outline"
|
|
1183
1185
|
size="icon"
|
|
1184
1186
|
disabled={turmasPage >= turmasLastPage}
|
|
1185
|
-
onClick={() =>
|
|
1187
|
+
onClick={() =>
|
|
1188
|
+
setTurmasPage((p) => Math.min(p + 1, turmasLastPage))
|
|
1189
|
+
}
|
|
1186
1190
|
>
|
|
1187
1191
|
<ChevronRight className="h-4 w-4" />
|
|
1188
1192
|
</Button>
|
|
@@ -1249,7 +1253,8 @@ export function InstructorFormSheet({
|
|
|
1249
1253
|
/>
|
|
1250
1254
|
))}
|
|
1251
1255
|
<span className="ml-1 text-xs font-medium tabular-nums text-muted-foreground">
|
|
1252
|
-
{ev.score.toFixed(1)}
|
|
1256
|
+
{ev.score.toFixed(1)}
|
|
1257
|
+
{t('form.evaluations.of')}10
|
|
1253
1258
|
</span>
|
|
1254
1259
|
</div>
|
|
1255
1260
|
</div>
|
|
@@ -1272,7 +1277,8 @@ export function InstructorFormSheet({
|
|
|
1272
1277
|
{/* Evaluator + date */}
|
|
1273
1278
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
1274
1279
|
<span>
|
|
1275
|
-
{ev.evaluatorName ??
|
|
1280
|
+
{ev.evaluatorName ??
|
|
1281
|
+
t('form.evaluations.anonymous')}
|
|
1276
1282
|
</span>
|
|
1277
1283
|
<span>·</span>
|
|
1278
1284
|
<span>
|
|
@@ -1146,7 +1146,7 @@ export default function TrainingPage() {
|
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
1148
1148
|
function courseStatusToApi(value: CourseSheetFormValues['status']) {
|
|
1149
|
-
if (value === '
|
|
1149
|
+
if (value === 'publicado') return 'published';
|
|
1150
1150
|
if (value === 'arquivado') return 'archived';
|
|
1151
1151
|
return 'draft';
|
|
1152
1152
|
}
|
|
@@ -1132,7 +1132,7 @@ export default function TrainingPage() {
|
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
1134
|
function courseStatusToApi(value: CourseSheetFormValues['status']) {
|
|
1135
|
-
if (value === '
|
|
1135
|
+
if (value === 'publicado') return 'published';
|
|
1136
1136
|
if (value === 'arquivado') return 'archived';
|
|
1137
1137
|
return 'draft';
|
|
1138
1138
|
}
|
|
@@ -400,7 +400,7 @@
|
|
|
400
400
|
"advanced": "Advanced"
|
|
401
401
|
},
|
|
402
402
|
"status": {
|
|
403
|
-
"
|
|
403
|
+
"published": "Published",
|
|
404
404
|
"draft": "Draft",
|
|
405
405
|
"archived": "Archived"
|
|
406
406
|
},
|
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
"sub": "registered"
|
|
575
575
|
},
|
|
576
576
|
"activeCourses": {
|
|
577
|
-
"label": "
|
|
577
|
+
"label": "Published Courses",
|
|
578
578
|
"sub": "published"
|
|
579
579
|
},
|
|
580
580
|
"totalStudents": {
|
|
@@ -610,6 +610,7 @@
|
|
|
610
610
|
"advanced": "Advanced"
|
|
611
611
|
},
|
|
612
612
|
"status": {
|
|
613
|
+
"published": "Published",
|
|
613
614
|
"active": "Active",
|
|
614
615
|
"draft": "Draft",
|
|
615
616
|
"archived": "Archived"
|
|
@@ -771,6 +772,8 @@
|
|
|
771
772
|
"courseRemoved": "Course \"{title}\" removed.",
|
|
772
773
|
"coursesRemoved": "{count} course(s) removed.",
|
|
773
774
|
"coursesArchived": "{count} course(s) archived.",
|
|
775
|
+
"deleteOnlyArchived": "Only archived courses can be deleted.",
|
|
776
|
+
"bulkDeleteArchivedOnly": "Only selected archived courses will be deleted.",
|
|
774
777
|
"codeCopied": "Code \"{code}\" copied."
|
|
775
778
|
},
|
|
776
779
|
"StructurePage": {
|
|
@@ -942,6 +945,8 @@
|
|
|
942
945
|
},
|
|
943
946
|
"lessonRow": {
|
|
944
947
|
"rename": "Rename lesson",
|
|
948
|
+
"draft": "Draft",
|
|
949
|
+
"published": "Published",
|
|
945
950
|
"visibility": "Visibility: {value}",
|
|
946
951
|
"status": "Status: {value}",
|
|
947
952
|
"videoLinked": "Linked video",
|
|
@@ -409,7 +409,7 @@
|
|
|
409
409
|
"advanced": "Avançado"
|
|
410
410
|
},
|
|
411
411
|
"status": {
|
|
412
|
-
"
|
|
412
|
+
"published": "Publicado",
|
|
413
413
|
"draft": "Rascunho",
|
|
414
414
|
"archived": "Arquivado"
|
|
415
415
|
},
|
|
@@ -583,7 +583,7 @@
|
|
|
583
583
|
"sub": "cadastrados"
|
|
584
584
|
},
|
|
585
585
|
"activeCourses": {
|
|
586
|
-
"label": "Cursos
|
|
586
|
+
"label": "Cursos Publicados",
|
|
587
587
|
"sub": "publicados"
|
|
588
588
|
},
|
|
589
589
|
"totalStudents": {
|
|
@@ -619,6 +619,7 @@
|
|
|
619
619
|
"advanced": "Avançado"
|
|
620
620
|
},
|
|
621
621
|
"status": {
|
|
622
|
+
"published": "Publicado",
|
|
622
623
|
"active": "Ativo",
|
|
623
624
|
"draft": "Rascunho",
|
|
624
625
|
"archived": "Arquivado"
|
|
@@ -780,6 +781,8 @@
|
|
|
780
781
|
"courseRemoved": "Curso \"{title}\" removido.",
|
|
781
782
|
"coursesRemoved": "{count} curso(s) removido(s).",
|
|
782
783
|
"coursesArchived": "{count} curso(s) arquivado(s).",
|
|
784
|
+
"deleteOnlyArchived": "Apenas cursos arquivados podem ser excluídos.",
|
|
785
|
+
"bulkDeleteArchivedOnly": "Somente cursos arquivados selecionados serão excluídos.",
|
|
783
786
|
"codeCopied": "Código \"{code}\" copiado."
|
|
784
787
|
},
|
|
785
788
|
"StructurePage": {
|
|
@@ -951,6 +954,8 @@
|
|
|
951
954
|
},
|
|
952
955
|
"lessonRow": {
|
|
953
956
|
"rename": "Renomear aula",
|
|
957
|
+
"draft": "Rascunho",
|
|
958
|
+
"published": "Publicada",
|
|
954
959
|
"visibility": "Visibilidade: {value}",
|
|
955
960
|
"status": "Status: {value}",
|
|
956
961
|
"videoLinked": "Vídeo vinculado",
|