@hed-hog/lms 0.0.331 → 0.0.347

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/class-group/class-group.controller.d.ts +8 -8
  2. package/dist/class-group/class-group.service.d.ts +8 -8
  3. package/dist/course/course.controller.d.ts +6 -1
  4. package/dist/course/course.controller.d.ts.map +1 -1
  5. package/dist/course/course.controller.js +19 -2
  6. package/dist/course/course.controller.js.map +1 -1
  7. package/dist/course/course.service.d.ts +6 -0
  8. package/dist/course/course.service.d.ts.map +1 -1
  9. package/dist/course/course.service.js +63 -28
  10. package/dist/course/course.service.js.map +1 -1
  11. package/dist/course/dto/create-course.dto.d.ts +1 -0
  12. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  13. package/dist/course/dto/create-course.dto.js +5 -0
  14. package/dist/course/dto/create-course.dto.js.map +1 -1
  15. package/dist/enterprise/enterprise.controller.d.ts +84 -12
  16. package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
  17. package/dist/enterprise/enterprise.controller.js +10 -0
  18. package/dist/enterprise/enterprise.controller.js.map +1 -1
  19. package/dist/enterprise/enterprise.service.d.ts +90 -12
  20. package/dist/enterprise/enterprise.service.d.ts.map +1 -1
  21. package/dist/enterprise/enterprise.service.js +413 -40
  22. package/dist/enterprise/enterprise.service.js.map +1 -1
  23. package/dist/enterprise/training/training-admin.controller.d.ts +9 -6
  24. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  25. package/dist/enterprise/training/training-admin.controller.js +10 -6
  26. package/dist/enterprise/training/training-admin.controller.js.map +1 -1
  27. package/dist/enterprise/training/training-admin.service.d.ts +11 -5
  28. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  29. package/dist/enterprise/training/training-admin.service.js +108 -52
  30. package/dist/enterprise/training/training-admin.service.js.map +1 -1
  31. package/dist/enterprise/training/training-viewer.controller.d.ts +3 -0
  32. package/dist/enterprise/training/training-viewer.controller.d.ts.map +1 -1
  33. package/dist/evaluation/evaluation.controller.d.ts +2 -2
  34. package/dist/evaluation/evaluation.service.d.ts +2 -2
  35. package/dist/instructor/dto/create-instructor-skill.dto.d.ts +0 -4
  36. package/dist/instructor/dto/create-instructor-skill.dto.d.ts.map +1 -1
  37. package/dist/instructor/dto/create-instructor-skill.dto.js +0 -21
  38. package/dist/instructor/dto/create-instructor-skill.dto.js.map +1 -1
  39. package/dist/instructor/dto/update-instructor-skill.dto.d.ts +0 -4
  40. package/dist/instructor/dto/update-instructor-skill.dto.d.ts.map +1 -1
  41. package/dist/instructor/dto/update-instructor-skill.dto.js +0 -22
  42. package/dist/instructor/dto/update-instructor-skill.dto.js.map +1 -1
  43. package/dist/instructor/instructor-skill.controller.d.ts +4 -4
  44. package/dist/instructor/instructor-skill.service.d.ts +4 -7
  45. package/dist/instructor/instructor-skill.service.d.ts.map +1 -1
  46. package/dist/instructor/instructor-skill.service.js +2 -89
  47. package/dist/instructor/instructor-skill.service.js.map +1 -1
  48. package/dist/instructor/instructor.controller.d.ts +21 -0
  49. package/dist/instructor/instructor.controller.d.ts.map +1 -1
  50. package/dist/instructor/instructor.controller.js +19 -0
  51. package/dist/instructor/instructor.controller.js.map +1 -1
  52. package/dist/instructor/instructor.service.d.ts +27 -0
  53. package/dist/instructor/instructor.service.d.ts.map +1 -1
  54. package/dist/instructor/instructor.service.js +79 -25
  55. package/dist/instructor/instructor.service.js.map +1 -1
  56. package/dist/lms.module.d.ts.map +1 -1
  57. package/dist/lms.module.js.map +1 -1
  58. package/dist/training/dto/create-training.dto.d.ts +1 -0
  59. package/dist/training/dto/create-training.dto.d.ts.map +1 -1
  60. package/dist/training/dto/create-training.dto.js +5 -0
  61. package/dist/training/dto/create-training.dto.js.map +1 -1
  62. package/dist/training/training.controller.d.ts +4 -0
  63. package/dist/training/training.controller.d.ts.map +1 -1
  64. package/dist/training/training.service.d.ts +8 -0
  65. package/dist/training/training.service.d.ts.map +1 -1
  66. package/dist/training/training.service.js +71 -6
  67. package/dist/training/training.service.js.map +1 -1
  68. package/hedhog/data/route.yaml +23 -1
  69. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +80 -33
  70. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +3 -3
  71. package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +591 -0
  72. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +6 -1
  73. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +39 -7
  74. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +1 -3
  75. package/hedhog/frontend/app/classes/page.tsx.ejs +34 -7
  76. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +3 -33
  77. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +9 -9
  78. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +109 -0
  79. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +40 -13
  80. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +76 -81
  81. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +60 -0
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/course-scheduled-classes-tab.tsx.ejs +406 -0
  83. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
  84. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
  85. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
  86. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
  87. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +243 -34
  88. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +185 -0
  89. package/hedhog/frontend/app/courses/page.tsx.ejs +6 -1
  90. package/hedhog/frontend/app/enterprise/_components/enterprise-activity-timeline.tsx.ejs +87 -0
  91. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +4 -0
  92. package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +31 -5
  93. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +79 -20
  94. package/hedhog/frontend/app/enterprise/_components/enterprise-company-identity-card.tsx.ejs +11 -2
  95. package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +201 -0
  96. package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +55 -24
  97. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +430 -296
  98. package/hedhog/frontend/app/enterprise/_components/enterprise-mocks.ts.ejs +277 -0
  99. package/hedhog/frontend/app/enterprise/_components/enterprise-overview-analytics.tsx.ejs +205 -0
  100. package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +97 -0
  101. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +82 -57
  102. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +4 -0
  103. package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +60 -22
  104. package/hedhog/frontend/app/enterprise/_components/enterprise-types.ts.ejs +54 -0
  105. package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +211 -0
  106. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -7
  107. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +31 -19
  108. package/hedhog/frontend/app/exams/page.tsx.ejs +6 -1
  109. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +51 -104
  110. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +625 -366
  111. package/hedhog/frontend/app/instructors/page.tsx.ejs +6 -1
  112. package/hedhog/frontend/app/paths/page.tsx.ejs +76 -8
  113. package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +6 -1
  114. package/hedhog/frontend/app/training/page.tsx.ejs +78 -9
  115. package/hedhog/frontend/messages/en.json +101 -10
  116. package/hedhog/frontend/messages/pt.json +115 -11
  117. package/hedhog/table/enterprise_student_license_event.yaml +30 -0
  118. package/hedhog/table/instructor_skill.yaml +0 -11
  119. package/hedhog/table/learning_path.yaml +4 -0
  120. package/package.json +6 -6
  121. package/src/course/course.controller.ts +18 -0
  122. package/src/course/course.service.ts +85 -26
  123. package/src/course/dto/create-course.dto.ts +4 -0
  124. package/src/enterprise/enterprise.controller.ts +5 -0
  125. package/src/enterprise/enterprise.service.ts +507 -29
  126. package/src/enterprise/training/training-admin.controller.ts +4 -0
  127. package/src/enterprise/training/training-admin.service.ts +115 -51
  128. package/src/instructor/dto/create-instructor-skill.dto.ts +0 -17
  129. package/src/instructor/dto/update-instructor-skill.dto.ts +0 -18
  130. package/src/instructor/instructor-skill.service.ts +2 -97
  131. package/src/instructor/instructor.controller.ts +16 -0
  132. package/src/instructor/instructor.service.ts +87 -10
  133. package/src/lms.module.ts +1 -0
  134. package/src/training/dto/create-training.dto.ts +4 -0
  135. package/src/training/training.service.ts +104 -5
