@hed-hog/lms 0.0.350 → 0.0.351

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 +7 -6
  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
@@ -33,7 +33,7 @@ export function CourseCertificateCard({
33
33
  return (
34
34
  <CourseSectionCard
35
35
  title={t('form.sections.certification')}
36
- description="Escolha o modelo usado na emissão e deixe o impacto desse vínculo explícito."
36
+ description={t('form.sectionDescriptions.certification')}
37
37
  icon={Award}
38
38
  >
39
39
  <FormField
@@ -47,21 +47,31 @@ export function CourseCertificateCard({
47
47
  onChange={(value) => field.onChange(String(value ?? ''))}
48
48
  options={options}
49
49
  placeholder={t('form.fields.certificateModel.placeholder')}
50
- entityLabel="modelo de certificado"
51
- createActionLabel="Criar modelo"
52
- createTitle="Criar modelo de certificado"
53
- createDescription="Crie um modelo base e já o vincule ao curso sem sair desta página."
50
+ entityLabel={t('form.fields.certificateModel.entityLabel')}
51
+ createActionLabel={t('form.fields.certificateModel.createAction')}
52
+ createTitle={t('form.fields.certificateModel.createTitle')}
53
+ createDescription={t(
54
+ 'form.fields.certificateModel.createDescription'
55
+ )}
54
56
  createFields={[
55
57
  {
56
58
  name: 'name',
57
- label: 'Nome do modelo',
58
- placeholder: 'Ex: Certificado institucional',
59
+ label: t(
60
+ 'form.fields.certificateModel.createFields.name.label'
61
+ ),
62
+ placeholder: t(
63
+ 'form.fields.certificateModel.createFields.name.placeholder'
64
+ ),
59
65
  required: true,
60
66
  },
61
67
  {
62
68
  name: 'description',
63
- label: 'Descrição',
64
- placeholder: 'Resumo interno para identificar o template',
69
+ label: t(
70
+ 'form.fields.certificateModel.createFields.description.label'
71
+ ),
72
+ placeholder: t(
73
+ 'form.fields.certificateModel.createFields.description.placeholder'
74
+ ),
65
75
  },
66
76
  ]}
67
77
  mapSearchToCreateValues={(search) => ({
@@ -46,7 +46,7 @@ export function CourseClassificationCard({
46
46
  description="Defina estágio, formato e identidade visual do curso."
47
47
  icon={Settings2}
48
48
  >
49
- <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
49
+ <div className="grid gap-2 sm:gap-3 md:grid-cols-2 xl:grid-cols-3">
50
50
  <FormField
51
51
  control={form.control}
52
52
  name="nivel"
@@ -181,6 +181,29 @@ export function CourseClassificationCard({
181
181
  </FormItem>
182
182
  )}
183
183
  />
184
+
185
+ <FormField
186
+ control={form.control}
187
+ name="code"
188
+ render={({ field }) => (
189
+ <FormItem>
190
+ <FormLabel>{t('form.fields.code.label')}</FormLabel>
191
+ <FormControl>
192
+ <Input
193
+ {...field}
194
+ placeholder={t('form.fields.code.placeholder')}
195
+ className="h-8 font-mono"
196
+ onChange={(e) =>
197
+ field.onChange(
198
+ e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '')
199
+ )
200
+ }
201
+ />
202
+ </FormControl>
203
+ <FormMessage />
204
+ </FormItem>
205
+ )}
206
+ />
184
207
  </div>
185
208
  </CourseSectionCard>
186
209
  );
@@ -23,7 +23,7 @@ export function CourseContentCard({ form }: CourseContentCardProps) {
23
23
  description="Concentre os textos longos que apoiam venda, orientação pedagógica e publicação."
24
24
  icon={FileText}
25
25
  >
26
- <div className="grid gap-3">
26
+ <div className="grid gap-2 sm:gap-3">
27
27
  <FormField
28
28
  control={form.control}
29
29
  name="objetivos"
@@ -22,7 +22,7 @@ export function CourseMainInfoCard({ form, t }: CourseMainInfoCardProps) {
22
22
  return (
23
23
  <CourseSectionCard
24
24
  title={t('form.sections.basicInfo')}
25
- description="Os identificadores e a comunicação principal do curso ficam aqui."
25
+ description={t('form.sectionDescriptions.basicInfo')}
26
26
  icon={NotebookPen}
27
27
  >
28
28
  <div className="grid gap-3 md:grid-cols-2">
@@ -46,25 +46,31 @@ export function CourseRelationsCard({
46
46
  return (
47
47
  <CourseSectionCard
48
48
  title={t('form.sections.relations')}
49
- description="Agrupe os vínculos que ajudam a encontrar, operar e ensinar este curso."
49
+ description={t('form.sectionDescriptions.relations')}
50
50
  icon={Network}
51
51
  >
52
- <div className="space-y-3">
53
- <div className="grid items-start gap-3 lg:grid-cols-2">
52
+ <div className="space-y-2 sm:space-y-3">
53
+ <div className="grid items-start gap-2 sm:gap-3 lg:grid-cols-2">
54
54
  <FormField
55
55
  control={form.control}
56
56
  name="operationsProjectId"
57
57
  render={({ field }) => (
58
58
  <FormItem className="self-start">
59
- <FormLabel>Projeto vinculado</FormLabel>
59
+ <FormLabel>
60
+ {t('form.fields.operationsProject.label')}
61
+ </FormLabel>
60
62
  <EntityPicker<PickerOption>
61
63
  value={field.value}
62
64
  onChange={(value) => field.onChange(String(value ?? ''))}
63
65
  options={projectOptions}
64
- placeholder="Selecione um projeto do Operations"
65
- searchPlaceholder="Buscar projeto..."
66
- entityLabel="projeto"
67
- emptyStateDescription="Nenhum projeto encontrado"
66
+ placeholder={t('form.fields.operationsProject.placeholder')}
67
+ searchPlaceholder={t(
68
+ 'form.fields.operationsProject.searchPlaceholder'
69
+ )}
70
+ entityLabel={t('form.fields.operationsProject.entityLabel')}
71
+ emptyStateDescription={t(
72
+ 'form.fields.operationsProject.noResults'
73
+ )}
68
74
  getOptionValue={(option) => option.value}
69
75
  getOptionLabel={(option) => option.label}
70
76
  getOptionDescription={(option) =>
@@ -93,15 +99,21 @@ export function CourseRelationsCard({
93
99
  )}
94
100
  entityLabel="categoria"
95
101
  emptyStateDescription={t('form.fields.categories.noResults')}
96
- emptyHint="Nenhuma categoria vinculada ainda."
97
- createActionLabel="Criar categoria"
98
- createTitle="Criar categoria"
99
- createDescription="Cadastre uma categoria rapidamente sem sair da edição do curso."
102
+ emptyHint={t('form.fields.categories.emptyHint')}
103
+ createActionLabel={t('form.fields.categories.createAction')}
104
+ createTitle={t('form.fields.categories.createTitle')}
105
+ createDescription={t(
106
+ 'form.fields.categories.createDescription'
107
+ )}
100
108
  createFields={[
101
109
  {
102
110
  name: 'name',
103
- label: 'Nome da categoria',
104
- placeholder: 'Ex: Desenvolvimento Web',
111
+ label: t(
112
+ 'form.fields.categories.createFields.name.label'
113
+ ),
114
+ placeholder: t(
115
+ 'form.fields.categories.createFields.name.placeholder'
116
+ ),
105
117
  required: true,
106
118
  },
107
119
  ]}
@@ -132,8 +144,8 @@ export function CourseRelationsCard({
132
144
  )}
133
145
  entityLabel="instrutor"
134
146
  emptyStateDescription={t('form.fields.instructors.noResults')}
135
- emptyHint="Nenhum instrutor vinculado ainda."
136
- createActionLabel="Cadastrar instrutor"
147
+ emptyHint={t('form.fields.instructors.emptyHint')}
148
+ createActionLabel={t('form.fields.instructors.createAction')}
137
149
  onCreateClick={onCreateInstructor}
138
150
  variant="instructor"
139
151
  renderOptionMeta
@@ -29,14 +29,19 @@ export function CourseSectionCard({
29
29
  action,
30
30
  }: CourseSectionCardProps) {
31
31
  const iconEl = Icon ? (
32
- <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg border border-border/60 bg-muted/30 text-foreground">
33
- <Icon className="h-3.5 w-3.5" />
32
+ <div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md border border-border/60 bg-muted/30 text-foreground sm:h-7 sm:w-7 sm:rounded-lg">
33
+ <Icon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
34
34
  </div>
35
35
  ) : null;
36
36
 
37
37
  return (
38
- <Card className={cn('border-border/70 shadow-sm', className)}>
39
- <CardHeader className="space-y-1.5 pb-3">
38
+ <Card
39
+ className={cn(
40
+ 'border-border/70 gap-3 py-3 shadow-sm sm:gap-6 sm:py-6',
41
+ className
42
+ )}
43
+ >
44
+ <CardHeader className="space-y-1 pb-2 px-3 sm:space-y-1.5 sm:pb-3 sm:px-6">
40
45
  <div className="flex items-center justify-between gap-3">
41
46
  <div className="flex min-w-0 items-center gap-2">
42
47
  {Icon && description ? (
@@ -59,7 +64,7 @@ export function CourseSectionCard({
59
64
  ) : (
60
65
  iconEl
61
66
  )}
62
- <CardTitle className="text-sm font-semibold tracking-tight">
67
+ <CardTitle className="text-xs font-semibold tracking-tight sm:text-sm">
63
68
  {title}
64
69
  </CardTitle>
65
70
  </div>
@@ -68,7 +73,7 @@ export function CourseSectionCard({
68
73
  </div>
69
74
  </CardHeader>
70
75
 
71
- <CardContent className={cn('pt-0', contentClassName)}>
76
+ <CardContent className={cn('px-3 pt-0 sm:px-6', contentClassName)}>
72
77
  {children}
73
78
  </CardContent>
74
79
  </Card>
@@ -1,5 +1,6 @@
1
1
  import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
2
2
  import { CalendarDays, Percent, Users, Video } from 'lucide-react';
3
+ import { useTranslations } from 'next-intl';
3
4
 
4
5
  interface CourseSummaryCardProps {
5
6
  course: {
@@ -11,12 +12,14 @@ interface CourseSummaryCardProps {
11
12
  }
12
13
 
13
14
  export function CourseSummaryCard({ course }: CourseSummaryCardProps) {
15
+ const t = useTranslations('lms.CursoEditPage');
16
+
14
17
  return (
15
18
  <KpiCardsGrid
16
19
  items={[
17
20
  {
18
21
  key: 'students',
19
- title: 'Total Alunos',
22
+ title: t('kpis.totalStudents'),
20
23
  value: (course?.totalAlunos ?? 0).toLocaleString('pt-BR'),
21
24
  icon: Users,
22
25
  layout: 'compact',
@@ -25,7 +28,7 @@ export function CourseSummaryCard({ course }: CourseSummaryCardProps) {
25
28
  },
26
29
  {
27
30
  key: 'completion',
28
- title: 'Conclusao Media',
31
+ title: t('kpis.avgCompletion'),
29
32
  value: `${course?.conclusaoMedia ?? 0}%`,
30
33
  icon: Percent,
31
34
  layout: 'compact',
@@ -35,7 +38,7 @@ export function CourseSummaryCard({ course }: CourseSummaryCardProps) {
35
38
  },
36
39
  {
37
40
  key: 'lessons',
38
- title: 'Total de Aulas',
41
+ title: t('kpis.totalLessons'),
39
42
  value: String(course?.totalAulas ?? 0),
40
43
  icon: Video,
41
44
  layout: 'compact',
@@ -44,7 +47,7 @@ export function CourseSummaryCard({ course }: CourseSummaryCardProps) {
44
47
  },
45
48
  {
46
49
  key: 'sessions',
47
- title: 'Sessoes',
50
+ title: t('kpis.sessions'),
48
51
  value: String(course?.totalSessoes ?? 0),
49
52
  icon: CalendarDays,
50
53
  layout: 'compact',
@@ -1,5 +1,6 @@
1
1
  export type CourseEditFormValues = {
2
2
  slug: string;
3
+ code: string;
3
4
  nomeInterno: string;
4
5
  tituloComercial: string;
5
6
  descricaoPublica: string;
@@ -1,9 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { use, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { use, useCallback, useEffect, useRef, useState } from 'react';
4
4
 
5
- import { Page, PageHeader } from '@/components/entity-list';
6
- import { Badge } from '@/components/ui/badge';
5
+ import { PageHeader } from '@/components/entity-list';
7
6
  import { Button } from '@/components/ui/button';
8
7
  import {
9
8
  ResizableHandle,
@@ -12,24 +11,16 @@ import {
12
11
  } from '@/components/ui/resizable';
13
12
  import {
14
13
  Sheet,
15
- SheetContent,
16
14
  SheetHeader,
17
15
  SheetTitle,
18
16
  } from '@/components/ui/sheet';
17
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
19
18
  import { useIsMobile } from '@/hooks/use-mobile';
20
19
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
21
20
  import { useIsMutating } from '@tanstack/react-query';
22
- import {
23
- AlertCircle,
24
- BookOpen,
25
- Layers,
26
- Menu,
27
- RefreshCw,
28
- Video,
29
- } from 'lucide-react';
21
+ import { AlertCircle, Menu, RefreshCw } from 'lucide-react';
30
22
  import { useTranslations } from 'next-intl';
31
23
 
32
- import { CourseAvatar } from '../../_components/course-avatar';
33
24
  import { ConfirmDialog } from './structure/_components/confirm-dialog';
34
25
  import { CourseTreePanel } from './structure/_components/course-tree-panel';
35
26
  import { CourseTreeSkeleton } from './structure/_components/course-tree-skeleton';
@@ -129,8 +120,6 @@ export default function CourseStructurePage({ params }: Props) {
129
120
  ]);
130
121
 
131
122
  const course = useStructureStore((s) => s.course);
132
- const sessions = useStructureStore((s) => s.sessions);
133
- const lessons = useStructureStore((s) => s.lessons);
134
123
 
135
124
  const mobileSheetOpen = useStructureStore((s) => s.mobileSheetOpen);
136
125
  const setMobileSheetOpen = useStructureStore((s) => s.setMobileSheetOpen);
@@ -204,77 +193,14 @@ export default function CourseStructurePage({ params }: Props) {
204
193
  ]),
205
194
  });
206
195
 
207
- const totalMinutes = useMemo(
208
- () => lessons.reduce((sum, l) => sum + l.duration, 0),
209
- [lessons]
210
- );
211
- const hours = Math.floor(totalMinutes / 60);
212
- const mins = totalMinutes % 60;
213
- const durationLabel = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
214
-
215
- const publishedLessons = useMemo(
216
- () =>
217
- lessons.filter(
218
- (l) => l.status === 'publicada' || l.visibility === 'publico'
219
- ).length,
220
- [lessons]
221
- );
222
-
223
- const metricsBadges = useMemo(
224
- () => (
225
- <div className="flex items-center gap-1.5 flex-wrap">
226
- <Badge
227
- variant="outline"
228
- className="gap-1 text-[0.65rem] h-5 px-1.5 font-normal"
229
- >
230
- <Layers className="size-3" />
231
- {sessions.length} {sessions.length === 1 ? 'sessão' : 'sessões'}
232
- </Badge>
233
- <Badge
234
- variant="outline"
235
- className="gap-1 text-[0.65rem] h-5 px-1.5 font-normal"
236
- >
237
- <Video className="size-3" />
238
- {lessons.length} {lessons.length === 1 ? 'aula' : 'aulas'}
239
- </Badge>
240
- <Badge
241
- variant="outline"
242
- className="gap-1 text-[0.65rem] h-5 px-1.5 font-normal"
243
- >
244
- <BookOpen className="size-3" />
245
- {durationLabel}
246
- </Badge>
247
- {publishedLessons > 0 && (
248
- <Badge className="gap-1 text-[0.65rem] h-5 px-1.5 font-normal bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-0 hover:bg-emerald-500/20">
249
- <span className="size-1.5 rounded-full bg-emerald-500 inline-block" />
250
- {publishedLessons} publicadas
251
- </Badge>
252
- )}
253
- </div>
254
- ),
255
- [sessions.length, lessons.length, durationLabel, publishedLessons]
256
- );
257
-
258
196
  return (
259
- <Page>
197
+ <div className="flex h-dvh max-h-dvh min-h-0 flex-1 flex-col gap-2 overflow-hidden overscroll-none px-2 pb-2 sm:gap-4 sm:px-4 sm:pb-0 md:h-[calc(100dvh-1rem)] md:max-h-[calc(100dvh-1rem)]">
260
198
  <PageHeader
261
199
  breadcrumbs={[
262
200
  { label: t('breadcrumbs.lms'), href: '/lms' },
263
201
  { label: t('breadcrumbs.courses'), href: '/lms/courses' },
264
202
  { label: course.name },
265
203
  ]}
266
- title={
267
- <span className="flex min-w-0 items-center gap-3">
268
- <CourseAvatar
269
- fileId={courseSummary?.logoFileId}
270
- title={courseSummary?.title ?? course.name}
271
- className="size-10 rounded-lg"
272
- iconSize="size-5"
273
- />
274
- <span className="min-w-0 truncate">{course.name}</span>
275
- </span>
276
- }
277
- extraContent={metricsBadges}
278
204
  actions={
279
205
  <>
280
206
  <TreeDisplaySettingsPopover />
@@ -289,13 +215,13 @@ export default function CourseStructurePage({ params }: Props) {
289
215
 
290
216
  {/* ── Desktop / Tablet ─────────────────────────────────────────────── */}
291
217
  {!isMobile && (
292
- <div className="flex-1 min-h-0 rounded-lg border overflow-hidden h-[calc(100dvh-10rem)]">
218
+ <div className="flex-1 min-h-0 max-h-full rounded-lg border overflow-hidden">
293
219
  <ResizablePanelGroup direction="horizontal" className="h-full">
294
220
  <ResizablePanel
295
221
  defaultSize={28}
296
222
  minSize={18}
297
223
  maxSize={45}
298
- className="flex flex-col"
224
+ className="flex flex-col min-h-0 overflow-hidden"
299
225
  >
300
226
  {isLoading ? (
301
227
  <CourseTreeSkeleton />
@@ -322,12 +248,17 @@ export default function CourseStructurePage({ params }: Props) {
322
248
 
323
249
  <ResizableHandle withHandle />
324
250
 
325
- <ResizablePanel defaultSize={72} className="flex flex-col min-h-0">
251
+ <ResizablePanel
252
+ defaultSize={72}
253
+ className="flex flex-col min-h-0 overflow-hidden"
254
+ >
326
255
  <div
327
256
  ref={detailPanelRef}
328
257
  className="flex flex-col h-full min-h-0"
329
258
  >
330
- <DetailPanel />
259
+ <div className="h-full min-h-0">
260
+ <DetailPanel />
261
+ </div>
331
262
  </div>
332
263
  </ResizablePanel>
333
264
  </ResizablePanelGroup>
@@ -353,11 +284,17 @@ export default function CourseStructurePage({ params }: Props) {
353
284
  ref={detailPanelRef}
354
285
  className="flex-1 min-h-0 border rounded-lg overflow-hidden"
355
286
  >
356
- <DetailPanel />
287
+ <div className="h-full min-h-0">
288
+ <DetailPanel />
289
+ </div>
357
290
  </div>
358
291
 
359
292
  <Sheet open={mobileSheetOpen} onOpenChange={setMobileSheetOpen}>
360
- <SheetContent side="left" className="w-[320px] p-0 flex flex-col">
293
+ <ResizableSheetContent
294
+ sheetId="lms-course-structure-mobile-sheet"
295
+ side="left"
296
+ className="p-0 flex flex-col"
297
+ >
361
298
  <SheetHeader className="px-4 py-3 border-b shrink-0">
362
299
  <SheetTitle className="text-sm">
363
300
  {t('mobile.sheetTitle', { title: course.title })}
@@ -386,10 +323,10 @@ export default function CourseStructurePage({ params }: Props) {
386
323
  <CourseTreePanel />
387
324
  )}
388
325
  </div>
389
- </SheetContent>
326
+ </ResizableSheetContent>
390
327
  </Sheet>
391
328
  </>
392
329
  )}
393
- </Page>
330
+ </div>
394
331
  );
395
332
  }