@hed-hog/lms 0.0.350 → 0.0.353

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 (160) hide show
  1. package/dist/certificate/certificate.controller.d.ts +2 -2
  2. package/dist/certificate/certificate.controller.d.ts.map +1 -1
  3. package/dist/certificate/certificate.controller.js +8 -6
  4. package/dist/certificate/certificate.controller.js.map +1 -1
  5. package/dist/certificate/certificate.service.d.ts +5 -2
  6. package/dist/certificate/certificate.service.d.ts.map +1 -1
  7. package/dist/certificate/certificate.service.js +70 -6
  8. package/dist/certificate/certificate.service.js.map +1 -1
  9. package/dist/course/course-structure.controller.d.ts +24 -10
  10. package/dist/course/course-structure.controller.d.ts.map +1 -1
  11. package/dist/course/course-structure.controller.js +23 -2
  12. package/dist/course/course-structure.controller.js.map +1 -1
  13. package/dist/course/course-structure.service.d.ts +16 -8
  14. package/dist/course/course-structure.service.d.ts.map +1 -1
  15. package/dist/course/course-structure.service.js +61 -30
  16. package/dist/course/course-structure.service.js.map +1 -1
  17. package/dist/course/course-video-conversion.service.d.ts +37 -0
  18. package/dist/course/course-video-conversion.service.d.ts.map +1 -0
  19. package/dist/course/course-video-conversion.service.js +308 -0
  20. package/dist/course/course-video-conversion.service.js.map +1 -0
  21. package/dist/course/course.controller.d.ts +17 -0
  22. package/dist/course/course.controller.d.ts.map +1 -1
  23. package/dist/course/course.controller.js +23 -0
  24. package/dist/course/course.controller.js.map +1 -1
  25. package/dist/course/course.module.d.ts.map +1 -1
  26. package/dist/course/course.module.js +15 -2
  27. package/dist/course/course.module.js.map +1 -1
  28. package/dist/course/course.service.d.ts +15 -0
  29. package/dist/course/course.service.d.ts.map +1 -1
  30. package/dist/course/course.service.js +103 -49
  31. package/dist/course/course.service.js.map +1 -1
  32. package/dist/course/dto/create-course-structure-lesson.dto.d.ts +5 -1
  33. package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
  34. package/dist/course/dto/create-course-structure-lesson.dto.js +16 -2
  35. package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
  36. package/dist/course/dto/create-course.dto.d.ts +1 -0
  37. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  38. package/dist/course/dto/create-course.dto.js +9 -0
  39. package/dist/course/dto/create-course.dto.js.map +1 -1
  40. package/dist/enterprise/enterprise.controller.d.ts +3 -3
  41. package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
  42. package/dist/enterprise/enterprise.controller.js +0 -1
  43. package/dist/enterprise/enterprise.controller.js.map +1 -1
  44. package/dist/enterprise/enterprise.service.d.ts +3 -3
  45. package/dist/evaluation/evaluation.service.d.ts.map +1 -1
  46. package/dist/evaluation/evaluation.service.js +9 -2
  47. package/dist/evaluation/evaluation.service.js.map +1 -1
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/lms.module.d.ts.map +1 -1
  53. package/dist/lms.module.js +3 -0
  54. package/dist/lms.module.js.map +1 -1
  55. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts +6 -0
  56. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts.map +1 -0
  57. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js +33 -0
  58. package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js.map +1 -0
  59. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts +6 -0
  60. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts.map +1 -0
  61. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js +33 -0
  62. package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js.map +1 -0
  63. package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts +38 -0
  64. package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts.map +1 -0
  65. package/dist/video-resolution-profile/video-resolution-profile.controller.js +89 -0
  66. package/dist/video-resolution-profile/video-resolution-profile.controller.js.map +1 -0
  67. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts +26 -0
  68. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts.map +1 -0
  69. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js +160 -0
  70. package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js.map +1 -0
  71. package/dist/video-resolution-profile/video-resolution-profile.module.d.ts +3 -0
  72. package/dist/video-resolution-profile/video-resolution-profile.module.d.ts.map +1 -0
  73. package/dist/video-resolution-profile/video-resolution-profile.module.js +26 -0
  74. package/dist/video-resolution-profile/video-resolution-profile.module.js.map +1 -0
  75. package/dist/video-resolution-profile/video-resolution-profile.service.d.ts +45 -0
  76. package/dist/video-resolution-profile/video-resolution-profile.service.d.ts.map +1 -0
  77. package/dist/video-resolution-profile/video-resolution-profile.service.js +117 -0
  78. package/dist/video-resolution-profile/video-resolution-profile.service.js.map +1 -0
  79. package/hedhog/data/menu.yaml +17 -0
  80. package/hedhog/data/route.yaml +133 -0
  81. package/hedhog/data/video_resolution_profile.yaml +7 -0
  82. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +269 -324
  83. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +124 -70
  84. package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +7 -4
  85. package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +2 -2
  86. package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +2 -2
  87. package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +34 -4
  88. package/hedhog/frontend/app/_lib/editor/types.ts.ejs +28 -3
  89. package/hedhog/frontend/app/achievements/page.tsx.ejs +9 -3
  90. package/hedhog/frontend/app/bitcodes/page.tsx.ejs +9 -3
  91. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +7 -3
  92. package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +29 -8
  93. package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +14 -0
  94. package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +194 -9
  95. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +15 -5
  96. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +9 -5
  97. package/hedhog/frontend/app/classes/page.tsx.ejs +73 -47
  98. package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +19 -9
  99. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +24 -1
  100. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +1 -1
  101. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +1 -1
  102. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +28 -16
  103. package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +11 -6
  104. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +7 -4
  105. package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -0
  106. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +24 -87
  107. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +892 -411
  108. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1004 -293
  109. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +11 -11
  110. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +62 -52
  111. package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +2 -0
  112. package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -6
  113. package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +86 -1
  114. package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +3 -0
  115. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +1 -0
  116. package/hedhog/frontend/app/courses/page.tsx.ejs +112 -89
  117. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +1 -1
  118. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +10 -3
  119. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +8 -4
  120. package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +2 -2
  121. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +10 -4
  122. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +10 -3
  123. package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +10 -3
  124. package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +10 -3
  125. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +23 -9
  126. package/hedhog/frontend/app/exams/page.tsx.ejs +14 -6
  127. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +9 -3
  128. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +190 -17
  129. package/hedhog/frontend/app/layout.tsx.ejs +5 -1
  130. package/hedhog/frontend/app/paths/page.tsx.ejs +13 -5
  131. package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +10 -10
  132. package/hedhog/frontend/app/training/page.tsx.ejs +13 -5
  133. package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +607 -0
  134. package/hedhog/frontend/messages/en.json +250 -9
  135. package/hedhog/frontend/messages/pt.json +250 -9
  136. package/hedhog/table/course.yaml +4 -0
  137. package/hedhog/table/course_lesson_file.yaml +8 -0
  138. package/hedhog/table/course_video_resolution_profile.yaml +22 -0
  139. package/hedhog/table/video_resolution_profile.yaml +18 -0
  140. package/package.json +9 -8
  141. package/src/certificate/certificate.controller.ts +19 -14
  142. package/src/certificate/certificate.service.ts +106 -11
  143. package/src/course/course-structure.controller.ts +24 -2
  144. package/src/course/course-structure.service.ts +21 -4
  145. package/src/course/course-video-conversion.service.ts +415 -0
  146. package/src/course/course.controller.ts +18 -0
  147. package/src/course/course.module.ts +15 -2
  148. package/src/course/course.service.ts +72 -2
  149. package/src/course/dto/create-course-structure-lesson.dto.ts +13 -2
  150. package/src/course/dto/create-course.dto.ts +8 -0
  151. package/src/enterprise/enterprise.controller.ts +0 -1
  152. package/src/evaluation/evaluation.service.ts +9 -2
  153. package/src/index.ts +1 -0
  154. package/src/lms.module.ts +3 -0
  155. package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +16 -0
  156. package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +16 -0
  157. package/src/video-resolution-profile/video-resolution-profile.controller.ts +62 -0
  158. package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +128 -0
  159. package/src/video-resolution-profile/video-resolution-profile.module.ts +13 -0
  160. package/src/video-resolution-profile/video-resolution-profile.service.ts +117 -0
