@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
|
@@ -60,6 +60,7 @@ import { cn } from '@/lib/utils';
|
|
|
60
60
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
61
61
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
62
62
|
import { MoreHorizontal, Pencil, Plus, Sparkles, Trash2 } from 'lucide-react';
|
|
63
|
+
import { useTranslations } from 'next-intl';
|
|
63
64
|
import { useEffect, useState } from 'react';
|
|
64
65
|
import { useForm } from 'react-hook-form';
|
|
65
66
|
import { toast } from 'sonner';
|
|
@@ -85,22 +86,26 @@ type InstructorSkillPaginatedResult = {
|
|
|
85
86
|
|
|
86
87
|
// ─── Zod schema ───────────────────────────────────────────────────────────────
|
|
87
88
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
const createSkillSchema = (t: ReturnType<typeof useTranslations>) =>
|
|
90
|
+
z.object({
|
|
91
|
+
slug: z
|
|
92
|
+
.string()
|
|
93
|
+
.min(1, t('form.validation.required'))
|
|
94
|
+
.max(100, t('form.validation.max100'))
|
|
95
|
+
.regex(/^[a-z0-9-]+$/, t('form.validation.slugPattern')),
|
|
96
|
+
namePt: z
|
|
97
|
+
.string()
|
|
98
|
+
.min(1, t('form.validation.required'))
|
|
99
|
+
.max(255, t('form.validation.max255')),
|
|
100
|
+
nameEn: z
|
|
101
|
+
.string()
|
|
102
|
+
.max(255, t('form.validation.max255'))
|
|
103
|
+
.optional()
|
|
104
|
+
.or(z.literal('')),
|
|
105
|
+
status: z.enum(['active', 'inactive']).default('active'),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
type SkillFormValues = z.infer<ReturnType<typeof createSkillSchema>>;
|
|
104
109
|
|
|
105
110
|
// ─── SkillFormSheet ────────────────────────────────────────────────────────────
|
|
106
111
|
|
|
@@ -118,10 +123,11 @@ function SkillFormSheet({
|
|
|
118
123
|
onSaved,
|
|
119
124
|
}: SkillFormSheetProps) {
|
|
120
125
|
const { request } = useApp();
|
|
126
|
+
const t = useTranslations('lms.InstructorSkillsPage');
|
|
121
127
|
const [isSaving, setIsSaving] = useState(false);
|
|
122
128
|
|
|
123
129
|
const form = useForm<SkillFormValues>({
|
|
124
|
-
resolver: zodResolver(
|
|
130
|
+
resolver: zodResolver(createSkillSchema(t)),
|
|
125
131
|
defaultValues: {
|
|
126
132
|
slug: '',
|
|
127
133
|
namePt: '',
|
|
@@ -167,20 +173,20 @@ function SkillFormSheet({
|
|
|
167
173
|
method: 'PATCH',
|
|
168
174
|
data: payload,
|
|
169
175
|
});
|
|
170
|
-
toast.success('
|
|
176
|
+
toast.success(t('messages.updateSuccess'));
|
|
171
177
|
} else {
|
|
172
178
|
await request({
|
|
173
179
|
url: '/lms/instructor-skills',
|
|
174
180
|
method: 'POST',
|
|
175
181
|
data: payload,
|
|
176
182
|
});
|
|
177
|
-
toast.success('
|
|
183
|
+
toast.success(t('messages.createSuccess'));
|
|
178
184
|
}
|
|
179
185
|
|
|
180
186
|
onSaved();
|
|
181
187
|
onOpenChange(false);
|
|
182
188
|
} catch {
|
|
183
|
-
toast.error('
|
|
189
|
+
toast.error(t('messages.saveError'));
|
|
184
190
|
} finally {
|
|
185
191
|
setIsSaving(false);
|
|
186
192
|
}
|
|
@@ -188,9 +194,11 @@ function SkillFormSheet({
|
|
|
188
194
|
|
|
189
195
|
return (
|
|
190
196
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
191
|
-
<SheetContent className="w-full overflow-y-auto sm:max-w-md">
|
|
197
|
+
<SheetContent className="w-full overflow-y-auto sm:max-w-md">
|
|
192
198
|
<SheetHeader>
|
|
193
|
-
<SheetTitle>
|
|
199
|
+
<SheetTitle>
|
|
200
|
+
{skillToEdit ? t('sheet.editTitle') : t('sheet.createTitle')}
|
|
201
|
+
</SheetTitle>
|
|
194
202
|
</SheetHeader>
|
|
195
203
|
|
|
196
204
|
<Form {...form}>
|
|
@@ -203,9 +211,9 @@ function SkillFormSheet({
|
|
|
203
211
|
name="slug"
|
|
204
212
|
render={({ field }) => (
|
|
205
213
|
<FormItem>
|
|
206
|
-
<FormLabel>
|
|
214
|
+
<FormLabel>{t('form.slug')}</FormLabel>
|
|
207
215
|
<FormControl>
|
|
208
|
-
<Input placeholder=
|
|
216
|
+
<Input placeholder={t('form.slugPlaceholder')} {...field} />
|
|
209
217
|
</FormControl>
|
|
210
218
|
<FormMessage />
|
|
211
219
|
</FormItem>
|
|
@@ -217,9 +225,12 @@ function SkillFormSheet({
|
|
|
217
225
|
name="namePt"
|
|
218
226
|
render={({ field }) => (
|
|
219
227
|
<FormItem>
|
|
220
|
-
<FormLabel>
|
|
228
|
+
<FormLabel>{t('form.namePt')}</FormLabel>
|
|
221
229
|
<FormControl>
|
|
222
|
-
<Input
|
|
230
|
+
<Input
|
|
231
|
+
placeholder={t('form.namePtPlaceholder')}
|
|
232
|
+
{...field}
|
|
233
|
+
/>
|
|
223
234
|
</FormControl>
|
|
224
235
|
<FormMessage />
|
|
225
236
|
</FormItem>
|
|
@@ -231,10 +242,10 @@ function SkillFormSheet({
|
|
|
231
242
|
name="nameEn"
|
|
232
243
|
render={({ field }) => (
|
|
233
244
|
<FormItem>
|
|
234
|
-
<FormLabel>
|
|
245
|
+
<FormLabel>{t('form.nameEn')}</FormLabel>
|
|
235
246
|
<FormControl>
|
|
236
247
|
<Input
|
|
237
|
-
placeholder=
|
|
248
|
+
placeholder={t('form.nameEnPlaceholder')}
|
|
238
249
|
{...field}
|
|
239
250
|
/>
|
|
240
251
|
</FormControl>
|
|
@@ -248,16 +259,22 @@ function SkillFormSheet({
|
|
|
248
259
|
name="status"
|
|
249
260
|
render={({ field }) => (
|
|
250
261
|
<FormItem>
|
|
251
|
-
<FormLabel>
|
|
262
|
+
<FormLabel>{t('form.status')}</FormLabel>
|
|
252
263
|
<Select onValueChange={field.onChange} value={field.value}>
|
|
253
264
|
<FormControl>
|
|
254
265
|
<SelectTrigger className="w-full">
|
|
255
|
-
<SelectValue
|
|
266
|
+
<SelectValue
|
|
267
|
+
placeholder={t('form.statusPlaceholder')}
|
|
268
|
+
/>
|
|
256
269
|
</SelectTrigger>
|
|
257
270
|
</FormControl>
|
|
258
271
|
<SelectContent>
|
|
259
|
-
<SelectItem value="active">
|
|
260
|
-
|
|
272
|
+
<SelectItem value="active">
|
|
273
|
+
{t('status.active')}
|
|
274
|
+
</SelectItem>
|
|
275
|
+
<SelectItem value="inactive">
|
|
276
|
+
{t('status.inactive')}
|
|
277
|
+
</SelectItem>
|
|
261
278
|
</SelectContent>
|
|
262
279
|
</Select>
|
|
263
280
|
<FormMessage />
|
|
@@ -272,10 +289,10 @@ function SkillFormSheet({
|
|
|
272
289
|
onClick={() => onOpenChange(false)}
|
|
273
290
|
disabled={isSaving}
|
|
274
291
|
>
|
|
275
|
-
|
|
292
|
+
{t('actions.cancel')}
|
|
276
293
|
</Button>
|
|
277
294
|
<Button type="submit" disabled={isSaving}>
|
|
278
|
-
{isSaving ? '
|
|
295
|
+
{isSaving ? t('actions.saving') : t('actions.save')}
|
|
279
296
|
</Button>
|
|
280
297
|
</div>
|
|
281
298
|
</form>
|
|
@@ -289,6 +306,7 @@ function SkillFormSheet({
|
|
|
289
306
|
|
|
290
307
|
export default function InstructorSkillsPage() {
|
|
291
308
|
const { request } = useApp();
|
|
309
|
+
const t = useTranslations('lms.InstructorSkillsPage');
|
|
292
310
|
|
|
293
311
|
const [page, setPage] = useState(1);
|
|
294
312
|
const [pageSize, setPageSize] = useState(15);
|
|
@@ -372,12 +390,12 @@ export default function InstructorSkillsPage() {
|
|
|
372
390
|
url: `/lms/instructor-skills/${skillToDelete.id}`,
|
|
373
391
|
method: 'DELETE',
|
|
374
392
|
});
|
|
375
|
-
toast.success('
|
|
393
|
+
toast.success(t('messages.deleteSuccess'));
|
|
376
394
|
setDeleteDialogOpen(false);
|
|
377
395
|
setSkillToDelete(null);
|
|
378
396
|
await refetchList();
|
|
379
397
|
} catch {
|
|
380
|
-
toast.error('
|
|
398
|
+
toast.error(t('messages.deleteError'));
|
|
381
399
|
} finally {
|
|
382
400
|
setIsDeleting(false);
|
|
383
401
|
}
|
|
@@ -387,15 +405,15 @@ export default function InstructorSkillsPage() {
|
|
|
387
405
|
<Page>
|
|
388
406
|
<PageHeader
|
|
389
407
|
breadcrumbs={[
|
|
390
|
-
{ label: '
|
|
391
|
-
{ label: '
|
|
392
|
-
{ label: '
|
|
408
|
+
{ label: t('breadcrumbs.home'), href: '/' },
|
|
409
|
+
{ label: t('breadcrumbs.lms'), href: '/lms' },
|
|
410
|
+
{ label: t('breadcrumbs.current') },
|
|
393
411
|
]}
|
|
394
|
-
title=
|
|
395
|
-
description=
|
|
412
|
+
title={t('title')}
|
|
413
|
+
description={t('description')}
|
|
396
414
|
actions={[
|
|
397
415
|
{
|
|
398
|
-
label: '
|
|
416
|
+
label: t('actions.create'),
|
|
399
417
|
onClick: openCreateSheet,
|
|
400
418
|
icon: <Plus className="h-4 w-4" />,
|
|
401
419
|
},
|
|
@@ -406,7 +424,7 @@ export default function InstructorSkillsPage() {
|
|
|
406
424
|
searchQuery={searchInput}
|
|
407
425
|
onSearchChange={(value) => setSearchInput(value)}
|
|
408
426
|
onSearch={() => setPage(1)}
|
|
409
|
-
placeholder=
|
|
427
|
+
placeholder={t('filters.searchPlaceholder')}
|
|
410
428
|
/>
|
|
411
429
|
|
|
412
430
|
{isLoading ? (
|
|
@@ -418,9 +436,9 @@ export default function InstructorSkillsPage() {
|
|
|
418
436
|
) : paginate.data.length === 0 ? (
|
|
419
437
|
<EmptyState
|
|
420
438
|
icon={<Sparkles className="h-12 w-12" />}
|
|
421
|
-
title=
|
|
422
|
-
description=
|
|
423
|
-
actionLabel=
|
|
439
|
+
title={t('empty.title')}
|
|
440
|
+
description={t('empty.description')}
|
|
441
|
+
actionLabel={t('actions.create')}
|
|
424
442
|
actionIcon={<Plus className="mr-2 h-4 w-4" />}
|
|
425
443
|
onAction={openCreateSheet}
|
|
426
444
|
/>
|
|
@@ -429,10 +447,10 @@ export default function InstructorSkillsPage() {
|
|
|
429
447
|
<Table>
|
|
430
448
|
<TableHeader>
|
|
431
449
|
<TableRow>
|
|
432
|
-
<TableHead>
|
|
433
|
-
<TableHead>
|
|
434
|
-
<TableHead>
|
|
435
|
-
<TableHead>
|
|
450
|
+
<TableHead>{t('table.slug')}</TableHead>
|
|
451
|
+
<TableHead>{t('table.namePt')}</TableHead>
|
|
452
|
+
<TableHead>{t('table.nameEn')}</TableHead>
|
|
453
|
+
<TableHead>{t('table.status')}</TableHead>
|
|
436
454
|
<TableHead className="w-10" />
|
|
437
455
|
</TableRow>
|
|
438
456
|
</TableHeader>
|
|
@@ -464,7 +482,9 @@ export default function InstructorSkillsPage() {
|
|
|
464
482
|
: 'border-gray-500/20 bg-gray-500/10 text-gray-600'
|
|
465
483
|
)}
|
|
466
484
|
>
|
|
467
|
-
{skill.status === 'active'
|
|
485
|
+
{skill.status === 'active'
|
|
486
|
+
? t('status.active')
|
|
487
|
+
: t('status.inactive')}
|
|
468
488
|
</Badge>
|
|
469
489
|
</TableCell>
|
|
470
490
|
<TableCell>
|
|
@@ -477,7 +497,7 @@ export default function InstructorSkillsPage() {
|
|
|
477
497
|
<DropdownMenuContent align="end">
|
|
478
498
|
<DropdownMenuItem onClick={() => openEditSheet(skill)}>
|
|
479
499
|
<Pencil className="mr-2 h-4 w-4" />
|
|
480
|
-
|
|
500
|
+
{t('actions.edit')}
|
|
481
501
|
</DropdownMenuItem>
|
|
482
502
|
<DropdownMenuSeparator />
|
|
483
503
|
<DropdownMenuItem
|
|
@@ -488,7 +508,7 @@ export default function InstructorSkillsPage() {
|
|
|
488
508
|
}}
|
|
489
509
|
>
|
|
490
510
|
<Trash2 className="mr-2 h-4 w-4" />
|
|
491
|
-
|
|
511
|
+
{t('actions.delete')}
|
|
492
512
|
</DropdownMenuItem>
|
|
493
513
|
</DropdownMenuContent>
|
|
494
514
|
</DropdownMenu>
|
|
@@ -521,23 +541,23 @@ export default function InstructorSkillsPage() {
|
|
|
521
541
|
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
|
522
542
|
<AlertDialogContent>
|
|
523
543
|
<AlertDialogHeader>
|
|
524
|
-
<AlertDialogTitle>
|
|
544
|
+
<AlertDialogTitle>{t('deleteDialog.title')}</AlertDialogTitle>
|
|
525
545
|
<AlertDialogDescription>
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
546
|
+
{t('deleteDialog.description', {
|
|
547
|
+
slug: skillToDelete?.slug ?? '',
|
|
548
|
+
})}
|
|
529
549
|
</AlertDialogDescription>
|
|
530
550
|
</AlertDialogHeader>
|
|
531
551
|
<div className="flex justify-end gap-2">
|
|
532
552
|
<AlertDialogCancel disabled={isDeleting}>
|
|
533
|
-
|
|
553
|
+
{t('actions.cancel')}
|
|
534
554
|
</AlertDialogCancel>
|
|
535
555
|
<AlertDialogAction
|
|
536
556
|
onClick={handleDeleteConfirm}
|
|
537
557
|
disabled={isDeleting}
|
|
538
558
|
className="bg-red-600 hover:bg-red-700"
|
|
539
559
|
>
|
|
540
|
-
{isDeleting ? '
|
|
560
|
+
{isDeleting ? t('actions.deleting') : t('actions.delete')}
|
|
541
561
|
</AlertDialogAction>
|
|
542
562
|
</div>
|
|
543
563
|
</AlertDialogContent>
|