@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
|
@@ -31,12 +31,12 @@ import {
|
|
|
31
31
|
} from '@/components/ui/select';
|
|
32
32
|
import {
|
|
33
33
|
Sheet,
|
|
34
|
-
SheetContent,
|
|
35
34
|
SheetDescription,
|
|
36
35
|
SheetFooter,
|
|
37
36
|
SheetHeader,
|
|
38
37
|
SheetTitle,
|
|
39
38
|
} from '@/components/ui/sheet';
|
|
39
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
40
40
|
import { Textarea } from '@/components/ui/textarea';
|
|
41
41
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
42
42
|
import {
|
|
@@ -227,10 +227,16 @@ export function getCourseSheetSchema(t: (key: string) => string) {
|
|
|
227
227
|
slug: z
|
|
228
228
|
.string()
|
|
229
229
|
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, t('form.validation.slugPattern')),
|
|
230
|
+
code: z
|
|
231
|
+
.string()
|
|
232
|
+
.regex(/^[A-Z0-9]{2,}$/, t('form.validation.editionCodePattern'))
|
|
233
|
+
.or(z.literal(''))
|
|
234
|
+
.optional(),
|
|
230
235
|
tituloComercial: z.string().optional(),
|
|
231
236
|
descricao: z.string().optional(),
|
|
232
237
|
nivel: z.enum(['iniciante', 'intermediario', 'avancado']).optional(),
|
|
233
238
|
status: z.enum(['ativo', 'rascunho', 'arquivado']).optional(),
|
|
239
|
+
offeringType: z.enum(['on_demand', 'agendado', 'hibrido']).optional(),
|
|
234
240
|
categorias: z.array(z.string()).optional(),
|
|
235
241
|
operationsProjectId: z.string().optional(),
|
|
236
242
|
primaryColor: z.string().optional(),
|
|
@@ -242,10 +248,12 @@ export function getCourseSheetSchema(t: (key: string) => string) {
|
|
|
242
248
|
export type CourseSheetFormValues = {
|
|
243
249
|
nomeInterno: string;
|
|
244
250
|
slug: string;
|
|
251
|
+
code: string;
|
|
245
252
|
tituloComercial: string;
|
|
246
253
|
descricao: string;
|
|
247
254
|
nivel: 'iniciante' | 'intermediario' | 'avancado';
|
|
248
255
|
status: 'ativo' | 'rascunho' | 'arquivado';
|
|
256
|
+
offeringType: 'on_demand' | 'agendado' | 'hibrido';
|
|
249
257
|
categorias: string[];
|
|
250
258
|
operationsProjectId: string;
|
|
251
259
|
primaryColor: string;
|
|
@@ -256,10 +264,12 @@ export type CourseSheetFormValues = {
|
|
|
256
264
|
export const DEFAULT_COURSE_FORM_VALUES: CourseSheetFormValues = {
|
|
257
265
|
nomeInterno: '',
|
|
258
266
|
slug: '',
|
|
267
|
+
code: '',
|
|
259
268
|
tituloComercial: '',
|
|
260
269
|
descricao: '',
|
|
261
270
|
nivel: 'iniciante',
|
|
262
271
|
status: 'rascunho',
|
|
272
|
+
offeringType: 'on_demand',
|
|
263
273
|
categorias: [],
|
|
264
274
|
operationsProjectId: '',
|
|
265
275
|
primaryColor: '#1D4ED8',
|
|
@@ -439,7 +449,11 @@ export function CourseFormSheet({
|
|
|
439
449
|
|
|
440
450
|
return (
|
|
441
451
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
442
|
-
<
|
|
452
|
+
<ResizableSheetContent
|
|
453
|
+
sheetId="lms-course-form-sheet"
|
|
454
|
+
defaultWidth={560}
|
|
455
|
+
minWidth={420}
|
|
456
|
+
maxWidth={920}
|
|
443
457
|
side="right"
|
|
444
458
|
className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
|
|
445
459
|
>
|
|
@@ -456,61 +470,80 @@ export function CourseFormSheet({
|
|
|
456
470
|
|
|
457
471
|
<form
|
|
458
472
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
459
|
-
className="flex flex-1 flex-col gap-
|
|
473
|
+
className="flex flex-1 flex-col gap-4 py-4 px-4"
|
|
460
474
|
>
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
<
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
</
|
|
477
|
-
|
|
475
|
+
{/* Internal name + Slug */}
|
|
476
|
+
<div className="grid grid-cols-2 gap-3">
|
|
477
|
+
<Field>
|
|
478
|
+
<FieldLabel htmlFor="nomeInterno">
|
|
479
|
+
{t('form.fields.internalName.label')}{' '}
|
|
480
|
+
<span className="text-destructive">*</span>
|
|
481
|
+
</FieldLabel>
|
|
482
|
+
<Input
|
|
483
|
+
id="nomeInterno"
|
|
484
|
+
placeholder={t('form.fields.internalName.placeholder')}
|
|
485
|
+
{...form.register('nomeInterno')}
|
|
486
|
+
/>
|
|
487
|
+
<FieldError>
|
|
488
|
+
{form.formState.errors.nomeInterno?.message}
|
|
489
|
+
</FieldError>
|
|
490
|
+
</Field>
|
|
491
|
+
|
|
492
|
+
<Field>
|
|
493
|
+
<FieldLabel htmlFor="slug">
|
|
494
|
+
{t('form.fields.slug.label')}{' '}
|
|
495
|
+
<span className="text-destructive">*</span>
|
|
496
|
+
</FieldLabel>
|
|
497
|
+
<Input
|
|
498
|
+
id="slug"
|
|
499
|
+
placeholder={t('form.fields.slug.placeholder')}
|
|
500
|
+
{...form.register('slug', {
|
|
501
|
+
onChange: () => {
|
|
502
|
+
slugTouchedRef.current = true;
|
|
503
|
+
},
|
|
504
|
+
})}
|
|
505
|
+
/>
|
|
506
|
+
<FieldError>{form.formState.errors.slug?.message}</FieldError>
|
|
507
|
+
</Field>
|
|
508
|
+
</div>
|
|
478
509
|
|
|
510
|
+
{/* Edition code */}
|
|
479
511
|
<Field>
|
|
480
|
-
<FieldLabel htmlFor="
|
|
481
|
-
{t('form.fields.
|
|
482
|
-
<span className="text-destructive">*</span>
|
|
512
|
+
<FieldLabel htmlFor="code">
|
|
513
|
+
{t('form.fields.code.label')}
|
|
483
514
|
</FieldLabel>
|
|
484
515
|
<Input
|
|
485
|
-
id="
|
|
486
|
-
placeholder={t('form.fields.
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
516
|
+
id="code"
|
|
517
|
+
placeholder={t('form.fields.code.placeholder')}
|
|
518
|
+
className="font-mono"
|
|
519
|
+
{...form.register('code', {
|
|
520
|
+
onChange: (e) => {
|
|
521
|
+
const cleaned = e.target.value
|
|
522
|
+
.toUpperCase()
|
|
523
|
+
.replace(/[^A-Z0-9]/g, '');
|
|
524
|
+
form.setValue('code', cleaned, { shouldValidate: true });
|
|
490
525
|
},
|
|
491
526
|
})}
|
|
492
527
|
/>
|
|
493
|
-
<
|
|
494
|
-
{t('form.fields.slug.description')}
|
|
495
|
-
</FieldDescription>
|
|
496
|
-
<FieldError>{form.formState.errors.slug?.message}</FieldError>
|
|
528
|
+
<FieldError>{form.formState.errors.code?.message}</FieldError>
|
|
497
529
|
</Field>
|
|
498
530
|
|
|
531
|
+
{/* Logo */}
|
|
499
532
|
<Field>
|
|
500
533
|
<FieldLabel>{t('form.fields.logo.label')}</FieldLabel>
|
|
501
|
-
<div className="flex items-
|
|
534
|
+
<div className="flex items-center gap-3">
|
|
502
535
|
{logoPreviewUrl ? (
|
|
503
536
|
<img
|
|
504
537
|
src={logoPreviewUrl}
|
|
505
538
|
alt={t('form.fields.logo.alt')}
|
|
506
|
-
className="size-
|
|
539
|
+
className="size-12 shrink-0 rounded-lg border object-cover"
|
|
507
540
|
/>
|
|
508
541
|
) : (
|
|
509
|
-
<div className="flex size-
|
|
510
|
-
<ImageIcon className="size-
|
|
542
|
+
<div className="flex size-12 shrink-0 items-center justify-center rounded-lg border border-dashed bg-muted/40">
|
|
543
|
+
<ImageIcon className="size-5 text-muted-foreground/50" />
|
|
511
544
|
</div>
|
|
512
545
|
)}
|
|
513
|
-
<div className="flex
|
|
546
|
+
<div className="flex items-center gap-2">
|
|
514
547
|
<input
|
|
515
548
|
ref={logoInputRef}
|
|
516
549
|
type="file"
|
|
@@ -547,40 +580,69 @@ export function CourseFormSheet({
|
|
|
547
580
|
{t('form.fields.logo.remove')}
|
|
548
581
|
</Button>
|
|
549
582
|
)}
|
|
583
|
+
{colorExtracting && (
|
|
584
|
+
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
585
|
+
<Loader2 className="size-3 animate-spin" />
|
|
586
|
+
Detectando cores...
|
|
587
|
+
</span>
|
|
588
|
+
)}
|
|
550
589
|
</div>
|
|
551
590
|
</div>
|
|
552
|
-
<FieldDescription>
|
|
553
|
-
{t('form.fields.logo.description')}
|
|
554
|
-
</FieldDescription>
|
|
555
591
|
</Field>
|
|
556
592
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
593
|
+
{/* Commercial title + Offering type */}
|
|
594
|
+
<div className="grid grid-cols-2 gap-3">
|
|
595
|
+
<Field>
|
|
596
|
+
<FieldLabel htmlFor="tituloComercial">
|
|
597
|
+
{t('form.fields.commercialTitle.label')}
|
|
598
|
+
</FieldLabel>
|
|
599
|
+
<Input
|
|
600
|
+
id="tituloComercial"
|
|
601
|
+
placeholder={t('form.fields.commercialTitle.placeholder')}
|
|
602
|
+
{...form.register('tituloComercial')}
|
|
603
|
+
/>
|
|
604
|
+
<FieldError>
|
|
605
|
+
{form.formState.errors.tituloComercial?.message}
|
|
606
|
+
</FieldError>
|
|
607
|
+
</Field>
|
|
570
608
|
|
|
609
|
+
<Field>
|
|
610
|
+
<FieldLabel>Tipo do curso</FieldLabel>
|
|
611
|
+
<Controller
|
|
612
|
+
name="offeringType"
|
|
613
|
+
control={form.control}
|
|
614
|
+
render={({ field }) => (
|
|
615
|
+
<Select onValueChange={field.onChange} value={field.value}>
|
|
616
|
+
<SelectTrigger>
|
|
617
|
+
<SelectValue placeholder="Selecione o tipo" />
|
|
618
|
+
</SelectTrigger>
|
|
619
|
+
<SelectContent>
|
|
620
|
+
<SelectItem value="on_demand">On Demand</SelectItem>
|
|
621
|
+
<SelectItem value="agendado">Agendado</SelectItem>
|
|
622
|
+
<SelectItem value="hibrido">Híbrido</SelectItem>
|
|
623
|
+
</SelectContent>
|
|
624
|
+
</Select>
|
|
625
|
+
)}
|
|
626
|
+
/>
|
|
627
|
+
<FieldError>{form.formState.errors.offeringType?.message}</FieldError>
|
|
628
|
+
</Field>
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
{/* Description */}
|
|
571
632
|
<Field>
|
|
572
633
|
<FieldLabel htmlFor="descricao">
|
|
573
634
|
{t('form.fields.description.label')}
|
|
574
635
|
</FieldLabel>
|
|
575
636
|
<Textarea
|
|
576
637
|
id="descricao"
|
|
577
|
-
rows={
|
|
638
|
+
rows={2}
|
|
578
639
|
placeholder={t('form.fields.description.placeholder')}
|
|
579
640
|
{...form.register('descricao')}
|
|
580
641
|
/>
|
|
581
642
|
<FieldError>{form.formState.errors.descricao?.message}</FieldError>
|
|
582
643
|
</Field>
|
|
583
644
|
|
|
645
|
+
{/* Operations project */}
|
|
584
646
|
<Field>
|
|
585
647
|
<FieldLabel>Projeto vinculado</FieldLabel>
|
|
586
648
|
<Controller
|
|
@@ -608,16 +670,13 @@ export function CourseFormSheet({
|
|
|
608
670
|
/>
|
|
609
671
|
)}
|
|
610
672
|
/>
|
|
611
|
-
<FieldDescription>
|
|
612
|
-
Quando o curso estiver vinculado a um projeto, cada nova aula gera
|
|
613
|
-
uma tarefa automaticamente.
|
|
614
|
-
</FieldDescription>
|
|
615
673
|
<FieldError>
|
|
616
674
|
{form.formState.errors.operationsProjectId?.message}
|
|
617
675
|
</FieldError>
|
|
618
676
|
</Field>
|
|
619
677
|
|
|
620
|
-
|
|
678
|
+
{/* Level + Status */}
|
|
679
|
+
<div className="grid grid-cols-2 gap-3">
|
|
621
680
|
<Field>
|
|
622
681
|
<FieldLabel>{t('form.fields.level.label')}</FieldLabel>
|
|
623
682
|
<Controller
|
|
@@ -677,6 +736,7 @@ export function CourseFormSheet({
|
|
|
677
736
|
</Field>
|
|
678
737
|
</div>
|
|
679
738
|
|
|
739
|
+
{/* Categories */}
|
|
680
740
|
<Field>
|
|
681
741
|
<FieldLabel>{t('form.fields.categories.label')}</FieldLabel>
|
|
682
742
|
<Controller
|
|
@@ -831,14 +891,8 @@ export function CourseFormSheet({
|
|
|
831
891
|
<FieldError>{form.formState.errors.categorias?.message}</FieldError>
|
|
832
892
|
</Field>
|
|
833
893
|
|
|
834
|
-
{
|
|
835
|
-
|
|
836
|
-
<Loader2 className="size-3 animate-spin" />
|
|
837
|
-
Detectando cores...
|
|
838
|
-
</div>
|
|
839
|
-
)}
|
|
840
|
-
|
|
841
|
-
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
894
|
+
{/* Primary + Secondary colors */}
|
|
895
|
+
<div className="grid grid-cols-2 gap-3">
|
|
842
896
|
<Field>
|
|
843
897
|
<FieldLabel htmlFor="primaryColor">
|
|
844
898
|
{t('form.fields.primaryColor.label')}
|
|
@@ -851,7 +905,7 @@ export function CourseFormSheet({
|
|
|
851
905
|
<Input
|
|
852
906
|
id="primaryColor"
|
|
853
907
|
type="color"
|
|
854
|
-
className="h-
|
|
908
|
+
className="h-9 w-12 p-1"
|
|
855
909
|
value={field.value}
|
|
856
910
|
onChange={field.onChange}
|
|
857
911
|
/>
|
|
@@ -880,7 +934,7 @@ export function CourseFormSheet({
|
|
|
880
934
|
<Input
|
|
881
935
|
id="secondaryColor"
|
|
882
936
|
type="color"
|
|
883
|
-
className="h-
|
|
937
|
+
className="h-9 w-12 p-1"
|
|
884
938
|
value={field.value}
|
|
885
939
|
onChange={field.onChange}
|
|
886
940
|
/>
|
|
@@ -905,7 +959,7 @@ export function CourseFormSheet({
|
|
|
905
959
|
</Button>
|
|
906
960
|
</SheetFooter>
|
|
907
961
|
</form>
|
|
908
|
-
|
|
962
|
+
</ResizableSheetContent>
|
|
909
963
|
</Sheet>
|
|
910
964
|
);
|
|
911
965
|
}
|
|
@@ -12,12 +12,12 @@ import { Input } from '@/components/ui/input';
|
|
|
12
12
|
import { Progress } from '@/components/ui/progress';
|
|
13
13
|
import {
|
|
14
14
|
Sheet,
|
|
15
|
-
SheetContent,
|
|
16
15
|
SheetDescription,
|
|
17
16
|
SheetFooter,
|
|
18
17
|
SheetHeader,
|
|
19
18
|
SheetTitle,
|
|
20
19
|
} from '@/components/ui/sheet';
|
|
20
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
21
21
|
import { Switch } from '@/components/ui/switch';
|
|
22
22
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
23
23
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
@@ -402,7 +402,11 @@ export function CreateLmsInstructorSheet({
|
|
|
402
402
|
|
|
403
403
|
return (
|
|
404
404
|
<Sheet open={open} onOpenChange={handleSheetOpenChange}>
|
|
405
|
-
<
|
|
405
|
+
<ResizableSheetContent
|
|
406
|
+
sheetId="lms-create-instructor-sheet"
|
|
407
|
+
defaultWidth={560}
|
|
408
|
+
minWidth={420}
|
|
409
|
+
maxWidth={920}
|
|
406
410
|
side="right"
|
|
407
411
|
className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
|
|
408
412
|
>
|
|
@@ -413,7 +417,6 @@ export function CreateLmsInstructorSheet({
|
|
|
413
417
|
|
|
414
418
|
{isEditing && loadingInstructorDetails ? (
|
|
415
419
|
<div className="flex items-center gap-2 px-4 py-10 text-sm text-muted-foreground">
|
|
416
|
-
<Loader2 className="size-4 animate-spin" />
|
|
417
420
|
Carregando dados do instrutor...
|
|
418
421
|
</div>
|
|
419
422
|
) : (
|
|
@@ -585,7 +588,7 @@ export function CreateLmsInstructorSheet({
|
|
|
585
588
|
</SheetFooter>
|
|
586
589
|
</form>
|
|
587
590
|
)}
|
|
588
|
-
</
|
|
591
|
+
</ResizableSheetContent>
|
|
589
592
|
</Sheet>
|
|
590
593
|
);
|
|
591
594
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { PersonFormSheet } from '@/app/(app)/(libraries)/
|
|
3
|
+
import { PersonFormSheet } from '@/app/(app)/(libraries)/crm/person/_components/person-form-sheet';
|
|
4
4
|
import type {
|
|
5
5
|
ContactTypeOption,
|
|
6
6
|
DocumentTypeOption,
|
|
7
7
|
Person,
|
|
8
|
-
} from '@/app/(app)/(libraries)/
|
|
8
|
+
} from '@/app/(app)/(libraries)/crm/person/_components/person-types';
|
|
9
9
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
10
10
|
import { useTranslations } from 'next-intl';
|
|
11
11
|
import { useMemo } from 'react';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { PersonFormSheet } from '@/app/(app)/(libraries)/
|
|
3
|
+
import { PersonFormSheet } from '@/app/(app)/(libraries)/crm/person/_components/person-form-sheet';
|
|
4
4
|
import type {
|
|
5
5
|
ContactTypeOption,
|
|
6
6
|
DocumentTypeOption,
|
|
7
7
|
Person,
|
|
8
|
-
} from '@/app/(app)/(libraries)/
|
|
8
|
+
} from '@/app/(app)/(libraries)/crm/person/_components/person-types';
|
|
9
9
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
10
10
|
import { useTranslations } from 'next-intl';
|
|
11
11
|
|
|
@@ -105,7 +105,20 @@ export function fabricObjToFull(fObj: any): TemplateObject {
|
|
|
105
105
|
|
|
106
106
|
/* image */
|
|
107
107
|
if (type === 'image') {
|
|
108
|
-
|
|
108
|
+
const rawFileId = fObj._tplImageFileId;
|
|
109
|
+
const rawResizeMode = fObj._tplImageResizeMode;
|
|
110
|
+
const resizeMode =
|
|
111
|
+
rawResizeMode === 'contain' ||
|
|
112
|
+
rawResizeMode === 'cover' ||
|
|
113
|
+
rawResizeMode === 'stretch' ||
|
|
114
|
+
rawResizeMode === 'center'
|
|
115
|
+
? rawResizeMode
|
|
116
|
+
: 'contain';
|
|
117
|
+
const fileId =
|
|
118
|
+
typeof rawFileId === 'number' && Number.isFinite(rawFileId)
|
|
119
|
+
? rawFileId
|
|
120
|
+
: null;
|
|
121
|
+
obj.image = { file_id: fileId, resizeMode };
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
return obj;
|
|
@@ -219,17 +232,34 @@ export function createFabricFromTemplate(
|
|
|
219
232
|
: 2,
|
|
220
233
|
});
|
|
221
234
|
} else if (tObj.type === 'image') {
|
|
235
|
+
const isCoursePlaceholder =
|
|
236
|
+
tObj.key === 'courseLogo' || tObj.key === 'courseBanner';
|
|
222
237
|
fObj = new fabric.Rect({
|
|
223
238
|
...common,
|
|
224
|
-
width: w || 150,
|
|
225
|
-
height: h || 150,
|
|
239
|
+
width: w || (tObj.key === 'courseBanner' ? 420 : 150),
|
|
240
|
+
height: h || (tObj.key === 'courseBanner' ? 140 : 150),
|
|
226
241
|
fill: '#e2e8f0',
|
|
227
242
|
stroke: '#cbd5e1',
|
|
228
243
|
strokeWidth: 1,
|
|
244
|
+
strokeDashArray: isCoursePlaceholder ? [8, 4] : undefined,
|
|
229
245
|
rx: 4,
|
|
230
246
|
ry: 4,
|
|
231
247
|
});
|
|
232
|
-
fObj.
|
|
248
|
+
fObj._tplImageFileId =
|
|
249
|
+
typeof tObj.image?.file_id === 'number' ? tObj.image.file_id : null;
|
|
250
|
+
fObj._tplImageResizeMode =
|
|
251
|
+
tObj.image?.resizeMode === 'cover' ||
|
|
252
|
+
tObj.image?.resizeMode === 'stretch' ||
|
|
253
|
+
tObj.image?.resizeMode === 'center'
|
|
254
|
+
? tObj.image.resizeMode
|
|
255
|
+
: 'contain';
|
|
256
|
+
fObj._tplImageSrc =
|
|
257
|
+
typeof tObj.image?.file_id === 'number' && tObj.image.file_id > 0
|
|
258
|
+
? `/file/open/${tObj.image.file_id}`
|
|
259
|
+
: '';
|
|
260
|
+
if (isCoursePlaceholder) {
|
|
261
|
+
fObj._tplImagePlaceholder = true;
|
|
262
|
+
}
|
|
233
263
|
}
|
|
234
264
|
|
|
235
265
|
if (fObj) {
|
|
@@ -30,9 +30,11 @@ export interface TemplateObject {
|
|
|
30
30
|
stroke?: { color: string; widthPct: number };
|
|
31
31
|
shadow?: { xPct: number; yPct: number; blurPct: number; color: string };
|
|
32
32
|
};
|
|
33
|
-
image?: {
|
|
33
|
+
image?: { file_id: number | null; resizeMode?: ImageResizeMode };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export type ImageResizeMode = 'contain' | 'cover' | 'stretch' | 'center';
|
|
37
|
+
|
|
36
38
|
export interface Template {
|
|
37
39
|
version: 1;
|
|
38
40
|
name: string;
|
|
@@ -53,6 +55,10 @@ export const FIELD_KEYS = [
|
|
|
53
55
|
'endDate',
|
|
54
56
|
'workloadHours',
|
|
55
57
|
'certificateId',
|
|
58
|
+
'enrollmentDate',
|
|
59
|
+
'issueDate',
|
|
60
|
+
'instructorName',
|
|
61
|
+
'qrCode',
|
|
56
62
|
] as const;
|
|
57
63
|
|
|
58
64
|
export type FieldKey = (typeof FIELD_KEYS)[number];
|
|
@@ -64,6 +70,10 @@ export const FIELD_LABELS: Record<FieldKey, string> = {
|
|
|
64
70
|
endDate: 'Data Fim',
|
|
65
71
|
workloadHours: 'Carga Horária',
|
|
66
72
|
certificateId: 'ID Certificado',
|
|
73
|
+
enrollmentDate: 'Data de Matrícula',
|
|
74
|
+
issueDate: 'Data de Emissão',
|
|
75
|
+
instructorName: 'Nome do Instrutor',
|
|
76
|
+
qrCode: 'QR Code',
|
|
67
77
|
};
|
|
68
78
|
|
|
69
79
|
export const FONT_FAMILIES = ['Inter', 'Montserrat', 'Roboto'] as const;
|
|
@@ -79,15 +89,30 @@ export function createDefaultTemplate(): Template {
|
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
export function getObjectLabel(obj: TemplateObject): string {
|
|
92
|
+
const fieldLabelMap: Record<string, string> = {
|
|
93
|
+
studentName: 'Nome do Aluno',
|
|
94
|
+
courseName: 'Nome do Curso',
|
|
95
|
+
startDate: 'Data Início',
|
|
96
|
+
endDate: 'Data Fim',
|
|
97
|
+
workloadHours: 'Carga Horária',
|
|
98
|
+
certificateId: 'ID Certificado',
|
|
99
|
+
enrollmentDate: 'Data de Matrícula',
|
|
100
|
+
issueDate: 'Data de Emissão',
|
|
101
|
+
instructorName: 'Nome do Instrutor',
|
|
102
|
+
qrCode: 'QR Code',
|
|
103
|
+
courseLogo: 'Logo do Curso',
|
|
104
|
+
courseBanner: 'Banner do Curso',
|
|
105
|
+
};
|
|
106
|
+
|
|
82
107
|
switch (obj.type) {
|
|
83
108
|
case 'field':
|
|
84
|
-
return `Field: ${obj.key ?? '?'}`;
|
|
109
|
+
return `Field: ${fieldLabelMap[obj.key ?? ''] ?? obj.key ?? '?'}`;
|
|
85
110
|
case 'staticText':
|
|
86
111
|
return `Text: ${(obj.text ?? 'Texto').slice(0, 20)}`;
|
|
87
112
|
case 'shape':
|
|
88
113
|
return obj.shape === 'line' ? 'Line' : 'Rect';
|
|
89
114
|
case 'image':
|
|
90
|
-
return 'Image';
|
|
115
|
+
return obj.key ? `Image: ${fieldLabelMap[obj.key] ?? obj.key}` : 'Image';
|
|
91
116
|
default:
|
|
92
117
|
return 'Object';
|
|
93
118
|
}
|
|
@@ -44,10 +44,10 @@ import {
|
|
|
44
44
|
} from '@/components/ui/select';
|
|
45
45
|
import {
|
|
46
46
|
Sheet,
|
|
47
|
-
SheetContent,
|
|
48
47
|
SheetHeader,
|
|
49
48
|
SheetTitle,
|
|
50
49
|
} from '@/components/ui/sheet';
|
|
50
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
51
51
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
52
52
|
import {
|
|
53
53
|
Table,
|
|
@@ -370,7 +370,13 @@ function AchievementFormSheet({
|
|
|
370
370
|
|
|
371
371
|
return (
|
|
372
372
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
373
|
-
<
|
|
373
|
+
<ResizableSheetContent
|
|
374
|
+
sheetId="lms-achievements-form-sheet"
|
|
375
|
+
defaultWidth={672}
|
|
376
|
+
minWidth={420}
|
|
377
|
+
maxWidth={960}
|
|
378
|
+
className="w-full overflow-y-auto"
|
|
379
|
+
>
|
|
374
380
|
<SheetHeader>
|
|
375
381
|
<SheetTitle>
|
|
376
382
|
{achievementToEdit ? t('sheet.editTitle') : t('sheet.createTitle')}
|
|
@@ -532,7 +538,7 @@ function AchievementFormSheet({
|
|
|
532
538
|
</div>
|
|
533
539
|
</form>
|
|
534
540
|
</Form>
|
|
535
|
-
</
|
|
541
|
+
</ResizableSheetContent>
|
|
536
542
|
</Sheet>
|
|
537
543
|
);
|
|
538
544
|
}
|
|
@@ -45,11 +45,11 @@ import {
|
|
|
45
45
|
} from '@/components/ui/select';
|
|
46
46
|
import {
|
|
47
47
|
Sheet,
|
|
48
|
-
SheetContent,
|
|
49
48
|
SheetDescription,
|
|
50
49
|
SheetHeader,
|
|
51
50
|
SheetTitle,
|
|
52
51
|
} from '@/components/ui/sheet';
|
|
52
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
53
53
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
54
54
|
import {
|
|
55
55
|
Table,
|
|
@@ -364,7 +364,13 @@ function WalletFormSheet({
|
|
|
364
364
|
return (
|
|
365
365
|
<>
|
|
366
366
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
367
|
-
<
|
|
367
|
+
<ResizableSheetContent
|
|
368
|
+
sheetId="lms-bitcodes-wallet-sheet"
|
|
369
|
+
defaultWidth={896}
|
|
370
|
+
minWidth={560}
|
|
371
|
+
maxWidth={1200}
|
|
372
|
+
className="w-full overflow-y-auto"
|
|
373
|
+
>
|
|
368
374
|
<SheetHeader>
|
|
369
375
|
<SheetTitle>
|
|
370
376
|
{walletToEdit ? t('sheet.editTitle') : t('sheet.createTitle')}
|
|
@@ -680,7 +686,7 @@ function WalletFormSheet({
|
|
|
680
686
|
) : null
|
|
681
687
|
) : null}
|
|
682
688
|
</div>
|
|
683
|
-
</
|
|
689
|
+
</ResizableSheetContent>
|
|
684
690
|
</Sheet>
|
|
685
691
|
</>
|
|
686
692
|
);
|
|
@@ -21,12 +21,12 @@ import {
|
|
|
21
21
|
} from '@/components/ui/select';
|
|
22
22
|
import {
|
|
23
23
|
Sheet,
|
|
24
|
-
SheetContent,
|
|
25
24
|
SheetDescription,
|
|
26
25
|
SheetFooter,
|
|
27
26
|
SheetHeader,
|
|
28
27
|
SheetTitle,
|
|
29
28
|
} from '@/components/ui/sheet';
|
|
29
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
30
30
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
31
31
|
import { Switch } from '@/components/ui/switch';
|
|
32
32
|
import { Textarea } from '@/components/ui/textarea';
|
|
@@ -616,7 +616,11 @@ export default function IssuedCertificatesPage() {
|
|
|
616
616
|
) : null}
|
|
617
617
|
|
|
618
618
|
<Sheet open={isTemplateSheetOpen} onOpenChange={setIsTemplateSheetOpen}>
|
|
619
|
-
<
|
|
619
|
+
<ResizableSheetContent
|
|
620
|
+
sheetId="lms-certificates-issued-create-template-sheet"
|
|
621
|
+
defaultWidth={560}
|
|
622
|
+
minWidth={420}
|
|
623
|
+
maxWidth={920}
|
|
620
624
|
side="right"
|
|
621
625
|
className="flex w-full flex-col sm:max-w-lg overflow-y-auto"
|
|
622
626
|
>
|
|
@@ -695,7 +699,7 @@ export default function IssuedCertificatesPage() {
|
|
|
695
699
|
{t('sheet.submit')}
|
|
696
700
|
</Button>
|
|
697
701
|
</SheetFooter>
|
|
698
|
-
</
|
|
702
|
+
</ResizableSheetContent>
|
|
699
703
|
</Sheet>
|
|
700
704
|
</Page>
|
|
701
705
|
);
|