@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.
- package/dist/certificate/certificate.controller.d.ts +2 -2
- package/dist/certificate/certificate.controller.d.ts.map +1 -1
- package/dist/certificate/certificate.controller.js +8 -6
- package/dist/certificate/certificate.controller.js.map +1 -1
- package/dist/certificate/certificate.service.d.ts +5 -2
- package/dist/certificate/certificate.service.d.ts.map +1 -1
- package/dist/certificate/certificate.service.js +70 -6
- package/dist/certificate/certificate.service.js.map +1 -1
- package/dist/course/course-structure.controller.d.ts +24 -10
- package/dist/course/course-structure.controller.d.ts.map +1 -1
- package/dist/course/course-structure.controller.js +23 -2
- package/dist/course/course-structure.controller.js.map +1 -1
- package/dist/course/course-structure.service.d.ts +16 -8
- package/dist/course/course-structure.service.d.ts.map +1 -1
- package/dist/course/course-structure.service.js +61 -30
- package/dist/course/course-structure.service.js.map +1 -1
- package/dist/course/course-video-conversion.service.d.ts +37 -0
- package/dist/course/course-video-conversion.service.d.ts.map +1 -0
- package/dist/course/course-video-conversion.service.js +308 -0
- package/dist/course/course-video-conversion.service.js.map +1 -0
- package/dist/course/course.controller.d.ts +17 -0
- package/dist/course/course.controller.d.ts.map +1 -1
- package/dist/course/course.controller.js +23 -0
- package/dist/course/course.controller.js.map +1 -1
- package/dist/course/course.module.d.ts.map +1 -1
- package/dist/course/course.module.js +15 -2
- package/dist/course/course.module.js.map +1 -1
- package/dist/course/course.service.d.ts +15 -0
- package/dist/course/course.service.d.ts.map +1 -1
- package/dist/course/course.service.js +103 -49
- package/dist/course/course.service.js.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts +5 -1
- package/dist/course/dto/create-course-structure-lesson.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course-structure-lesson.dto.js +16 -2
- package/dist/course/dto/create-course-structure-lesson.dto.js.map +1 -1
- package/dist/course/dto/create-course.dto.d.ts +1 -0
- package/dist/course/dto/create-course.dto.d.ts.map +1 -1
- package/dist/course/dto/create-course.dto.js +9 -0
- package/dist/course/dto/create-course.dto.js.map +1 -1
- package/dist/enterprise/enterprise.controller.d.ts +3 -3
- package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
- package/dist/enterprise/enterprise.controller.js +0 -1
- package/dist/enterprise/enterprise.controller.js.map +1 -1
- package/dist/enterprise/enterprise.service.d.ts +3 -3
- package/dist/evaluation/evaluation.service.d.ts.map +1 -1
- package/dist/evaluation/evaluation.service.js +9 -2
- package/dist/evaluation/evaluation.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lms.module.d.ts.map +1 -1
- package/dist/lms.module.js +3 -0
- package/dist/lms.module.js.map +1 -1
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts +6 -0
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.d.ts.map +1 -0
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js +33 -0
- package/dist/video-resolution-profile/dto/create-video-resolution-profile.dto.js.map +1 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts +6 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.d.ts.map +1 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js +33 -0
- package/dist/video-resolution-profile/dto/update-video-resolution-profile.dto.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts +38 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.js +89 -0
- package/dist/video-resolution-profile/video-resolution-profile.controller.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts +26 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js +160 -0
- package/dist/video-resolution-profile/video-resolution-profile.mcp-tools.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.d.ts +3 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.js +26 -0
- package/dist/video-resolution-profile/video-resolution-profile.module.js.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.d.ts +45 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.d.ts.map +1 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.js +117 -0
- package/dist/video-resolution-profile/video-resolution-profile.service.js.map +1 -0
- package/hedhog/data/menu.yaml +17 -0
- package/hedhog/data/route.yaml +133 -0
- package/hedhog/data/video_resolution_profile.yaml +7 -0
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +269 -324
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +124 -70
- package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +7 -4
- package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/_lib/editor/templateSerializer.ts.ejs +34 -4
- package/hedhog/frontend/app/_lib/editor/types.ts.ejs +28 -3
- package/hedhog/frontend/app/achievements/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/bitcodes/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +7 -3
- package/hedhog/frontend/app/certificates/models/CanvasStage.tsx.ejs +29 -8
- package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +14 -0
- package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +194 -9
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +15 -5
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +9 -5
- package/hedhog/frontend/app/classes/page.tsx.ejs +73 -47
- package/hedhog/frontend/app/courses/[id]/_components/CourseCertificateCard.tsx.ejs +19 -9
- package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +24 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +28 -16
- package/hedhog/frontend/app/courses/[id]/_components/CourseSectionCard.tsx.ejs +11 -6
- package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +7 -4
- package/hedhog/frontend/app/courses/[id]/_components/course-edit-types.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +24 -87
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +892 -411
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +1004 -293
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-session.tsx.ejs +11 -11
- package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +62 -52
- package/hedhog/frontend/app/courses/[id]/structure/_components/types.ts.ejs +2 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/adapters/course-structure.adapter.ts.ejs +19 -6
- package/hedhog/frontend/app/courses/[id]/structure/_data/services/course-structure.service.ts.ejs +86 -1
- package/hedhog/frontend/app/courses/[id]/structure/_data/types/api-course.types.ts.ejs +3 -0
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +1 -0
- package/hedhog/frontend/app/courses/page.tsx.ejs +112 -89
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +8 -4
- package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +2 -2
- package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +10 -4
- package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +10 -3
- package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +23 -9
- package/hedhog/frontend/app/exams/page.tsx.ejs +14 -6
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +190 -17
- package/hedhog/frontend/app/layout.tsx.ejs +5 -1
- package/hedhog/frontend/app/paths/page.tsx.ejs +13 -5
- package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +10 -10
- package/hedhog/frontend/app/training/page.tsx.ejs +13 -5
- package/hedhog/frontend/app/video-resolution-profiles/page.tsx.ejs +607 -0
- package/hedhog/frontend/messages/en.json +250 -9
- package/hedhog/frontend/messages/pt.json +250 -9
- package/hedhog/table/course.yaml +4 -0
- package/hedhog/table/course_lesson_file.yaml +8 -0
- package/hedhog/table/course_video_resolution_profile.yaml +22 -0
- package/hedhog/table/video_resolution_profile.yaml +18 -0
- package/package.json +7 -6
- package/src/certificate/certificate.controller.ts +19 -14
- package/src/certificate/certificate.service.ts +106 -11
- package/src/course/course-structure.controller.ts +24 -2
- package/src/course/course-structure.service.ts +21 -4
- package/src/course/course-video-conversion.service.ts +415 -0
- package/src/course/course.controller.ts +18 -0
- package/src/course/course.module.ts +15 -2
- package/src/course/course.service.ts +72 -2
- package/src/course/dto/create-course-structure-lesson.dto.ts +13 -2
- package/src/course/dto/create-course.dto.ts +8 -0
- package/src/enterprise/enterprise.controller.ts +0 -1
- package/src/evaluation/evaluation.service.ts +9 -2
- package/src/index.ts +1 -0
- package/src/lms.module.ts +3 -0
- package/src/video-resolution-profile/dto/create-video-resolution-profile.dto.ts +16 -0
- package/src/video-resolution-profile/dto/update-video-resolution-profile.dto.ts +16 -0
- package/src/video-resolution-profile/video-resolution-profile.controller.ts +62 -0
- package/src/video-resolution-profile/video-resolution-profile.mcp-tools.ts +128 -0
- package/src/video-resolution-profile/video-resolution-profile.module.ts +13 -0
- 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=
|
|
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=
|
|
51
|
-
createActionLabel=
|
|
52
|
-
createTitle=
|
|
53
|
-
createDescription=
|
|
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:
|
|
58
|
-
|
|
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:
|
|
64
|
-
|
|
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=
|
|
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=
|
|
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>
|
|
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=
|
|
65
|
-
searchPlaceholder=
|
|
66
|
-
|
|
67
|
-
|
|
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=
|
|
97
|
-
createActionLabel=
|
|
98
|
-
createTitle=
|
|
99
|
-
createDescription=
|
|
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:
|
|
104
|
-
|
|
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=
|
|
136
|
-
createActionLabel=
|
|
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-
|
|
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
|
|
39
|
-
|
|
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-
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
50
|
+
title: t('kpis.sessions'),
|
|
48
51
|
value: String(course?.totalSessoes ?? 0),
|
|
49
52
|
icon: CalendarDays,
|
|
50
53
|
layout: 'compact',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { use, useCallback, useEffect,
|
|
3
|
+
import { use, useCallback, useEffect, useRef, useState } from 'react';
|
|
4
4
|
|
|
5
|
-
import {
|
|
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
|
-
<
|
|
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
|
|
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
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
</
|
|
326
|
+
</ResizableSheetContent>
|
|
390
327
|
</Sheet>
|
|
391
328
|
</>
|
|
392
329
|
)}
|
|
393
|
-
</
|
|
330
|
+
</div>
|
|
394
331
|
);
|
|
395
332
|
}
|