@hed-hog/lms 0.0.330 → 0.0.331

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 (31) hide show
  1. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +3 -3
  2. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +1 -1
  3. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +17 -17
  4. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +2 -2
  5. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +3 -3
  6. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +1 -1
  7. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +228 -152
  8. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +71 -31
  9. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +37 -41
  10. package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +1 -1
  11. package/hedhog/frontend/app/exams/page.tsx.ejs +6 -2
  12. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +145 -119
  13. package/hedhog/frontend/app/instructors/page.tsx.ejs +71 -52
  14. package/hedhog/frontend/app/paths/page.tsx.ejs +11 -7
  15. package/hedhog/frontend/app/reports/courses/page.tsx.ejs +5 -5
  16. package/hedhog/frontend/app/reports/dashboard/page.tsx.ejs +8 -8
  17. package/hedhog/frontend/app/reports/page.tsx.ejs +7 -7
  18. package/hedhog/frontend/app/reports/students/page.tsx.ejs +6 -6
  19. package/hedhog/frontend/app/training/page.tsx.ejs +5 -5
  20. package/hedhog/frontend/messages/en.json +294 -46
  21. package/hedhog/frontend/messages/pt.json +289 -39
  22. package/hedhog/frontend/widgets/active-classes-kpi.tsx.ejs +1 -1
  23. package/hedhog/frontend/widgets/active-courses-kpi.tsx.ejs +1 -1
  24. package/hedhog/frontend/widgets/approval-rate-kpi.tsx.ejs +1 -1
  25. package/hedhog/frontend/widgets/class-calendar.tsx.ejs +2 -2
  26. package/hedhog/frontend/widgets/completion-rate-kpi.tsx.ejs +1 -1
  27. package/hedhog/frontend/widgets/issued-certificates-kpi.tsx.ejs +1 -1
  28. package/hedhog/frontend/widgets/total-students-kpi.tsx.ejs +1 -1
  29. package/hedhog/table/instructor_qualification.yaml +1 -1
  30. package/hedhog/table/instructor_skill.yaml +1 -1
  31. package/package.json +7 -7
@@ -31,6 +31,7 @@ import { Switch } from '@/components/ui/switch';
31
31
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
32
32
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
33
33
  import { zodResolver } from '@hookform/resolvers/zod';
34
+ import { useTranslations } from 'next-intl';
34
35
  import { useQueryClient } from '@tanstack/react-query';
35
36
  import { BookOpen, Loader2, Pencil, Star, X } from 'lucide-react';
36
37
  import { EntityPicker } from '@/components/ui/entity-picker';
@@ -40,35 +41,6 @@ import { toast } from 'sonner';
40
41
  import { z } from 'zod';
41
42
  import type { InstructorRow, InstructorSkill } from './instructor-types';
42
43
 