@@ -48,12 +48,12 @@ import {
48
48
  import { Separator } from '@/components/ui/separator';
49
49
  import {
50
50
  Sheet,
51
- SheetContent,
52
51
  SheetDescription,
53
52
  SheetFooter,
54
53
  SheetHeader,
55
54
  SheetTitle,
56
55
  } from '@/components/ui/sheet';
56
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
57
57
  import { Skeleton } from '@/components/ui/skeleton';
58
58
  import {
59
59
  Table,
@@ -642,7 +642,10 @@ export default function QuestoesPage() {
642
642
  });
643
643
 
644
644
  const questaoSchema = z.object({
645
- subjectId: z.coerce.number().int().min(1, t('sheet.validation.subjectRequired')),
645
+ subjectId: z.coerce
646
+ .number()
647
+ .int()
648
+ .min(1, t('sheet.validation.subjectRequired')),
646
649
  enunciado: z.string().min(5, t('sheet.validation.statementMin')),
647
650
  explicacao: z.string().optional(),
648
651
  questionType: z.enum([
@@ -739,7 +742,6 @@ export default function QuestoesPage() {
739
742
  setCurrentPage(1);
740
743
  }
741
744
 
742
-
743
745
  async function loadQuestionSubjectOptions(args: {
744
746
  page: number;
745
747
  pageSize: number;
@@ -1586,7 +1588,11 @@ export default function QuestoesPage() {
1586
1588
 
1587
1589
  {/* Create/Edit Sheet */}
1588
1590
  <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
1589
- <SheetContent
1591
+ <ResizableSheetContent
1592
+ sheetId="lms-exam-questions-form-sheet"
1593
+ defaultWidth={560}
1594
+ minWidth={420}
1595
+ maxWidth={920}
1590
1596
  side="right"
1591
1597
  className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
1592
1598
  >
@@ -1616,20 +1622,28 @@ export default function QuestoesPage() {
1616
1622
  value={field.value || null}
1617
1623
  valueType="number"
1618
1624
  placeholder={t('sheet.fields.subjectPlaceholder')}
1619
- searchPlaceholder={t('sheet.fields.subjectSearchPlaceholder')}
1625
+ searchPlaceholder={t(
1626
+ 'sheet.fields.subjectSearchPlaceholder'
1627
+ )}
1620
1628
  entityLabel={t('sheet.fields.subjectEntityLabel')}
1621
1629
  noResultsLabel={t('sheet.fields.subjectNoResults')}
1622
1630
  options={assuntosLocais}
1623
1631
  loadOptions={loadQuestionSubjectOptions}
1624
1632
  getOptionLabel={(option) => option.name}
1625
- getOptionDescription={(option) => option.description ?? undefined}
1633
+ getOptionDescription={(option) =>
1634
+ option.description ?? undefined
1635
+ }
1626
1636
  createTitle={t('sheet.fields.subjectCreateTitle')}
1627
- createDescription={t('sheet.fields.subjectCreateDescription')}
1637
+ createDescription={t(
1638
+ 'sheet.fields.subjectCreateDescription'
1639
+ )}
1628
1640
  createFields={[
1629
1641
  {
1630
1642
  name: 'name',
1631
1643
  label: t('sheet.fields.subjectCreateNameLabel'),
1632
- placeholder: t('sheet.fields.subjectCreateNamePlaceholder'),
1644
+ placeholder: t(
1645
+ 'sheet.fields.subjectCreateNamePlaceholder'
1646
+ ),
1633
1647
  required: true,
1634
1648
  },
1635
1649
  ]}
@@ -1983,7 +1997,7 @@ export default function QuestoesPage() {
1983
1997
  </SheetFooter>
1984
1998
  </form>
1985
1999
  </Form>
1986
- </SheetContent>
2000
+ </ResizableSheetContent>
1987
2001
  </Sheet>
1988
2002
 
1989
2003
  {/* Delete Dialog */}
@@ -43,12 +43,12 @@ import {
43
43
  } from '@/components/ui/select';
44
44
  import {
45
45
  Sheet,
46
- SheetContent,
47
46
  SheetDescription,
48
47
  SheetFooter,
49
48
  SheetHeader,
50
49
  SheetTitle,
51
50
  } from '@/components/ui/sheet';
51
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
52
52
  import { Skeleton } from '@/components/ui/skeleton';
53
53
  import { Switch } from '@/components/ui/switch';
54
54
  import {
@@ -59,6 +59,7 @@ import {
59
59
  TableHeader,
60
60
  TableRow,
61
61
  } from '@/components/ui/table';
62
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
62
63
  import { usePersistedViewMode } from '@/hooks/use-persisted-view-mode';
63
64
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
64
65
  import { zodResolver } from '@hookform/resolvers/zod';
@@ -83,7 +84,6 @@ import {
83
84
  import { useTranslations } from 'next-intl';
84
85
  import { useRouter } from 'next/navigation';
85
86
  import { useEffect, useMemo, useState } from 'react';
86
- import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
87
87
  import { Controller, useForm, useWatch } from 'react-hook-form';
88
88
  import { toast } from 'sonner';
89
89
  import { z } from 'zod';
@@ -787,7 +787,9 @@ export default function ExamesPage() {
787
787
  <motion.div key={exame.id} variants={fadeUp}>
788
788
  <Card
789
789
  className="group relative h-95 cursor-pointer overflow-hidden border-border/70 shadow-sm transition-all duration-200 hover:border-border hover:shadow-md"
790
- onDoubleClick={() => openEditSheet(exame)}
790
+ onDoubleClick={() =>
791
+ router.push(`/lms/exams/${exame.id}`)
792
+ }
791
793
  title={t('cards.tooltip')}
792
794
  >
793
795
  <div
@@ -966,7 +968,9 @@ export default function ExamesPage() {
966
968
  <TableRow
967
969
  key={exame.id}
968
970
  className="cursor-pointer"
969
- onDoubleClick={() => openEditSheet(exame)}
971
+ onDoubleClick={() =>
972
+ router.push(`/lms/exams/${exame.id}`)
973
+ }
970
974
  title={t('cards.tooltip')}
971
975
  >
972
976
  <TableCell>
@@ -1076,7 +1080,11 @@ export default function ExamesPage() {
1076
1080
 
1077
1081
  {/* Sheet */}
1078
1082
  <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
1079
- <SheetContent
1083
+ <ResizableSheetContent
1084
+ sheetId="lms-exams-form-sheet"
1085
+ defaultWidth={560}
1086
+ minWidth={420}
1087
+ maxWidth={920}
1080
1088
  side="right"
1081
1089
  className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
1082
1090
  >
@@ -1325,7 +1333,7 @@ export default function ExamesPage() {
1325
1333
  </Button>
1326
1334
  </SheetFooter>
1327
1335
  </form>
1328
- </SheetContent>
1336
+ </ResizableSheetContent>
1329
1337
  </Sheet>
1330
1338
 
1331
1339
  {/* Delete Dialog */}
@@ -43,10 +43,10 @@ import {
43
43
  } from '@/components/ui/select';
44
44
  import {
45
45
  Sheet,
46
- SheetContent,
47
46
  SheetHeader,
48
47
  SheetTitle,
49
48
  } from '@/components/ui/sheet';
49
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
50
50
  import { Skeleton } from '@/components/ui/skeleton';
51
51
  import {
52
52
  Table,
@@ -176,7 +176,13 @@ function SkillFormSheet({
176
176
 
177
177
  return (
178
178
  <Sheet open={open} onOpenChange={onOpenChange}>
179
- <SheetContent className="w-full overflow-y-auto sm:max-w-md">
179
+ <ResizableSheetContent
180
+ sheetId="lms-instructor-skills-form-sheet"
181
+ defaultWidth={520}
182
+ minWidth={400}
183
+ maxWidth={860}
184
+ className="w-full overflow-y-auto sm:max-w-md"
185
+ >
180
186
  <SheetHeader>
181
187
  <SheetTitle>
182
188
  {skillToEdit ? t('sheet.editTitle') : t('sheet.createTitle')}
@@ -250,7 +256,7 @@ function SkillFormSheet({
250
256
  </div>
251
257
  </form>
252
258
  </Form>
253
- </SheetContent>
259
+ </ResizableSheetContent>
254
260
  </Sheet>
255
261
  );
256
262
  }
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { PersonPicker } from '@/app/(app)/(libraries)/contact/_components/person-picker';
4
- import { PersonFormSheet } from '@/app/(app)/(libraries)/contact/person/_components/person-form-sheet';
3
+ import { PersonPicker } from '@/app/(app)/(libraries)/crm/_components/person-picker';
4
+ import { PersonFormSheet } from '@/app/(app)/(libraries)/crm/person/_components/person-form-sheet';
5
5
  import type {
6
6
  ContactTypeOption,
7
7
  DocumentTypeOption,
8
8
  Person,
9
- } from '@/app/(app)/(libraries)/contact/person/_components/person-types';
9
+ } from '@/app/(app)/(libraries)/crm/person/_components/person-types';
10
10
  import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
11
11
  import { Badge } from '@/components/ui/badge';
12
12
  import { Button } from '@/components/ui/button';
@@ -23,12 +23,12 @@ import {
23
23
  } from '@/components/ui/select';
24
24
  import {
25
25
  Sheet,
26
- SheetContent,
27
26
  SheetDescription,
28
27
  SheetFooter,
29
28
  SheetHeader,
30
29
  SheetTitle,
31
30
  } from '@/components/ui/sheet';
31
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
32
32
  import { Switch } from '@/components/ui/switch';
33
33
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
34
34
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
@@ -69,6 +69,27 @@ type ClassGroupItem = {
69
69
  totalSlots: number | null;
70
70
  };
71
71
 
72
+ type EvaluationItem = {
73
+ id: number;
74
+ topicName: string;
75
+ topicDescription: string | null;
76
+ targetType: string;
77
+ targetName: string;
78
+ score: number;
79
+ comment: string | null;
80
+ evaluatorName: string | null;
81
+ evaluatorId: number | null;
82
+ courseName: string | null;
83
+ courseId: number | null;
84
+ className: string | null;
85
+ classId: number | null;
86
+ instructorName: string | null;
87
+ instructorId: number | null;
88
+ createdAt: string;
89
+ topicAvgScore: number;
90
+ topicRatingCount: number;
91
+ };
92
+
72
93
  function toKebabCase(str: string) {
73
94
  return (
74
95
  str
@@ -127,6 +148,8 @@ export function InstructorFormSheet({
127
148
  const [classSheetOpen, setClassSheetOpen] = useState(false);
128
149
  const [editingClassId, setEditingClassId] = useState<number | null>(null);
129
150
  const TURMAS_PAGE_SIZE = 6;
151
+ const [avaliacoesPage, setAvaliacoesPage] = useState(1);
152
+ const AVALIACOES_PAGE_SIZE = 6;
130
153
  const isEditing = typeof instructorId === 'number' && instructorId > 0;
131
154
  const qualificationOptions = [
132
155
  {
@@ -329,6 +352,38 @@ export function InstructorFormSheet({
329
352
  const turmasData = turmasResult?.data ?? [];
330
353
  const turmasLastPage = turmasResult?.lastPage ?? 1;
331
354
 
355
+ const {
356
+ data: avaliacoesResult,
357
+ isLoading: loadingAvaliacoes,
358
+ } = useQuery<{
359
+ data: EvaluationItem[];
360
+ total: number;
361
+ lastPage: number;
362
+ }>({
363
+ queryKey: ['lms-instructor-avaliacoes', instructorId, avaliacoesPage],
364
+ queryFn: async () => {
365
+ if (!instructorId) return { data: [], total: 0, lastPage: 1 };
366
+ const params = new URLSearchParams({
367
+ instructorId: String(instructorId),
368
+ page: String(avaliacoesPage),
369
+ pageSize: String(AVALIACOES_PAGE_SIZE),
370
+ });
371
+ const response = await request<{
372
+ data: EvaluationItem[];
373
+ total: number;
374
+ lastPage: number;
375
+ }>({
376
+ url: `/lms/evaluations?${params.toString()}`,
377
+ method: 'GET',
378
+ });
379
+ return response.data;
380
+ },
381
+ enabled: isEditing && open && activeTab === 'avaliacoes',
382
+ placeholderData: (prev) => prev,
383
+ });
384
+ const avaliacoesData = avaliacoesResult?.data ?? [];
385
+ const avaliacoesLastPage = avaliacoesResult?.lastPage ?? 1;
386
+
332
387
  useEffect(() => {
333
388
  const timeout = setTimeout(() => {
334
389
  setDebouncedTurmasSearch(turmasSearchInput.trim());
@@ -377,7 +432,7 @@ export function InstructorFormSheet({
377
432
  }
378
433
  }, [isEditing, existingInstructor, open, reset]);
379
434
 
380
- // Reset to first tab and turmas page when sheet closes
435
+ // Reset to first tab and turmas/avaliacoes page when sheet closes
381
436
  useEffect(() => {
382
437
  if (!open) {
383
438
  setActiveTab('detalhes');
@@ -387,6 +442,7 @@ export function InstructorFormSheet({
387
442
  setTurmasStatusFilter('all');
388
443
  setClassSheetOpen(false);
389
444
  setEditingClassId(null);
445
+ setAvaliacoesPage(1);
390
446
  }
391
447
  }, [open]);
392
448
 
@@ -489,9 +545,13 @@ export function InstructorFormSheet({
489
545
  return (
490
546
  <>
491
547
  <Sheet open={open} onOpenChange={handleSheetClose}>
492
- <SheetContent
548
+ <ResizableSheetContent
549
+ sheetId="lms-instructors-form-sheet"
550
+ defaultWidth={640}
551
+ minWidth={460}
552
+ maxWidth={1100}
493
553
  side="right"
494
- className="flex w-full max-w-lg flex-col overflow-hidden p-0 sm:max-w-xl"
554
+ className="flex w-full flex-col overflow-hidden p-0"
495
555
  >
496
556
  {/* ── Instructor header (edit mode) ── */}
497
557
  {isEditing && existingInstructor ? (
@@ -1136,15 +1196,128 @@ export function InstructorFormSheet({
1136
1196
  value="avaliacoes"
1137
1197
  className="flex min-h-0 flex-1 flex-col overflow-hidden data-[state=inactive]:hidden"
1138
1198
  >
1139
- {/* TODO: implementar endpoint de avaliações de instrutor */}
1140
- <div className="flex flex-1 flex-col items-center justify-center px-6 py-12 text-center">
1141
- <Star className="mb-3 h-10 w-10 text-muted-foreground/40" />
1142
- <p className="text-sm font-medium text-muted-foreground">
1143
- {t('form.evaluations.comingSoonTitle')}
1144
- </p>
1145
- <p className="mt-1 text-xs text-muted-foreground">
1146
- {t('form.evaluations.comingSoonDescription')}
1147
- </p>
1199
+ <div className="flex min-h-0 flex-1 flex-col">
1200
+ {/* List area */}
1201
+ <div className="flex-1 overflow-y-auto px-6 py-4">
1202
+ {loadingAvaliacoes ? (
1203
+ <div className="flex flex-col gap-3">
1204
+ {Array.from({ length: 3 }).map((_, i) => (
1205
+ <div
1206
+ key={i}
1207
+ className="flex flex-col gap-2 rounded-lg border p-4"
1208
+ >
1209
+ <div className="flex items-center justify-between">
1210
+ <div className="h-4 w-32 animate-pulse rounded bg-muted" />
1211
+ <div className="h-4 w-16 animate-pulse rounded bg-muted" />
1212
+ </div>
1213
+ <div className="h-3 w-48 animate-pulse rounded bg-muted" />
1214
+ <div className="h-3 w-64 animate-pulse rounded bg-muted" />
1215
+ </div>
1216
+ ))}
1217
+ </div>
1218
+ ) : avaliacoesData.length === 0 ? (
1219
+ <div className="flex flex-col items-center justify-center py-12 text-center">
1220
+ <Star className="mb-3 h-10 w-10 text-muted-foreground/40" />
1221
+ <p className="text-sm font-medium text-muted-foreground">
1222
+ {t('form.evaluations.emptyTitle')}
1223
+ </p>
1224
+ <p className="mt-1 text-xs text-muted-foreground">
1225
+ {t('form.evaluations.emptyDescription')}
1226
+ </p>
1227
+ </div>
1228
+ ) : (
1229
+ <div className="flex flex-col gap-3">
1230
+ {avaliacoesData.map((ev) => (
1231
+ <div
1232
+ key={ev.id}
1233
+ className="flex flex-col gap-2 rounded-lg border p-4"
1234
+ >
1235
+ {/* Header: topic name + score */}
1236
+ <div className="flex items-start justify-between gap-2">
1237
+ <p className="text-sm font-medium leading-snug">
1238
+ {ev.topicName}
1239
+ </p>
1240
+ <div className="flex shrink-0 items-center gap-1">
1241
+ {Array.from({ length: 5 }).map((_, i) => (
1242
+ <Star
1243
+ key={i}
1244
+ className={`h-3.5 w-3.5 ${
1245
+ i < Math.round(ev.score / 2)
1246
+ ? 'fill-amber-400 text-amber-400'
1247
+ : 'fill-none text-muted-foreground/40'
1248
+ }`}
1249
+ />
1250
+ ))}
1251
+ <span className="ml-1 text-xs font-medium tabular-nums text-muted-foreground">
1252
+ {ev.score.toFixed(1)}{t('form.evaluations.of')}10
1253
+ </span>
1254
+ </div>
1255
+ </div>
1256
+
1257
+ {/* Target + course/class info */}
1258
+ <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
1259
+ {ev.className && (
1260
+ <span className="text-xs text-muted-foreground">
1261
+ <BookOpen className="mr-0.5 inline h-3 w-3" />
1262
+ {ev.className}
1263
+ </span>
1264
+ )}
1265
+ {ev.courseName && (
1266
+ <span className="text-xs text-muted-foreground">
1267
+ · {ev.courseName}
1268
+ </span>
1269
+ )}
1270
+ </div>
1271
+
1272
+ {/* Evaluator + date */}
1273
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
1274
+ <span>
1275
+ {ev.evaluatorName ?? t('form.evaluations.anonymous')}
1276
+ </span>
1277
+ <span>·</span>
1278
+ <span>
1279
+ {new Date(ev.createdAt).toLocaleDateString()}
1280
+ </span>
1281
+ </div>
1282
+
1283
+ {/* Comment */}
1284
+ {ev.comment && (
1285
+ <p className="line-clamp-2 text-xs text-muted-foreground italic">
1286
+ "{ev.comment}"
1287
+ </p>
1288
+ )}
1289
+ </div>
1290
+ ))}
1291
+ </div>
1292
+ )}
1293
+ </div>
1294
+
1295
+ {/* Pagination */}
1296
+ {avaliacoesLastPage > 1 && (
1297
+ <div className="flex shrink-0 items-center justify-center gap-2 border-t px-6 py-3">
1298
+ <Button
1299
+ type="button"
1300
+ variant="outline"
1301
+ size="icon"
1302
+ disabled={avaliacoesPage <= 1}
1303
+ onClick={() => setAvaliacoesPage((p) => p - 1)}
1304
+ >
1305
+ <ChevronLeft className="h-4 w-4" />
1306
+ </Button>
1307
+ <span className="text-xs text-muted-foreground">
1308
+ {avaliacoesPage} / {avaliacoesLastPage}
1309
+ </span>
1310
+ <Button
1311
+ type="button"
1312
+ variant="outline"
1313
+ size="icon"
1314
+ disabled={avaliacoesPage >= avaliacoesLastPage}
1315
+ onClick={() => setAvaliacoesPage((p) => p + 1)}
1316
+ >
1317
+ <ChevronRight className="h-4 w-4" />
1318
+ </Button>
1319
+ </div>
1320
+ )}
1148
1321
  </div>
1149
1322
  </TabsContent>
1150
1323
  </Tabs>
@@ -1343,7 +1516,7 @@ export function InstructorFormSheet({
1343
1516
  </SheetFooter>
1344
1517
  </form>
1345
1518
  )}
1346
- </SheetContent>
1519
+ </ResizableSheetContent>
1347
1520
  </Sheet>
1348
1521
 
1349
1522
  <ClassFormSheet
@@ -1,5 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
3
  export default function LmsLayout({ children }: { children: ReactNode }) {
4
- return <div>{children}</div>;
4
+ return (
5
+ <div className="flex h-full min-h-0 flex-col overflow-hidden">
6
+ {children}
7
+ </div>
8
+ );
5
9
  }
@@ -47,12 +47,12 @@ import {
47
47
  import { Separator } from '@/components/ui/separator';
48
48
  import {
49
49
  Sheet,
50
- SheetContent,
51
50
  SheetDescription,
52
51
  SheetFooter,
53
52
  SheetHeader,
54
53
  SheetTitle,
55
54
  } from '@/components/ui/sheet';
55
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
56
56
  import { Skeleton } from '@/components/ui/skeleton';
57
57
  import { Switch } from '@/components/ui/switch';
58
58
  import {
@@ -1883,7 +1883,11 @@ export default function TrainingPage() {
1883
1883
 
1884
1884
  {/* Sheet */}
1885
1885
  <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
1886
- <SheetContent
1886
+ <ResizableSheetContent
1887
+ sheetId="lms-paths-form-sheet"
1888
+ defaultWidth={560}
1889
+ minWidth={420}
1890
+ maxWidth={920}
1887
1891
  side="right"
1888
1892
  className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
1889
1893
  >
@@ -2241,7 +2245,7 @@ export default function TrainingPage() {
2241
2245
  </Button>
2242
2246
  </SheetFooter>
2243
2247
  </form>
2244
- </SheetContent>
2248
+ </ResizableSheetContent>
2245
2249
  </Sheet>
2246
2250
 
2247
2251
  <CourseFormSheet
@@ -2257,7 +2261,11 @@ export default function TrainingPage() {
2257
2261
  />
2258
2262
 
2259
2263
  <Sheet open={examSheetOpen} onOpenChange={setExamSheetOpen}>
2260
- <SheetContent
2264
+ <ResizableSheetContent
2265
+ sheetId="lms-paths-create-exam-sheet"
2266
+ defaultWidth={560}
2267
+ minWidth={420}
2268
+ maxWidth={920}
2261
2269
  side="right"
2262
2270
  className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
2263
2271
  >
@@ -2370,7 +2378,7 @@ export default function TrainingPage() {
2370
2378
  </Button>
2371
2379
  </SheetFooter>
2372
2380
  </form>
2373
- </SheetContent>
2381
+ </ResizableSheetContent>
2374
2382
  </Sheet>
2375
2383
 
2376
2384
  {/* Delete Dialog */}
@@ -25,12 +25,8 @@ import {
25
25
  PopoverContent,
26
26
  PopoverTrigger,
27
27
  } from '@/components/ui/popover';
28
- import {
29
- Sheet,
30
- SheetContent,
31
- SheetHeader,
32
- SheetTitle,
33
- } from '@/components/ui/sheet';
28
+ import { Sheet, SheetHeader, SheetTitle } from '@/components/ui/sheet';
29
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
34
30
  import { Skeleton } from '@/components/ui/skeleton';
35
31
  import {
36
32
  Table,
@@ -981,7 +977,11 @@ export default function EvaluationsReportPage() {
981
977
 
982
978
  {/* Detail Sheet */}
983
979
  <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
984
- <SheetContent
980
+ <ResizableSheetContent
981
+ sheetId="lms-reports-evaluations-detail-sheet"
982
+ defaultWidth={640}
983
+ minWidth={460}
984
+ maxWidth={1080}
985
985
  side="right"
986
986
  className="w-full overflow-y-auto p-0 sm:max-w-xl"
987
987
  >
@@ -1166,7 +1166,7 @@ export default function EvaluationsReportPage() {
1166
1166
  <div className="grid grid-cols-2 gap-3">
1167
1167
  <button
1168
1168
  type="button"
1169
- onClick={() => window.open('/contact/accounts', '_blank')}
1169
+ onClick={() => window.open('/crm/accounts', '_blank')}
1170
1170
  className="flex items-center gap-3 rounded-lg border border-border/50 bg-card p-3 text-left transition-colors hover:bg-muted/40"
1171
1171
  >
1172
1172
  <Avatar className="size-9 shrink-0">
@@ -1188,7 +1188,7 @@ export default function EvaluationsReportPage() {
1188
1188
  <button
1189
1189
  type="button"
1190
1190
  onClick={() =>
1191
- window.open('/contact/accounts', '_blank')
1191
+ window.open('/crm/accounts', '_blank')
1192
1192
  }
1193
1193
  className="flex items-center gap-3 rounded-lg border border-border/50 bg-card p-3 text-left transition-colors hover:bg-muted/40"
1194
1194
  >
@@ -1276,7 +1276,7 @@ export default function EvaluationsReportPage() {
1276
1276
  </div>
1277
1277
  </>
1278
1278
  )}
1279
- </SheetContent>
1279
+ </ResizableSheetContent>
1280
1280
  </Sheet>
1281
1281
  </Page>
1282
1282
  );
@@ -46,12 +46,12 @@ import {
46
46
  import { Separator } from '@/components/ui/separator';
47
47
  import {
48
48
  Sheet,
49
- SheetContent,
50
49
  SheetDescription,
51
50
  SheetFooter,
52
51
  SheetHeader,
53
52
  SheetTitle,
54
53
  } from '@/components/ui/sheet';
54
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
55
55
  import { Skeleton } from '@/components/ui/skeleton';
56
56
  import { Switch } from '@/components/ui/switch';
57
57
  import {
@@ -1873,7 +1873,11 @@ export default function TrainingPage() {
1873
1873
 
1874
1874
  {/* Sheet */}
1875
1875
  <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
1876
- <SheetContent
1876
+ <ResizableSheetContent
1877
+ sheetId="lms-training-form-sheet"
1878
+ defaultWidth={560}
1879
+ minWidth={420}
1880
+ maxWidth={920}
1877
1881
  side="right"
1878
1882
  className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
1879
1883
  >
@@ -2216,7 +2220,7 @@ export default function TrainingPage() {
2216
2220
  </Button>
2217
2221
  </SheetFooter>
2218
2222
  </form>
2219
- </SheetContent>
2223
+ </ResizableSheetContent>
2220
2224
  </Sheet>
2221
2225
 
2222
2226
  <CourseFormSheet
@@ -2232,7 +2236,11 @@ export default function TrainingPage() {
2232
2236
  />
2233
2237
 
2234
2238
  <Sheet open={examSheetOpen} onOpenChange={setExamSheetOpen}>
2235
- <SheetContent
2239
+ <ResizableSheetContent
2240
+ sheetId="lms-training-create-exam-sheet"
2241
+ defaultWidth={560}
2242
+ minWidth={420}
2243
+ maxWidth={920}
2236
2244
  side="right"
2237
2245
  className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
2238
2246
  >
@@ -2345,7 +2353,7 @@ export default function TrainingPage() {
2345
2353
  </Button>
2346
2354
  </SheetFooter>
2347
2355
  </form>
2348
- </SheetContent>
2356
+ </ResizableSheetContent>
2349
2357
  </Sheet>
2350
2358
 
2351
2359
  {/* Delete Dialog */}