@hed-hog/lms 0.0.331 → 0.0.347

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 (135) hide show
  1. package/dist/class-group/class-group.controller.d.ts +8 -8
  2. package/dist/class-group/class-group.service.d.ts +8 -8
  3. package/dist/course/course.controller.d.ts +6 -1
  4. package/dist/course/course.controller.d.ts.map +1 -1
  5. package/dist/course/course.controller.js +19 -2
  6. package/dist/course/course.controller.js.map +1 -1
  7. package/dist/course/course.service.d.ts +6 -0
  8. package/dist/course/course.service.d.ts.map +1 -1
  9. package/dist/course/course.service.js +63 -28
  10. package/dist/course/course.service.js.map +1 -1
  11. package/dist/course/dto/create-course.dto.d.ts +1 -0
  12. package/dist/course/dto/create-course.dto.d.ts.map +1 -1
  13. package/dist/course/dto/create-course.dto.js +5 -0
  14. package/dist/course/dto/create-course.dto.js.map +1 -1
  15. package/dist/enterprise/enterprise.controller.d.ts +84 -12
  16. package/dist/enterprise/enterprise.controller.d.ts.map +1 -1
  17. package/dist/enterprise/enterprise.controller.js +10 -0
  18. package/dist/enterprise/enterprise.controller.js.map +1 -1
  19. package/dist/enterprise/enterprise.service.d.ts +90 -12
  20. package/dist/enterprise/enterprise.service.d.ts.map +1 -1
  21. package/dist/enterprise/enterprise.service.js +413 -40
  22. package/dist/enterprise/enterprise.service.js.map +1 -1
  23. package/dist/enterprise/training/training-admin.controller.d.ts +9 -6
  24. package/dist/enterprise/training/training-admin.controller.d.ts.map +1 -1
  25. package/dist/enterprise/training/training-admin.controller.js +10 -6
  26. package/dist/enterprise/training/training-admin.controller.js.map +1 -1
  27. package/dist/enterprise/training/training-admin.service.d.ts +11 -5
  28. package/dist/enterprise/training/training-admin.service.d.ts.map +1 -1
  29. package/dist/enterprise/training/training-admin.service.js +108 -52
  30. package/dist/enterprise/training/training-admin.service.js.map +1 -1
  31. package/dist/enterprise/training/training-viewer.controller.d.ts +3 -0
  32. package/dist/enterprise/training/training-viewer.controller.d.ts.map +1 -1
  33. package/dist/evaluation/evaluation.controller.d.ts +2 -2
  34. package/dist/evaluation/evaluation.service.d.ts +2 -2
  35. package/dist/instructor/dto/create-instructor-skill.dto.d.ts +0 -4
  36. package/dist/instructor/dto/create-instructor-skill.dto.d.ts.map +1 -1
  37. package/dist/instructor/dto/create-instructor-skill.dto.js +0 -21
  38. package/dist/instructor/dto/create-instructor-skill.dto.js.map +1 -1
  39. package/dist/instructor/dto/update-instructor-skill.dto.d.ts +0 -4
  40. package/dist/instructor/dto/update-instructor-skill.dto.d.ts.map +1 -1
  41. package/dist/instructor/dto/update-instructor-skill.dto.js +0 -22
  42. package/dist/instructor/dto/update-instructor-skill.dto.js.map +1 -1
  43. package/dist/instructor/instructor-skill.controller.d.ts +4 -4
  44. package/dist/instructor/instructor-skill.service.d.ts +4 -7
  45. package/dist/instructor/instructor-skill.service.d.ts.map +1 -1
  46. package/dist/instructor/instructor-skill.service.js +2 -89
  47. package/dist/instructor/instructor-skill.service.js.map +1 -1
  48. package/dist/instructor/instructor.controller.d.ts +21 -0
  49. package/dist/instructor/instructor.controller.d.ts.map +1 -1
  50. package/dist/instructor/instructor.controller.js +19 -0
  51. package/dist/instructor/instructor.controller.js.map +1 -1
  52. package/dist/instructor/instructor.service.d.ts +27 -0
  53. package/dist/instructor/instructor.service.d.ts.map +1 -1
  54. package/dist/instructor/instructor.service.js +79 -25
  55. package/dist/instructor/instructor.service.js.map +1 -1
  56. package/dist/lms.module.d.ts.map +1 -1
  57. package/dist/lms.module.js.map +1 -1
  58. package/dist/training/dto/create-training.dto.d.ts +1 -0
  59. package/dist/training/dto/create-training.dto.d.ts.map +1 -1
  60. package/dist/training/dto/create-training.dto.js +5 -0
  61. package/dist/training/dto/create-training.dto.js.map +1 -1
  62. package/dist/training/training.controller.d.ts +4 -0
  63. package/dist/training/training.controller.d.ts.map +1 -1
  64. package/dist/training/training.service.d.ts +8 -0
  65. package/dist/training/training.service.d.ts.map +1 -1
  66. package/dist/training/training.service.js +71 -6
  67. package/dist/training/training.service.js.map +1 -1
  68. package/hedhog/data/route.yaml +23 -1
  69. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +80 -33
  70. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +3 -3
  71. package/hedhog/frontend/app/_components/create-lms-instructor-sheet.tsx.ejs +591 -0
  72. package/hedhog/frontend/app/certificates/issued/page.tsx.ejs +6 -1
  73. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +39 -7
  74. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +1 -3
  75. package/hedhog/frontend/app/classes/page.tsx.ejs +34 -7
  76. package/hedhog/frontend/app/courses/[id]/_components/CourseClassificationCard.tsx.ejs +3 -33
  77. package/hedhog/frontend/app/courses/[id]/_components/CourseContentCard.tsx.ejs +9 -9
  78. package/hedhog/frontend/app/courses/[id]/_components/CourseMainInfoCard.tsx.ejs +109 -0
  79. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +40 -13
  80. package/hedhog/frontend/app/courses/[id]/_components/CourseRelationsCard.tsx.ejs +76 -81
  81. package/hedhog/frontend/app/courses/[id]/_components/CourseSummaryCard.tsx.ejs +60 -0
  82. package/hedhog/frontend/app/courses/[id]/structure/_components/course-scheduled-classes-tab.tsx.ejs +406 -0
  83. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree.tsx.ejs +134 -0
  84. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-course.tsx.ejs +113 -0
  85. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-lesson.tsx.ejs +314 -0
  86. package/hedhog/frontend/app/courses/[id]/structure/_components/detail-session.tsx.ejs +174 -0
  87. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-course.tsx.ejs +243 -34
  88. package/hedhog/frontend/app/courses/[id]/structure/_components/mock-data.ts.ejs +185 -0
  89. package/hedhog/frontend/app/courses/page.tsx.ejs +6 -1
  90. package/hedhog/frontend/app/enterprise/_components/enterprise-activity-timeline.tsx.ejs +87 -0
  91. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +4 -0
  92. package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +31 -5
  93. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +79 -20
  94. package/hedhog/frontend/app/enterprise/_components/enterprise-company-identity-card.tsx.ejs +11 -2
  95. package/hedhog/frontend/app/enterprise/_components/enterprise-course-edit-sheet.tsx.ejs +201 -0
  96. package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +55 -24
  97. package/hedhog/frontend/app/enterprise/_components/enterprise-detail-sheet.tsx.ejs +430 -296
  98. package/hedhog/frontend/app/enterprise/_components/enterprise-mocks.ts.ejs +277 -0
  99. package/hedhog/frontend/app/enterprise/_components/enterprise-overview-analytics.tsx.ejs +205 -0
  100. package/hedhog/frontend/app/enterprise/_components/enterprise-person-edit-sheet.tsx.ejs +97 -0
  101. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +82 -57
  102. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +4 -0
  103. package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +60 -22
  104. package/hedhog/frontend/app/enterprise/_components/enterprise-types.ts.ejs +54 -0
  105. package/hedhog/frontend/app/enterprise/_components/enterprise-user-create-sheet.tsx.ejs +211 -0
  106. package/hedhog/frontend/app/enterprise/page.tsx.ejs +39 -7
  107. package/hedhog/frontend/app/exams/[id]/questions/page.tsx.ejs +31 -19
  108. package/hedhog/frontend/app/exams/page.tsx.ejs +6 -1
  109. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +51 -104
  110. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +625 -366
  111. package/hedhog/frontend/app/instructors/page.tsx.ejs +6 -1
  112. package/hedhog/frontend/app/paths/page.tsx.ejs +76 -8
  113. package/hedhog/frontend/app/reports/evaluations/page.tsx.ejs +6 -1
  114. package/hedhog/frontend/app/training/page.tsx.ejs +78 -9
  115. package/hedhog/frontend/messages/en.json +101 -10
  116. package/hedhog/frontend/messages/pt.json +115 -11
  117. package/hedhog/table/enterprise_student_license_event.yaml +30 -0
  118. package/hedhog/table/instructor_skill.yaml +0 -11
  119. package/hedhog/table/learning_path.yaml +4 -0
  120. package/package.json +6 -6
  121. package/src/course/course.controller.ts +18 -0
  122. package/src/course/course.service.ts +85 -26
  123. package/src/course/dto/create-course.dto.ts +4 -0
  124. package/src/enterprise/enterprise.controller.ts +5 -0
  125. package/src/enterprise/enterprise.service.ts +507 -29
  126. package/src/enterprise/training/training-admin.controller.ts +4 -0
  127. package/src/enterprise/training/training-admin.service.ts +115 -51
  128. package/src/instructor/dto/create-instructor-skill.dto.ts +0 -17
  129. package/src/instructor/dto/update-instructor-skill.dto.ts +0 -18
  130. package/src/instructor/instructor-skill.service.ts +2 -97
  131. package/src/instructor/instructor.controller.ts +16 -0
  132. package/src/instructor/instructor.service.ts +87 -10
  133. package/src/lms.module.ts +1 -0
  134. package/src/training/dto/create-training.dto.ts +4 -0
  135. package/src/training/training.service.ts +104 -5