43
- const QUALIFICATION_OPTIONS = [
44
- {
45
- slug: 'course-lessons',
46
- label: 'Aulas de curso',
47
- description: 'Pode atuar em aulas gravadas e estrutura de curso.',
48
- },
49
- {
50
- slug: 'class-sessions',
51
- label: 'Sessões de turma',
52
- description: 'Pode atuar em contextos ao vivo e turmas.',
53
- },
54
- ];
55
-
56
- const instructorFormSchema = z.object({
57
- personId: z
58
- .number({ invalid_type_error: 'Selecione uma pessoa' })
59
- .int()
60
- .positive('Selecione uma pessoa'),
61
- qualificationSlugs: z
62
- .array(z.string())
63
- .min(1, 'Selecione pelo menos uma qualificação'),
64
- status: z.enum(['active', 'inactive']),
65
- can_teach_courses: z.boolean(),
66
- hourlyRate: z.coerce.number().min(0).optional().nullable(),
67
- skillSlugs: z.array(z.string()).optional().default([]),
68
- });
69
-
70
- type InstructorFormValues = z.infer<typeof instructorFormSchema>;
71
-
72
44
  type ClassGroupItem = {
73
45
  id: number;
74
46
  name: string;
@@ -95,13 +67,13 @@ function getSkillDisplayName(s: InstructorSkill): string {
95
67
  return locales?.[0]?.name ?? s.name ?? s.slug;
96
68
  }
97
69
 
98
- const STATUS_LABEL: Record<string, string> = {
99
- active: 'Ativo',
100
- inactive: 'Inativo',
101
- in_progress: 'Em andamento',
102
- upcoming: 'Futuro',
103
- completed: 'Concluído',
104
- cancelled: 'Cancelado',
70
+ type InstructorFormValues = {
71
+ personId?: number;
72
+ qualificationSlugs: string[];
73
+ status: 'active' | 'inactive';
74
+ can_teach_courses: boolean;
75
+ hourlyRate?: number | null;
76
+ skillSlugs?: string[];
105
77
  };
106
78
 
107
79
  type InstructorFormSheetProps = {
@@ -118,6 +90,7 @@ export function InstructorFormSheet({
118
90
  onSaved,
119
91
  }: InstructorFormSheetProps) {
120
92
  const { request, currentLocaleCode } = useApp();
93
+ const t = useTranslations('lms.InstructorsPage');
121
94
  const queryClient = useQueryClient();
122
95
  const [saving, setSaving] = useState(false);
123
96
  const [selectedPersonName, setSelectedPersonName] = useState('');
@@ -130,6 +103,39 @@ export function InstructorFormSheet({
130
103
  const [savingSkills, setSavingSkills] = useState(false);
131
104
  const [pickerResetKey, setPickerResetKey] = useState(0);
132
105
  const isEditing = typeof instructorId === 'number' && instructorId > 0;
106
+ const qualificationOptions = [
107
+ {
108
+ slug: 'course-lessons',
109
+ label: t('qualificationLabels.courseLessons'),
110
+ description: t('form.qualificationDescriptions.courseLessons'),
111
+ },
112
+ {
113
+ slug: 'class-sessions',
114
+ label: t('qualificationLabels.classSessions'),
115
+ description: t('form.qualificationDescriptions.classSessions'),
116
+ },
117
+ ];
118
+ const statusLabel: Record<string, string> = {
119
+ active: t('form.fields.active'),
120
+ inactive: t('form.fields.inactive'),
121
+ in_progress: t('form.classStatuses.inProgress'),
122
+ upcoming: t('form.classStatuses.upcoming'),
123
+ completed: t('form.classStatuses.completed'),
124
+ cancelled: t('form.classStatuses.cancelled'),
125
+ };
126
+ const instructorFormSchema = z.object({
127
+ personId: z
128
+ .number({ invalid_type_error: t('form.fields.personRequired') })
129
+ .int()
130
+ .positive(t('form.fields.personRequired')),
131
+ qualificationSlugs: z
132
+ .array(z.string())
133
+ .min(1, t('form.fields.qualificationsRequired')),
134
+ status: z.enum(['active', 'inactive']),
135
+ can_teach_courses: z.boolean(),
136
+ hourlyRate: z.coerce.number().min(0).optional().nullable(),
137
+ skillSlugs: z.array(z.string()).optional().default([]),
138
+ });
133
139
 
134
140
  const {
135
141
  control,
@@ -287,9 +293,9 @@ export function InstructorFormSheet({
287
293
  queryKey: ['lms-instructor-detail', instructorId],
288
294
  });
289
295
  await queryClient.invalidateQueries({ queryKey: ['lms-instructors'] });
290
- toast.success('Skills atualizadas com sucesso.');
296
+ toast.success(t('form.toasts.skillsSaved'));
291
297
  } catch {
292
- toast.error('Erro ao salvar skills.');
298
+ toast.error(t('form.toasts.skillsError'));
293
299
  } finally {
294
300
  setSavingSkills(false);
295
301
  }
@@ -314,7 +320,7 @@ export function InstructorFormSheet({
314
320
  },
315
321
  });
316
322
  result = response.data;
317
- toast.success('Instrutor atualizado com sucesso.');
323
+ toast.success(t('form.toasts.instructorUpdated'));
318
324
  } else {
319
325
  const response = await request<InstructorRow>({
320
326
  url: '/lms/instructors',
@@ -329,7 +335,7 @@ export function InstructorFormSheet({
329
335
  },
330
336
  });
331
337
  result = response.data;
332
- toast.success('Instrutor criado com sucesso.');
338
+ toast.success(t('form.toasts.instructorCreated'));
333
339
  }
334
340
 
335
341
  await onSaved?.(result);
@@ -337,8 +343,8 @@ export function InstructorFormSheet({
337
343
  } catch {
338
344
  toast.error(
339
345
  isEditing
340
- ? 'Erro ao atualizar instrutor. Tente novamente.'
341
- : 'Erro ao criar instrutor. Tente novamente.'
346
+ ? t('form.toasts.updateError')
347
+ : t('form.toasts.createError')
342
348
  );
343
349
  } finally {
344
350
  setSaving(false);
@@ -381,8 +387,8 @@ export function InstructorFormSheet({
381
387
  }
382
388
  >
383
389
  {existingInstructor.status === 'active'
384
- ? 'Ativo'
385
- : 'Inativo'}
390
+ ? t('form.fields.active')
391
+ : t('form.fields.inactive')}
386
392
  </Badge>
387
393
  {existingInstructor.email && (
388
394
  <span className="truncate text-xs text-muted-foreground">
@@ -399,11 +405,8 @@ export function InstructorFormSheet({
399
405
  </div>
400
406
  ) : (
401
407
  <SheetHeader className="shrink-0 border-b px-6 py-4">
402
- <SheetTitle>Novo Instrutor</SheetTitle>
403
- <SheetDescription>
404
- Vincule uma pessoa existente ou crie uma nova para cadastrá-la
405
- como instrutora.
406
- </SheetDescription>
408
+ <SheetTitle>{t('form.title')}</SheetTitle>
409
+ <SheetDescription>{t('form.description')}</SheetDescription>
407
410
  </SheetHeader>
408
411
  )}
409
412
 
@@ -415,10 +418,14 @@ export function InstructorFormSheet({
415
418
  className="flex min-h-0 flex-1 flex-col"
416
419
  >
417
420
  <TabsList className="mx-6 mt-3 shrink-0 justify-start">
418
- <TabsTrigger value="detalhes">Detalhes</TabsTrigger>
419
- <TabsTrigger value="skills">Skills</TabsTrigger>
420
- <TabsTrigger value="turmas">Turmas</TabsTrigger>
421
- <TabsTrigger value="avaliacoes">Avaliações</TabsTrigger>
421
+ <TabsTrigger value="detalhes">
422
+ {t('form.tabs.details')}
423
+ </TabsTrigger>
424
+ <TabsTrigger value="skills">{t('form.tabs.skills')}</TabsTrigger>
425
+ <TabsTrigger value="turmas">{t('form.tabs.classes')}</TabsTrigger>
426
+ <TabsTrigger value="avaliacoes">
427
+ {t('form.tabs.evaluations')}
428
+ </TabsTrigger>
422
429
  </TabsList>
423
430
 
424
431
  {/* ── Detalhes ── */}
@@ -435,7 +442,8 @@ export function InstructorFormSheet({
435
442
  {/* Person picker */}
436
443
  <Field>
437
444
  <FieldLabel>
438
- Pessoa <span className="text-destructive">*</span>
445
+ {t('form.fields.person')}{' '}
446
+ <span className="text-destructive">*</span>
439
447
  </FieldLabel>
440
448
  <div className="flex items-center gap-2">
441
449
  <div className="min-w-0 flex-1">
@@ -445,14 +453,16 @@ export function InstructorFormSheet({
445
453
  render={({ field }) => (
446
454
  <PersonPicker
447
455
  label=""
448
- entityLabel="pessoa"
456
+ entityLabel={t('form.fields.person')}
449
457
  value={field.value ?? null}
450
458
  initialSelectedLabel={selectedPersonName}
451
459
  onChange={(personId, personName) => {
452
460
  field.onChange(personId ?? undefined);
453
461
  setSelectedPersonName(personName);
454
462
  }}
455
- selectPlaceholder="Buscar ou selecionar pessoa..."
463
+ selectPlaceholder={t(
464
+ 'form.fields.searchPerson'
465
+ )}
456
466
  personTypeFilter="individual"
457
467
  createType="individual"
458
468
  lockCreateType
@@ -467,8 +477,8 @@ export function InstructorFormSheet({
467
477
  className="shrink-0"
468
478
  disabled={!watchedPersonId}
469
479
  onClick={handleOpenPersonEdit}
470
- aria-label="Editar cadastro da pessoa"
471
- title="Editar cadastro da pessoa"
480
+ aria-label={t('form.fields.editPerson')}
481
+ title={t('form.fields.editPerson')}
472
482
  >
473
483
  <Pencil className="h-4 w-4" />
474
484
  </Button>
@@ -480,7 +490,7 @@ export function InstructorFormSheet({
480
490
 
481
491
  {/* Status */}
482
492
  <Field>
483
- <FieldLabel>Status</FieldLabel>
493
+ <FieldLabel>{t('form.fields.status')}</FieldLabel>
484
494
  <Controller
485
495
  control={control}
486
496
  name="status"
@@ -490,12 +500,16 @@ export function InstructorFormSheet({
490
500
  onValueChange={field.onChange}
491
501
  >
492
502
  <SelectTrigger className="w-full">
493
- <SelectValue placeholder="Selecione o status" />
503
+ <SelectValue
504
+ placeholder={t('filters.statusPlaceholder')}
505
+ />
494
506
  </SelectTrigger>
495
507
  <SelectContent>
496
- <SelectItem value="active">Ativo</SelectItem>
508
+ <SelectItem value="active">
509
+ {t('form.fields.active')}
510
+ </SelectItem>
497
511
  <SelectItem value="inactive">
498
- Inativo
512
+ {t('form.fields.inactive')}
499
513
  </SelectItem>
500
514
  </SelectContent>
501
515
  </Select>
@@ -505,13 +519,13 @@ export function InstructorFormSheet({
505
519
 
506
520
  {/* Valor/hora */}
507
521
  <Field>
508
- <FieldLabel>Valor/hora (R$)</FieldLabel>
522
+ <FieldLabel>{t('form.fields.hourlyRate')}</FieldLabel>
509
523
  <Controller
510
524
  control={control}
511
525
  name="hourlyRate"
512
526
  render={({ field }) => (
513
527
  <InputMoney
514
- placeholder="Ex: 150,00"
528
+ placeholder={t('form.fields.hourlyRatePlaceholder')}
515
529
  value={field.value ?? ''}
516
530
  onValueChange={(value) => field.onChange(value)}
517
531
  />
@@ -523,11 +537,10 @@ export function InstructorFormSheet({
523
537
  <div className="flex items-center justify-between rounded-lg border p-4">
524
538
  <div className="space-y-0.5">
525
539
  <p className="text-sm font-medium">
526
- Pode ensinar cursos
540
+ {t('form.fields.canTeachCourses')}
527
541
  </p>
528
542
  <p className="text-xs text-muted-foreground">
529
- Permite que o instrutor seja vinculado a aulas de
530
- curso.
543
+ {t('form.fields.canTeachCoursesHint')}
531
544
  </p>
532
545
  </div>
533
546
  <Controller
@@ -546,12 +559,12 @@ export function InstructorFormSheet({
546
559
  <div className="flex items-center justify-between rounded-lg border p-4">
547
560
  <div className="space-y-0.5">
548
561
  <p className="text-sm font-medium">
549
- Acesso ao Hcode Training
562
+ {t('form.fields.trainingAccess')}
550
563
  </p>
551
564
  <p className="text-xs text-muted-foreground">
552
565
  {existingInstructor?.userId
553
- ? 'Permite que este instrutor acesse a plataforma com o perfil de instrutor.'
554
- : 'Nenhum usuário vinculado a este cadastro. Vincule um usuário para habilitar.'}
566
+ ? t('form.fields.trainingAccessHint')
567
+ : t('form.fields.noUserLinked')}
555
568
  </p>
556
569
  </div>
557
570
  <Switch
@@ -570,13 +583,11 @@ export function InstructorFormSheet({
570
583
  setTrainingAccess(next);
571
584
  toast.success(
572
585
  next
573
- ? 'Acesso ao Training habilitado.'
574
- : 'Acesso ao Training desabilitado.'
586
+ ? t('form.toasts.accessEnabled')
587
+ : t('form.toasts.accessDisabled')
575
588
  );
576
589
  } catch {
577
- toast.error(
578
- 'Erro ao alterar acesso ao Training.'
579
- );
590
+ toast.error(t('form.toasts.accessError'));
580
591
  } finally {
581
592
  setTogglingAccess(false);
582
593
  }
@@ -587,11 +598,11 @@ export function InstructorFormSheet({
587
598
  {/* Qualificações */}
588
599
  <Field>
589
600
  <FieldLabel>
590
- Qualificações{' '}
601
+ {t('form.fields.qualifications')}{' '}
591
602
  <span className="text-destructive">*</span>
592
603
  </FieldLabel>
593
604
  <div className="space-y-3">
594
- {QUALIFICATION_OPTIONS.map((option) => (
605
+ {qualificationOptions.map((option) => (
595
606
  <Controller
596
607
  key={option.slug}
597
608
  control={control}
@@ -649,13 +660,13 @@ export function InstructorFormSheet({
649
660
  onClick={() => handleSheetClose(false)}
650
661
  disabled={saving}
651
662
  >
652
- Cancelar
663
+ {t('form.buttons.cancel')}
653
664
  </Button>
654
665
  <Button type="submit" disabled={saving}>
655
666
  {saving && (
656
667
  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
657
668
  )}
658
- Salvar alterações
669
+ {t('form.buttons.save')}
659
670
  </Button>
660
671
  </SheetFooter>
661
672
  </form>
@@ -669,7 +680,7 @@ export function InstructorFormSheet({
669
680
  <div className="flex min-h-0 flex-1 flex-col">
670
681
  <div className="flex-1 overflow-y-auto px-6 py-4">
671
682
  <p className="mb-4 text-sm text-muted-foreground">
672
- Gerencie as skills associadas a este instrutor.
683
+ {t('form.skills.description')}
673
684
  </p>
674
685
 
675
686
  {tabSkillSlugs.length > 0 ? (
@@ -691,7 +702,9 @@ export function InstructorFormSheet({
691
702
  prev.filter((s) => s !== slug)
692
703
  )
693
704
  }
694
- aria-label={`Remover skill ${skill?.name ?? slug}`}
705
+ aria-label={t('form.skills.removeSkill', {
706
+ skill: skill?.name ?? slug,
707
+ })}
695
708
  >
696
709
  <X className="h-3 w-3" />
697
710
  </button>
@@ -701,21 +714,21 @@ export function InstructorFormSheet({
701
714
  </div>
702
715
  ) : (
703
716
  <p className="mb-6 italic text-sm text-muted-foreground">
704
- Nenhuma skill atribuída.
717
+ {t('form.skills.none')}
705
718
  </p>
706
719
  )}
707
720
 
708
721
  <EntityPicker
709
722
  key={pickerResetKey}
710
- placeholder="Adicionar skill..."
723
+ placeholder={t('form.skills.addSkill')}
711
724
  value={null}
712
725
  clearable={false}
713
726
  valueType="string"
714
727
  showCreateButton
715
728
  entityLabel="skill"
716
- noResultsLabel="Nenhuma skill disponível"
717
- createTitle="Nova skill"
718
- createDescription="Cadastre uma nova skill para vincular ao instrutor."
729
+ noResultsLabel={t('form.skills.noSkills')}
730
+ createTitle={t('form.skills.newSkill')}
731
+ createDescription={t('form.skills.newSkillDescription')}
719
732
  options={skillsData
720
733
  .filter((s) => !tabSkillSlugs.includes(s.slug))
721
734
  .map((s) => ({
@@ -738,14 +751,14 @@ export function InstructorFormSheet({
738
751
  createFields={[
739
752
  {
740
753
  name: 'namePt',
741
- label: 'Nome',
754
+ label: t('form.skills.createFields.name'),
742
755
  required: true,
743
- placeholder: 'Ex: JavaScript',
756
+ placeholder: t('form.skills.createFields.namePlaceholder'),
744
757
  },
745
758
  {
746
759
  name: 'slug',
747
- label: 'Slug (opcional)',
748
- placeholder: 'Ex: javascript',
760
+ label: t('form.skills.createFields.slug'),
761
+ placeholder: t('form.skills.createFields.slugPlaceholder'),
749
762
  },
750
763
  ]}
751
764
  mapSearchToCreateValues={(search) => ({
@@ -775,7 +788,7 @@ export function InstructorFormSheet({
775
788
  name: namePt,
776
789
  } as any;
777
790
  } catch {
778
- toast.error('Erro ao criar skill.');
791
+ toast.error(t('InstructorSkillsPage.messages.createError'));
779
792
  return null;
780
793
  }
781
794
  }}
@@ -790,7 +803,7 @@ export function InstructorFormSheet({
790
803
  onClick={() => handleSheetClose(false)}
791
804
  disabled={savingSkills}
792
805
  >
793
- Cancelar
806
+ {t('form.buttons.cancel')}
794
807
  </Button>
795
808
  <Button
796
809
  type="button"
@@ -800,7 +813,7 @@ export function InstructorFormSheet({
800
813
  {savingSkills && (
801
814
  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
802
815
  )}
803
- Salvar skills
816
+ {t('form.buttons.saveSkills')}
804
817
  </Button>
805
818
  </div>
806
819
  </div>
@@ -821,10 +834,10 @@ export function InstructorFormSheet({
821
834
  <div className="flex flex-col items-center justify-center py-12 text-center">
822
835
  <BookOpen className="mb-3 h-10 w-10 text-muted-foreground/40" />
823
836
  <p className="text-sm font-medium text-muted-foreground">
824
- Nenhuma turma encontrada
837
+ {t('form.classes.emptyTitle')}
825
838
  </p>
826
839
  <p className="mt-1 text-xs text-muted-foreground">
827
- Este instrutor ainda não está vinculado a nenhuma turma.
840
+ {t('form.classes.emptyDescription')}
828
841
  </p>
829
842
  </div>
830
843
  ) : (
@@ -846,13 +859,13 @@ export function InstructorFormSheet({
846
859
  variant="outline"
847
860
  className="shrink-0 text-xs"
848
861
  >
849
- {STATUS_LABEL[turma.status] ?? turma.status}
862
+ {statusLabel[turma.status] ?? turma.status}
850
863
  </Badge>
851
864
  </div>
852
865
  <div className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
853
866
  {turma.startDate && (
854
867
  <span>
855
- Início:{' '}
868
+ {t('form.classes.start')}{' '}
856
869
  {new Date(turma.startDate).toLocaleDateString(
857
870
  'pt-BR'
858
871
  )}
@@ -860,14 +873,18 @@ export function InstructorFormSheet({
860
873
  )}
861
874
  {turma.endDate && (
862
875
  <span>
863
- Fim:{' '}
876
+ {t('form.classes.end')}{' '}
864
877
  {new Date(turma.endDate).toLocaleDateString(
865
878
  'pt-BR'
866
879
  )}
867
880
  </span>
868
881
  )}
869
882
  {turma.slots > 0 && (
870
- <span>{turma.slots} aluno(s)</span>
883
+ <span>
884
+ {t('form.classes.students', {
885
+ count: turma.slots,
886
+ })}
887
+ </span>
871
888
  )}
872
889
  </div>
873
890
  </div>
@@ -886,11 +903,10 @@ export function InstructorFormSheet({
886
903
  <div className="flex flex-1 flex-col items-center justify-center px-6 py-12 text-center">
887
904
  <Star className="mb-3 h-10 w-10 text-muted-foreground/40" />
888
905
  <p className="text-sm font-medium text-muted-foreground">
889
- Avaliações — funcionalidade em breve
906
+ {t('form.evaluations.comingSoonTitle')}
890
907
  </p>
891
908
  <p className="mt-1 text-xs text-muted-foreground">
892
- Em breve você poderá visualizar as avaliações recebidas
893
- pelos alunos.
909
+ {t('form.evaluations.comingSoonDescription')}
894
910
  </p>
895
911
  </div>
896
912
  </TabsContent>
@@ -906,7 +922,8 @@ export function InstructorFormSheet({
906
922
  {/* Person picker */}
907
923
  <Field>
908
924
  <FieldLabel>
909
- Pessoa <span className="text-destructive">*</span>
925
+ {t('form.fields.person')}{' '}
926
+ <span className="text-destructive">*</span>
910
927
  </FieldLabel>
911
928
  <div className="flex items-center gap-2">
912
929
  <div className="min-w-0 flex-1">
@@ -916,14 +933,14 @@ export function InstructorFormSheet({
916
933
  render={({ field }) => (
917
934
  <PersonPicker
918
935
  label=""
919
- entityLabel="pessoa"
936
+ entityLabel={t('form.fields.person')}
920
937
  value={field.value ?? null}
921
938
  initialSelectedLabel={selectedPersonName}
922
939
  onChange={(personId, personName) => {
923
940
  field.onChange(personId ?? undefined);
924
941
  setSelectedPersonName(personName);
925
942
  }}
926
- selectPlaceholder="Buscar ou selecionar pessoa..."
943
+ selectPlaceholder={t('form.fields.searchPerson')}
927
944
  personTypeFilter="individual"
928
945
  createType="individual"
929
946
  lockCreateType
@@ -938,8 +955,8 @@ export function InstructorFormSheet({
938
955
  className="shrink-0"
939
956
  disabled={!watchedPersonId}
940
957
  onClick={handleOpenPersonEdit}
941
- aria-label="Editar cadastro da pessoa"
942
- title="Editar cadastro da pessoa"
958
+ aria-label={t('form.fields.editPerson')}
959
+ title={t('form.fields.editPerson')}
943
960
  >
944
961
  <Pencil className="h-4 w-4" />
945
962
  </Button>
@@ -951,7 +968,7 @@ export function InstructorFormSheet({
951
968
 
952
969
  {/* Status */}
953
970
  <Field>
954
- <FieldLabel>Status</FieldLabel>
971
+ <FieldLabel>{t('form.fields.status')}</FieldLabel>
955
972
  <Controller
956
973
  control={control}
957
974
  name="status"
@@ -961,11 +978,17 @@ export function InstructorFormSheet({
961
978
  onValueChange={field.onChange}
962
979
  >
963
980
  <SelectTrigger className="w-full">
964
- <SelectValue placeholder="Selecione o status" />
981
+ <SelectValue
982
+ placeholder={t('filters.statusPlaceholder')}
983
+ />
965
984
  </SelectTrigger>
966
985
  <SelectContent>
967
- <SelectItem value="active">Ativo</SelectItem>
968
- <SelectItem value="inactive">Inativo</SelectItem>
986
+ <SelectItem value="active">
987
+ {t('form.fields.active')}
988
+ </SelectItem>
989
+ <SelectItem value="inactive">
990
+ {t('form.fields.inactive')}
991
+ </SelectItem>
969
992
  </SelectContent>
970
993
  </Select>
971
994
  )}
@@ -974,13 +997,13 @@ export function InstructorFormSheet({
974
997
 
975
998
  {/* Valor/hora */}
976
999
  <Field>
977
- <FieldLabel>Valor/hora (R$)</FieldLabel>
1000
+ <FieldLabel>{t('form.fields.hourlyRate')}</FieldLabel>
978
1001
  <Controller
979
1002
  control={control}
980
1003
  name="hourlyRate"
981
1004
  render={({ field }) => (
982
1005
  <InputMoney
983
- placeholder="Ex: 150,00"
1006
+ placeholder={t('form.fields.hourlyRatePlaceholder')}
984
1007
  value={field.value ?? ''}
985
1008
  onValueChange={(value) => field.onChange(value)}
986
1009
  />
@@ -991,9 +1014,11 @@ export function InstructorFormSheet({
991
1014
  {/* can_teach_courses */}
992
1015
  <div className="flex items-center justify-between rounded-lg border p-4">
993
1016
  <div className="space-y-0.5">
994
- <p className="text-sm font-medium">Pode ensinar cursos</p>
1017
+ <p className="text-sm font-medium">
1018
+ {t('form.fields.canTeachCourses')}
1019
+ </p>
995
1020
  <p className="text-xs text-muted-foreground">
996
- Permite que o instrutor seja vinculado a aulas de curso.
1021
+ {t('form.fields.canTeachCoursesHint')}
997
1022
  </p>
998
1023
  </div>
999
1024
  <Controller
@@ -1011,10 +1036,11 @@ export function InstructorFormSheet({
1011
1036
  {/* Qualificações */}
1012
1037
  <Field>
1013
1038
  <FieldLabel>
1014
- Qualificações <span className="text-destructive">*</span>
1039
+ {t('form.fields.qualifications')}{' '}
1040
+ <span className="text-destructive">*</span>
1015
1041
  </FieldLabel>
1016
1042
  <div className="space-y-3">
1017
- {QUALIFICATION_OPTIONS.map((option) => (
1043
+ {qualificationOptions.map((option) => (
1018
1044
  <Controller
1019
1045
  key={option.slug}
1020
1046
  control={control}
@@ -1070,11 +1096,11 @@ export function InstructorFormSheet({
1070
1096
  onClick={() => handleSheetClose(false)}
1071
1097
  disabled={saving}
1072
1098
  >
1073
- Cancelar
1099
+ {t('form.buttons.cancel')}
1074
1100
  </Button>
1075
1101
  <Button type="submit" disabled={saving}>
1076
1102
  {saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
1077
- Criar instrutor
1103
+ {t('form.buttons.create')}
1078
1104
  </Button>
1079
1105
  </SheetFooter>
1080
1106
  </form>