@hed-hog/lms 0.0.329 → 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.
- package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +18 -8
- package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +10 -8
- package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +5 -9
- package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +5 -9
- package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +15 -14
- package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +66 -29
- package/hedhog/frontend/app/certificates/models/TemplateEditorPage.tsx.ejs +4 -2
- package/hedhog/frontend/app/certificates/models/TopBar.tsx.ejs +44 -34
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +27 -27
- package/hedhog/frontend/app/classes/page.tsx.ejs +23 -15
- package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +2 -2
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +8 -6
- package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +5 -3
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +9 -7
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-skeleton.tsx.ejs +3 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +4 -2
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +24 -23
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +228 -152
- package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +21 -19
- package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +78 -36
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +18 -16
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +13 -11
- package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +5 -3
- package/hedhog/frontend/app/courses/[id]/structure/_components/use-course-structure-shortcuts.ts.ejs +14 -9
- package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +42 -25
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +37 -41
- package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +3 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +10 -8
- package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +22 -20
- package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +3 -3
- package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +21 -19
- package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +34 -36
- package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +3 -1
- package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +7 -5
- package/hedhog/frontend/app/enterprise/page.tsx.ejs +106 -54
- package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +1 -1
- package/hedhog/frontend/app/exams/page.tsx.ejs +6 -2
- package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +79 -59
- package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +145 -119
- package/hedhog/frontend/app/instructors/page.tsx.ejs +75 -54
- package/hedhog/frontend/app/paths/page.tsx.ejs +11 -7
- package/hedhog/frontend/app/reports/courses/page.tsx.ejs +5 -5
- package/hedhog/frontend/app/reports/dashboard/page.tsx.ejs +8 -8
- package/hedhog/frontend/app/reports/page.tsx.ejs +7 -7
- package/hedhog/frontend/app/reports/students/page.tsx.ejs +6 -6
- package/hedhog/frontend/app/training/page.tsx.ejs +5 -5
- package/hedhog/frontend/messages/en.json +899 -45
- package/hedhog/frontend/messages/pt.json +894 -38
- package/hedhog/frontend/widgets/active-classes-kpi.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/active-courses-kpi.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/approval-rate-kpi.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/class-calendar.tsx.ejs +2 -2
- package/hedhog/frontend/widgets/completion-rate-kpi.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/issued-certificates-kpi.tsx.ejs +1 -1
- package/hedhog/frontend/widgets/total-students-kpi.tsx.ejs +1 -1
- package/hedhog/table/instructor_qualification.yaml +1 -1
- package/hedhog/table/instructor_skill.yaml +1 -1
- 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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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('
|
|
296
|
+
toast.success(t('form.toasts.skillsSaved'));
|
|
291
297
|
} catch {
|
|
292
|
-
toast.error('
|
|
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('
|
|
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('
|
|
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
|
-
? '
|
|
341
|
-
: '
|
|
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
|
-
? '
|
|
385
|
-
: '
|
|
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>
|
|
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">
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
<TabsTrigger value="
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
471
|
-
title=
|
|
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>
|
|
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
|
|
503
|
+
<SelectValue
|
|
504
|
+
placeholder={t('filters.statusPlaceholder')}
|
|
505
|
+
/>
|
|
494
506
|
</SelectTrigger>
|
|
495
507
|
<SelectContent>
|
|
496
|
-
<SelectItem value="active">
|
|
508
|
+
<SelectItem value="active">
|
|
509
|
+
{t('form.fields.active')}
|
|
510
|
+
</SelectItem>
|
|
497
511
|
<SelectItem value="inactive">
|
|
498
|
-
|
|
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>
|
|
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=
|
|
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
|
-
|
|
540
|
+
{t('form.fields.canTeachCourses')}
|
|
527
541
|
</p>
|
|
528
542
|
<p className="text-xs text-muted-foreground">
|
|
529
|
-
|
|
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
|
-
|
|
562
|
+
{t('form.fields.trainingAccess')}
|
|
550
563
|
</p>
|
|
551
564
|
<p className="text-xs text-muted-foreground">
|
|
552
565
|
{existingInstructor?.userId
|
|
553
|
-
? '
|
|
554
|
-
: '
|
|
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
|
-
? '
|
|
574
|
-
: '
|
|
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
|
-
|
|
601
|
+
{t('form.fields.qualifications')}{' '}
|
|
591
602
|
<span className="text-destructive">*</span>
|
|
592
603
|
</FieldLabel>
|
|
593
604
|
<div className="space-y-3">
|
|
594
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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={
|
|
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
|
-
|
|
717
|
+
{t('form.skills.none')}
|
|
705
718
|
</p>
|
|
706
719
|
)}
|
|
707
720
|
|
|
708
721
|
<EntityPicker
|
|
709
722
|
key={pickerResetKey}
|
|
710
|
-
placeholder=
|
|
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=
|
|
717
|
-
createTitle=
|
|
718
|
-
createDescription=
|
|
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: '
|
|
754
|
+
label: t('form.skills.createFields.name'),
|
|
742
755
|
required: true,
|
|
743
|
-
placeholder: '
|
|
756
|
+
placeholder: t('form.skills.createFields.namePlaceholder'),
|
|
744
757
|
},
|
|
745
758
|
{
|
|
746
759
|
name: 'slug',
|
|
747
|
-
label: '
|
|
748
|
-
placeholder: '
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
837
|
+
{t('form.classes.emptyTitle')}
|
|
825
838
|
</p>
|
|
826
839
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
827
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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>
|
|
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
|
-
|
|
906
|
+
{t('form.evaluations.comingSoonTitle')}
|
|
890
907
|
</p>
|
|
891
908
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
892
|
-
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
942
|
-
title=
|
|
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>
|
|
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
|
|
981
|
+
<SelectValue
|
|
982
|
+
placeholder={t('filters.statusPlaceholder')}
|
|
983
|
+
/>
|
|
965
984
|
</SelectTrigger>
|
|
966
985
|
<SelectContent>
|
|
967
|
-
<SelectItem value="active">
|
|
968
|
-
|
|
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>
|
|
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=
|
|
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">
|
|
1017
|
+
<p className="text-sm font-medium">
|
|
1018
|
+
{t('form.fields.canTeachCourses')}
|
|
1019
|
+
</p>
|
|
995
1020
|
<p className="text-xs text-muted-foreground">
|
|
996
|
-
|
|
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
|
-
|
|
1039
|
+
{t('form.fields.qualifications')}{' '}
|
|
1040
|
+
<span className="text-destructive">*</span>
|
|
1015
1041
|
</FieldLabel>
|
|
1016
1042
|
<div className="space-y-3">
|
|
1017
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
1103
|
+
{t('form.buttons.create')}
|
|
1078
1104
|
</Button>
|
|
1079
1105
|
</SheetFooter>
|
|
1080
1106
|
</form>
|