@@ -8,6 +8,7 @@ import {
8
8
  SearchBar,
9
9
  type SearchBarControl,
10
10
  } from '@/components/entity-list';
11
+ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
11
12
  import { Badge } from '@/components/ui/badge';
12
13
  import { Button } from '@/components/ui/button';
13
14
  import { Card, CardContent } from '@/components/ui/card';
@@ -246,6 +247,21 @@ export default function EnterprisePage() {
246
247
  const accounts = enterpriseList?.data ?? [];
247
248
  const totalItems = enterpriseList?.total ?? 0;
248
249
 
250
+ function getPersonAvatarUrl(avatarId?: number | null) {
251
+ return typeof avatarId === 'number' && avatarId > 0
252
+ ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${avatarId}`
253
+ : undefined;
254
+ }
255
+
256
+ function getInitials(name: string) {
257
+ const parts = name.trim().split(/\s+/).filter(Boolean);
258
+ if (parts.length === 0) return '?';
259
+ if (parts.length === 1) return (parts[0] ?? '').slice(0, 2).toUpperCase();
260
+ return (
261
+ ((parts[0] ?? '')[0] ?? '') + ((parts[1] ?? '')[0] ?? '')
262
+ ).toUpperCase();
263
+ }
264
+
249
265
  function handleSearch(v: string) {
250
266
  setSearch(v);
251
267
  setPage(1);
@@ -386,9 +402,7 @@ export default function EnterprisePage() {
386
402
  <TableHead>{t('table.name')}</TableHead>
387
403
  <TableHead>{t('table.status')}</TableHead>
388
404
  <TableHead>
389
- {t.has('table.crmAccount')
390
- ? t('table.crmAccount')
391
- : 'CRM Account'}
405
+ {t.has('table.client') ? t('table.client') : 'Cliente'}
392
406
  </TableHead>
393
407
  <TableHead className="text-center">
394
408
  {t.has('table.classes') ? t('table.classes') : 'Classes'}
@@ -410,7 +424,7 @@ export default function EnterprisePage() {
410
424
  <TableRow
411
425
  key={account.id}
412
426
  className="cursor-pointer"
413
- onClick={() => handleViewDetails(account)}
427
+ onDoubleClick={() => handleViewDetails(account)}
414
428
  >
415
429
  <TableCell>
416
430
  <p className="font-medium">{account.name}</p>
@@ -437,7 +451,25 @@ export default function EnterprisePage() {
437
451
  </Badge>
438
452
  </TableCell>
439
453
  <TableCell className="text-sm text-muted-foreground">
440
- {account.crmAccountName ?? '—'}
454
+ {account.crmAccountName ? (
455
+ <div className="flex items-center gap-2">
456
+ <Avatar className="h-7 w-7">
457
+ <AvatarImage
458
+ src={getPersonAvatarUrl(
459
+ account.crmAccount?.avatarId ?? null
460
+ )}
461
+ />
462
+ <AvatarFallback className="text-[10px] font-semibold">
463
+ {getInitials(account.crmAccountName)}
464
+ </AvatarFallback>
465
+ </Avatar>
466
+ <span className="truncate text-sm text-foreground">
467
+ {account.crmAccountName}
468
+ </span>
469
+ </div>
470
+ ) : (
471
+ '—'
472
+ )}
441
473
  </TableCell>
442
474
  <TableCell className="text-center text-sm tabular-nums">
443
475
  {account.classesCount}
@@ -490,7 +522,7 @@ export default function EnterprisePage() {
490
522
  <Card
491
523
  key={account.id}
492
524
  className="group cursor-pointer overflow-hidden border-border/70 py-0 transition-colors hover:border-border hover:shadow-md"
493
- onClick={() => handleViewDetails(account)}
525
+ onDoubleClick={() => handleViewDetails(account)}
494
526
  >
495
527
  <CardContent className="flex flex-col gap-3 p-4">
496
528
  {/* Header */}
@@ -616,7 +648,7 @@ export default function EnterprisePage() {
616
648
  pageSize={PAGE_SIZE}
617
649
  totalItems={totalItems}
618
650
  onPageChange={setPage}
619
- onPageSizeChange={function (pageSize: number): void {
651
+ onPageSizeChange={function (): void {
620
652
  throw new Error('Function not implemented.');
621
653
  }}
622
654
  />
@@ -104,6 +104,7 @@ import {
104
104
  import { useTranslations } from 'next-intl';
105
105
  import { useParams, useRouter } from 'next/navigation';
106
106
  import { useEffect, useMemo, useState } from 'react';
107
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
107
108
  import { useForm } from 'react-hook-form';
108
109
  import { toast } from 'sonner';
109
110
  import { z } from 'zod';
@@ -588,6 +589,7 @@ export default function QuestoesPage() {
588
589
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
589
590
  const [questaoToDelete, setQuestaoToDelete] = useState<Questao | null>(null);
590
591
  const [hasChanges, setHasChanges] = useState(false);
592
+ const [saving, setSaving] = useState(false);
591
593
  const [buscaInput, setBuscaInput] = useState('');
592
594
  const [debouncedBuscaInput, setDebouncedBuscaInput] = useState('');
593
595
  const [filtroTipoInput, setFiltroTipoInput] = useState<
@@ -599,7 +601,11 @@ export default function QuestoesPage() {
599
601
  allowedValues: ['cards', 'list'],
600
602
  });
601
603
  const [currentPage, setCurrentPage] = useState(1);
602
- const [pageSize, setPageSize] = useState(12);
604
+ const [pageSize, setPageSize] = usePersistedPageSize({
605
+ storageKey: 'pagination:global:pageSize',
606
+ defaultValue: 12,
607
+ allowedValues: PAGE_SIZE_OPTIONS,
608
+ });
603
609
 
604
610
  const questaoSchema = z.object({
605
611
  enunciado: z.string().min(5, t('sheet.validation.statementMin')),
@@ -988,24 +994,29 @@ export default function QuestoesPage() {
988
994
  }),
989
995
  };
990
996
 
991
- if (editingQuestao) {
992
- await request({
993
- url: `/lms/exams/${examId}/questions/${editingQuestao.id}`,
994
- method: 'PATCH',
995
- data: payload,
996
- });
997
- toast.success(t('toasts.questionUpdated'));
998
- } else {
999
- await request({
1000
- url: `/lms/exams/${examId}/questions`,
1001
- method: 'POST',
1002
- data: payload,
1003
- });
1004
- toast.success(t('toasts.questionCreated'));
1005
- }
997
+ setSaving(true);
998
+ try {
999
+ if (editingQuestao) {
1000
+ await request({
1001
+ url: `/lms/exams/${examId}/questions/${editingQuestao.id}`,
1002
+ method: 'PATCH',
1003
+ data: payload,
1004
+ });
1005
+ toast.success(t('toasts.questionUpdated'));
1006
+ } else {
1007
+ await request({
1008
+ url: `/lms/exams/${examId}/questions`,
1009
+ method: 'POST',
1010
+ data: payload,
1011
+ });
1012
+ toast.success(t('toasts.questionCreated'));
1013
+ }
1006
1014
 
1007
- await refetchQuestions();
1008
- setSheetOpen(false);
1015
+ await refetchQuestions();
1016
+ setSheetOpen(false);
1017
+ } finally {
1018
+ setSaving(false);
1019
+ }
1009
1020
  }
1010
1021
 
1011
1022
  async function confirmDelete() {
@@ -1796,7 +1807,8 @@ export default function QuestoesPage() {
1796
1807
  )}
1797
1808
 
1798
1809
  <SheetFooter className="mt-auto gap-2 p-0 pt-4">
1799
- <Button type="submit" className="w-full">
1810
+ <Button type="submit" className="w-full" disabled={saving}>
1811
+ {saving && <Loader2 className="mr-2 size-4 animate-spin" />}
1800
1812
  {editingQuestao
1801
1813
  ? t('sheet.actions.update')
1802
1814
  : t('sheet.actions.create')}
@@ -83,6 +83,7 @@ import {
83
83
  import { useTranslations } from 'next-intl';
84
84
  import { useRouter } from 'next/navigation';
85
85
  import { useEffect, useMemo, useRef, useState } from 'react';
86
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
86
87
  import { Controller, useForm, useWatch } from 'react-hook-form';
87
88
  import { toast } from 'sonner';
88
89
  import { z } from 'zod';
@@ -261,7 +262,11 @@ export default function ExamesPage() {
261
262
 
262
263
  // Pagination
263
264
  const [currentPage, setCurrentPage] = useState(1);
264
- const [pageSize, setPageSize] = useState(12);
265
+ const [pageSize, setPageSize] = usePersistedPageSize({
266
+ storageKey: 'pagination:global:pageSize',
267
+ defaultValue: 12,
268
+ allowedValues: PAGE_SIZES,
269
+ });
265
270
 
266
271
  // Double-click tracking
267
272
  const clickTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(
@@ -56,6 +56,7 @@ import {
56
56
  TableHeader,
57
57
  TableRow,
58
58
  } from '@/components/ui/table';
59
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
59
60
  import { cn } from '@/lib/utils';
60
61
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
61
62
  import { zodResolver } from '@hookform/resolvers/zod';
@@ -71,8 +72,6 @@ import * as z from 'zod';
71
72
  type InstructorSkill = {
72
73
  id: number;
73
74
  slug: string;
74
- namePt: string;
75
- nameEn?: string | null;
76
75
  status: 'active' | 'inactive';
77
76
  };
78
77
 
@@ -93,16 +92,7 @@ const createSkillSchema = (t: ReturnType<typeof useTranslations>) =>
93
92
  .min(1, t('form.validation.required'))
94
93
  .max(100, t('form.validation.max100'))
95
94
  .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'),
95
+ status: z.enum(['active', 'inactive']),
106
96
  });
107
97
 
108
98
  type SkillFormValues = z.infer<ReturnType<typeof createSkillSchema>>;
@@ -130,8 +120,6 @@ function SkillFormSheet({
130
120
  resolver: zodResolver(createSkillSchema(t)),
131
121
  defaultValues: {
132
122
  slug: '',
133
- namePt: '',
134
- nameEn: '',
135
123
  status: 'active',
136
124
  },
137
125
  });
@@ -142,15 +130,11 @@ function SkillFormSheet({
142
130
  if (skillToEdit) {
143
131
  form.reset({
144
132
  slug: skillToEdit.slug,
145
- namePt: skillToEdit.namePt,
146
- nameEn: skillToEdit.nameEn ?? '',
147
133
  status: skillToEdit.status,
148
134
  });
149
135
  } else {
150
136
  form.reset({
151
137
  slug: '',
152
- namePt: '',
153
- nameEn: '',
154
138
  status: 'active',
155
139
  });
156
140
  }
@@ -162,8 +146,6 @@ function SkillFormSheet({
162
146
  setIsSaving(true);
163
147
  const payload = {
164
148
  slug: values.slug,
165
- namePt: values.namePt,
166
- nameEn: values.nameEn || undefined,
167
149
  status: values.status,
168
150
  };
169
151
 
@@ -206,81 +188,52 @@ function SkillFormSheet({
206
188
  onSubmit={form.handleSubmit(onSubmit)}
207
189
  className="flex flex-col gap-4 px-4 py-4"
208
190
  >
209
- <FormField
210
- control={form.control}
211
- name="slug"
212
- render={({ field }) => (
213
- <FormItem>
214
- <FormLabel>{t('form.slug')}</FormLabel>
215
- <FormControl>
216
- <Input placeholder={t('form.slugPlaceholder')} {...field} />
217
- </FormControl>
218
- <FormMessage />
219
- </FormItem>
220
- )}
221
- />
222
-
223
- <FormField
224
- control={form.control}
225
- name="namePt"
226
- render={({ field }) => (
227
- <FormItem>
228
- <FormLabel>{t('form.namePt')}</FormLabel>
229
- <FormControl>
230
- <Input
231
- placeholder={t('form.namePtPlaceholder')}
232
- {...field}
233
- />
234
- </FormControl>
235
- <FormMessage />
236
- </FormItem>
237
- )}
238
- />
239
-
240
- <FormField
241
- control={form.control}
242
- name="nameEn"
243
- render={({ field }) => (
244
- <FormItem>
245
- <FormLabel>{t('form.nameEn')}</FormLabel>
246
- <FormControl>
247
- <Input
248
- placeholder={t('form.nameEnPlaceholder')}
249
- {...field}
250
- />
251
- </FormControl>
252
- <FormMessage />
253
- </FormItem>
254
- )}
255
- />
256
-
257
- <FormField
258
- control={form.control}
259
- name="status"
260
- render={({ field }) => (
261
- <FormItem>
262
- <FormLabel>{t('form.status')}</FormLabel>
263
- <Select onValueChange={field.onChange} value={field.value}>
191
+ <div className="grid grid-cols-2 gap-4">
192
+ <FormField
193
+ control={form.control}
194
+ name="slug"
195
+ render={({ field }) => (
196
+ <FormItem>
197
+ <FormLabel>{t('form.slug')}</FormLabel>
264
198
  <FormControl>
265
- <SelectTrigger className="w-full">
266
- <SelectValue
267
- placeholder={t('form.statusPlaceholder')}
268
- />
269
- </SelectTrigger>
199
+ <Input
200
+ placeholder={t('form.slugPlaceholder')}
201
+ {...field}
202
+ />
270
203
  </FormControl>
271
- <SelectContent>
272
- <SelectItem value="active">
273
- {t('status.active')}
274
- </SelectItem>
275
- <SelectItem value="inactive">
276
- {t('status.inactive')}
277
- </SelectItem>
278
- </SelectContent>
279
- </Select>
280
- <FormMessage />
281
- </FormItem>
282
- )}
283
- />
204
+ <FormMessage />
205
+ </FormItem>
206
+ )}
207
+ />
208
+
209
+ <FormField
210
+ control={form.control}
211
+ name="status"
212
+ render={({ field }) => (
213
+ <FormItem>
214
+ <FormLabel>{t('form.status')}</FormLabel>
215
+ <Select onValueChange={field.onChange} value={field.value}>
216
+ <FormControl>
217
+ <SelectTrigger className="w-full">
218
+ <SelectValue
219
+ placeholder={t('form.statusPlaceholder')}
220
+ />
221
+ </SelectTrigger>
222
+ </FormControl>
223
+ <SelectContent>
224
+ <SelectItem value="active">
225
+ {t('status.active')}
226
+ </SelectItem>
227
+ <SelectItem value="inactive">
228
+ {t('status.inactive')}
229
+ </SelectItem>
230
+ </SelectContent>
231
+ </Select>
232
+ <FormMessage />
233
+ </FormItem>
234
+ )}
235
+ />
236
+ </div>
284
237
 
285
238
  <div className="flex justify-end gap-2 pt-2">
286
239
  <Button
@@ -309,7 +262,11 @@ export default function InstructorSkillsPage() {
309
262
  const t = useTranslations('lms.InstructorSkillsPage');
310
263
 
311
264
  const [page, setPage] = useState(1);
312
- const [pageSize, setPageSize] = useState(15);
265
+ const [pageSize, setPageSize] = usePersistedPageSize({
266
+ storageKey: 'pagination:global:pageSize',
267
+ defaultValue: 6,
268
+ allowedValues: [6, 12, 24, 48],
269
+ });
313
270
  const [searchInput, setSearchInput] = useState('');
314
271
  const [debouncedSearch, setDebouncedSearch] = useState('');
315
272
  const [sheetOpen, setSheetOpen] = useState(false);
@@ -448,8 +405,6 @@ export default function InstructorSkillsPage() {
448
405
  <TableHeader>
449
406
  <TableRow>
450
407
  <TableHead>{t('table.slug')}</TableHead>
451
- <TableHead>{t('table.namePt')}</TableHead>
452
- <TableHead>{t('table.nameEn')}</TableHead>
453
408
  <TableHead>{t('table.status')}</TableHead>
454
409
  <TableHead className="w-10" />
455
410
  </TableRow>
@@ -464,14 +419,6 @@ export default function InstructorSkillsPage() {
464
419
  <TableCell>
465
420
  <span className="font-mono text-sm">{skill.slug}</span>
466
421
  </TableCell>
467
- <TableCell>
468
- <span className="font-medium">{skill.namePt}</span>
469
- </TableCell>
470
- <TableCell>
471
- <span className="text-sm text-muted-foreground">
472
- {skill.nameEn ?? '—'}
473
- </span>
474
- </TableCell>
475
422
  <TableCell>
476
423
  <Badge
477
424
  variant="outline"