@@ -42,13 +42,14 @@ import {
42
42
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
43
43
  import { zodResolver } from '@hookform/resolvers/zod';
44
44
  import { format } from 'date-fns';
45
- import { CalendarIcon, Loader2, Plus } from 'lucide-react';
45
+ import { CalendarIcon, Loader2, Pencil, Plus } from 'lucide-react';
46
46
  import { useTranslations } from 'next-intl';
47
47
  import { useEffect, useMemo, useRef, useState } from 'react';
48
48
  import type { DateRange } from 'react-day-picker';
49
49
  import { Controller, useForm, useWatch } from 'react-hook-form';
50
50
  import { toast } from 'sonner';
51
51
  import { z } from 'zod';
52
+ import { InstructorFormSheet } from '../instructors/_components/instructor-form-sheet';
52
53
  import {
53
54
  CourseFormSheet,
54
55
  DEFAULT_COURSE_FORM_VALUES,
@@ -56,7 +57,6 @@ import {
56
57
  type CourseCategoryOption,
57
58
  type CourseSheetFormValues,
58
59
  } from './course-form-sheet';
59
- import { CreateLmsPersonSheet } from './create-lms-person-sheet';
60
60
 
61
61
  // ── Types ─────────────────────────────────────────────────────────────────────
62
62
 
@@ -136,6 +136,7 @@ type InstructorOption = {
136
136
  name: string;
137
137
  personId?: number;
138
138
  avatarId?: number | null;
139
+ userPhotoId?: number | null;
139
140
  qualificationSlugs?: string[];
140
141
  };
141
142
 
@@ -151,6 +152,8 @@ type InstructorApiRow = {
151
152
  person_id?: number | string;
152
153
  avatarId?: number | string | null;
153
154
  avatar_id?: number | string | null;
155
+ userPhotoId?: number | string | null;
156
+ user_photo_id?: number | string | null;
154
157
  qualificationSlugs?: string[];
155
158
  };
156
159
 
@@ -310,17 +313,38 @@ function normalizeInstructorOption(
310
313
  rawAvatarId !== undefined && rawAvatarId !== null
311
314
  ? Number(rawAvatarId) || null
312
315
  : null;
316
+ const rawUserPhotoId = item?.userPhotoId ?? item?.user_photo_id;
317
+ const userPhotoId =
318
+ rawUserPhotoId !== undefined && rawUserPhotoId !== null
319
+ ? Number(rawUserPhotoId) || null
320
+ : null;
313
321
  return {
314
322
  id,
315
323
  name,
316
324
  personId: Number(item?.personId ?? item?.person_id ?? 0) || undefined,
317
325
  avatarId,
326
+ userPhotoId,
318
327
  qualificationSlugs: Array.isArray(item?.qualificationSlugs)
319
328
  ? item.qualificationSlugs
320
329
  : undefined,
321
330
  };
322
331
  }
323
332
 
333
+ function getInstructorAvatarUrl(option?: {
334
+ userPhotoId?: number | null;
335
+ avatarId?: number | null;
336
+ }) {
337
+ if (option?.userPhotoId) {
338
+ return `${process.env.NEXT_PUBLIC_API_BASE_URL}/user/avatar/${option.userPhotoId}`;
339
+ }
340
+
341
+ if (option?.avatarId) {
342
+ return `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${option.avatarId}`;
343
+ }
344
+
345
+ return undefined;
346
+ }
347
+
324
348
  function getContrastColor(hex: string) {
325
349
  const cleaned = hex.replace('#', '');
326
350
  if (cleaned.length !== 6) return '#FFFFFF';
@@ -493,6 +517,10 @@ export interface ClassFormSheetProps {
493
517
  onOpenChange: (open: boolean) => void;
494
518
  /** Omit for create mode; provide for edit mode. */
495
519
  classId?: string;
520
+ defaultCourse?: { id: number; title: string };
521
+ lockCourse?: boolean;
522
+ /** Disable nested instructor sheet to avoid recursive sheet composition. */
523
+ enableInstructorSheet?: boolean;
496
524
  sheetTitle?: string;
497
525
  sheetDescription?: string;
498
526
  onAfterCreate?: (cls: ApiClass) => Promise<void>;
@@ -507,6 +535,9 @@ export function ClassFormSheet({
507
535
  open,
508
536
  onOpenChange,
509
537
  classId,
538
+ defaultCourse,
539
+ lockCourse = false,
540
+ enableInstructorSheet = true,
510
541
  sheetTitle,
511
542
  sheetDescription,
512
543
  onAfterCreate,
@@ -522,8 +553,10 @@ export function ClassFormSheet({
522
553
  const [savingCourse, setSavingCourse] = useState(false);
523
554
  const [dateRangeOpen, setDateRangeOpen] = useState(false);
524
555
  const [dateRangeDraft, setDateRangeDraft] = useState<DateRange | undefined>();
525
- const [createProfessorDialogOpen, setCreateProfessorDialogOpen] =
526
- useState(false);
556
+ const [instructorSheetOpen, setInstructorSheetOpen] = useState(false);
557
+ const [editingInstructorId, setEditingInstructorId] = useState<number | null>(
558
+ null
559
+ );
527
560
  const [courseSheetOpen, setCourseSheetOpen] = useState(false);
528
561
  const [customRecurrenceDialogOpen, setCustomRecurrenceDialogOpen] =
529
562
  useState(false);
@@ -664,14 +697,15 @@ export function ClassFormSheet({
664
697
  } else {
665
698
  // Create mode: reset with fresh defaults + generated code
666
699
  const nextSeed = createClassCodeSeed();
667
- const nextCode = buildClassCode(undefined, nextSeed);
700
+ const defaultCourseTitle = defaultCourse?.title ?? '';
701
+ const nextCode = buildClassCode(defaultCourseTitle, nextSeed);
668
702
  setDateRangeDraft(undefined);
669
703
  setDateRangeOpen(false);
670
704
  setPreviousRecurrenceMode('none');
671
705
  form.reset({
672
706
  codigo: nextCode,
673
- curso: '',
674
- courseId: undefined,
707
+ curso: defaultCourseTitle,
708
+ courseId: defaultCourse?.id,
675
709
  instructorId: undefined,
676
710
  tipo: 'online',
677
711
  professor: '',
@@ -967,12 +1001,7 @@ export function ClassFormSheet({
967
1001
  setCustomRecurrenceDialogOpen(false);
968
1002
  }
969
1003
 
970
- const handleProfessorCreated = (instructor: {
971
- id: number;
972
- personId: number;
973
- name: string;
974
- qualificationSlugs: string[];
975
- }) => {
1004
+ const handleProfessorCreated = (instructor: { id: number; name: string }) => {
976
1005
  form.setValue('instructorId', instructor.id, {
977
1006
  shouldDirty: true,
978
1007
  shouldTouch: true,
@@ -1173,6 +1202,7 @@ export function ClassFormSheet({
1173
1202
  noResultsLabel={t(
1174
1203
  'components.entityPicker.courses.empty'
1175
1204
  )}
1205
+ disabled={lockCourse}
1176
1206
  showCreateButton={false}
1177
1207
  onChange={(value, option) => {
1178
1208
  const cId =
@@ -1218,6 +1248,7 @@ export function ClassFormSheet({
1218
1248
  variant="outline"
1219
1249
  size="icon"
1220
1250
  className="shrink-0"
1251
+ disabled={lockCourse}
1221
1252
  onClick={() => {
1222
1253
  courseForm.reset(DEFAULT_COURSE_FORM_VALUES);
1223
1254
  setCourseSheetOpen(true);
@@ -1774,9 +1805,7 @@ export function ClassFormSheet({
1774
1805
  .slice(0, 2)
1775
1806
  .map((p) => p[0]?.toUpperCase() ?? '')
1776
1807
  .join('');
1777
- const avatarUrl = option.avatarId
1778
- ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${option.avatarId}`
1779
- : undefined;
1808
+ const avatarUrl = getInstructorAvatarUrl(option);
1780
1809
  return (
1781
1810
  <div className="flex min-w-0 items-center gap-3 py-0.5">
1782
1811
  <Avatar className="h-8 w-8 shrink-0 rounded-lg border border-border/60">
@@ -1799,9 +1828,9 @@ export function ClassFormSheet({
1799
1828
  .slice(0, 2)
1800
1829
  .map((p) => p[0]?.toUpperCase() ?? '')
1801
1830
  .join('');
1802
- const avatarUrl = option?.avatarId
1803
- ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${option.avatarId}`
1804
- : undefined;
1831
+ const avatarUrl = getInstructorAvatarUrl(
1832
+ option ?? undefined
1833
+ );
1805
1834
  return (
1806
1835
  <div className="flex items-center gap-2">
1807
1836
  <Avatar className="h-5 w-5 shrink-0 rounded">
@@ -1821,8 +1850,26 @@ export function ClassFormSheet({
1821
1850
  variant="outline"
1822
1851
  size="icon"
1823
1852
  className="shrink-0"
1824
- onClick={() => setCreateProfessorDialogOpen(true)}
1825
- aria-label="Cadastrar novo professor"
1853
+ disabled={!watchedFormValues.instructorId}
1854
+ onClick={() => {
1855
+ if (!watchedFormValues.instructorId) return;
1856
+ setEditingInstructorId(watchedFormValues.instructorId);
1857
+ setInstructorSheetOpen(true);
1858
+ }}
1859
+ aria-label="Editar instrutor"
1860
+ >
1861
+ <Pencil className="h-4 w-4" />
1862
+ </Button>
1863
+ <Button
1864
+ type="button"
1865
+ variant="outline"
1866
+ size="icon"
1867
+ className="shrink-0"
1868
+ onClick={() => {
1869
+ setEditingInstructorId(null);
1870
+ setInstructorSheetOpen(true);
1871
+ }}
1872
+ aria-label="Cadastrar novo instrutor"
1826
1873
  >
1827
1874
  <Plus className="h-4 w-4" />
1828
1875
  </Button>
@@ -2032,18 +2079,18 @@ export function ClassFormSheet({
2032
2079
  t={courseSheetT}
2033
2080
  />
2034
2081
 
2035
- {/* Professor create sheet */}
2036
- <CreateLmsPersonSheet
2037
- open={createProfessorDialogOpen}
2038
- onOpenChange={setCreateProfessorDialogOpen}
2039
- onCreated={handleProfessorCreated}
2040
- title="Cadastrar novo professor"
2041
- description="Crie um novo professor para seleciona-lo nesta turma."
2042
- submitLabel="Cadastrar"
2043
- successMessage="Professor cadastrado com sucesso."
2044
- errorMessage="Não foi possível cadastrar o professor."
2045
- defaultQualificationSlugs={['class-sessions']}
2046
- />
2082
+ {/* Instructor create/edit sheet */}
2083
+ {enableInstructorSheet && (
2084
+ <InstructorFormSheet
2085
+ open={instructorSheetOpen}
2086
+ onOpenChange={(nextOpen) => {
2087
+ setInstructorSheetOpen(nextOpen);
2088
+ if (!nextOpen) setEditingInstructorId(null);
2089
+ }}
2090
+ instructorId={editingInstructorId}
2091
+ onSaved={handleProfessorCreated}
2092
+ />
2093
+ )}
2047
2094
  </>
2048
2095
  );
2049
2096
  }
@@ -526,14 +526,14 @@ export function CourseFormSheet({
526
526
  </Field>
527
527
 
528
528
  <Field>
529
- <FieldLabel htmlFor="descricao">
529
+ <FieldLabel htmlFor="descricao">
530
530
  {t('form.fields.description.label')}
531
531
  </FieldLabel>
532
532
  <Textarea
533
- id="descricao"
533
+ id="descricao"
534
534
  rows={3}
535
535
  placeholder={t('form.fields.description.placeholder')}
536
- {...form.register('descricao')}
536
+ {...form.register('descricao')}
537
537
  />
538
538
  <FieldError>{form.formState.errors.descricao?.message}</FieldError>
539
539
  </Field>