@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.
Files changed (60) hide show
  1. package/hedhog/frontend/app/_components/class-form-sheet.tsx.ejs +18 -8
  2. package/hedhog/frontend/app/_components/course-form-sheet.tsx.ejs +10 -8
  3. package/hedhog/frontend/app/_components/create-lms-person-sheet.tsx.ejs +5 -9
  4. package/hedhog/frontend/app/_components/create-lms-student-person-sheet.tsx.ejs +5 -9
  5. package/hedhog/frontend/app/certificates/models/LeftPanel.tsx.ejs +15 -14
  6. package/hedhog/frontend/app/certificates/models/RightPanel.tsx.ejs +66 -29
  7. package/hedhog/frontend/app/certificates/models/TemplateEditorPage.tsx.ejs +4 -2
  8. package/hedhog/frontend/app/certificates/models/TopBar.tsx.ejs +44 -34
  9. package/hedhog/frontend/app/certificates/models/page.tsx.ejs +1 -1
  10. package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +27 -27
  11. package/hedhog/frontend/app/classes/page.tsx.ejs +23 -15
  12. package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +2 -2
  13. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +8 -6
  14. package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +5 -3
  15. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +1 -1
  16. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +9 -7
  17. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-skeleton.tsx.ejs +3 -1
  18. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +4 -2
  19. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +24 -23
  20. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +228 -152
  21. package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +21 -19
  22. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +78 -36
  23. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +18 -16
  24. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +13 -11
  25. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +5 -3
  26. package/hedhog/frontend/app/courses/[id]/structure/_components/use-course-structure-shortcuts.ts.ejs +14 -9
  27. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +42 -25
  28. package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +37 -41
  29. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +3 -1
  30. package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +10 -8
  31. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +22 -20
  32. package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +3 -3
  33. package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +21 -19
  34. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +34 -36
  35. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +3 -1
  36. package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +7 -5
  37. package/hedhog/frontend/app/enterprise/page.tsx.ejs +106 -54
  38. package/hedhog/frontend/app/evaluations/_components/evaluation-topic-form-sheet.tsx.ejs +1 -1
  39. package/hedhog/frontend/app/exams/page.tsx.ejs +6 -2
  40. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +79 -59
  41. package/hedhog/frontend/app/instructors/_components/instructor-form-sheet.tsx.ejs +145 -119
  42. package/hedhog/frontend/app/instructors/page.tsx.ejs +75 -54
  43. package/hedhog/frontend/app/paths/page.tsx.ejs +11 -7
  44. package/hedhog/frontend/app/reports/courses/page.tsx.ejs +5 -5
  45. package/hedhog/frontend/app/reports/dashboard/page.tsx.ejs +8 -8
  46. package/hedhog/frontend/app/reports/page.tsx.ejs +7 -7
  47. package/hedhog/frontend/app/reports/students/page.tsx.ejs +6 -6
  48. package/hedhog/frontend/app/training/page.tsx.ejs +5 -5
  49. package/hedhog/frontend/messages/en.json +899 -45
  50. package/hedhog/frontend/messages/pt.json +894 -38
  51. package/hedhog/frontend/widgets/active-classes-kpi.tsx.ejs +1 -1
  52. package/hedhog/frontend/widgets/active-courses-kpi.tsx.ejs +1 -1
  53. package/hedhog/frontend/widgets/approval-rate-kpi.tsx.ejs +1 -1
  54. package/hedhog/frontend/widgets/class-calendar.tsx.ejs +2 -2
  55. package/hedhog/frontend/widgets/completion-rate-kpi.tsx.ejs +1 -1
  56. package/hedhog/frontend/widgets/issued-certificates-kpi.tsx.ejs +1 -1
  57. package/hedhog/frontend/widgets/total-students-kpi.tsx.ejs +1 -1
  58. package/hedhog/table/instructor_qualification.yaml +1 -1
  59. package/hedhog/table/instructor_skill.yaml +1 -1
  60. package/package.json +7 -7
@@ -22,6 +22,7 @@ import { formatDate } from '@/lib/format-date';
22
22
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
23
23
  import { CalendarDays, SquareArrowOutUpRight, Trash2 } from 'lucide-react';
24
24
  import { useRouter } from 'next/navigation';
25
+ import { useTranslations } from 'next-intl';
25
26
  import { useCallback, useState } from 'react';
26
27
  import { toast } from 'sonner';
27
28
  import { EnterpriseClassCreateSheet } from './enterprise-class-create-sheet';
@@ -93,6 +94,7 @@ export function ClassesTab({
93
94
  }) {
94
95
  const router = useRouter();
95
96
  const { request, currentLocaleCode, getSettingValue } = useApp();
97
+ const t = useTranslations('lms.EnterpriseDetailPage.relatedClasses');
96
98
 
97
99
  const [search, setSearch] = useState('');
98
100
  const [statusFilter, setStatusFilter] = useState<
@@ -144,11 +146,11 @@ export function ClassesTab({
144
146
  url: `/lms/enterprise/${enterpriseId}/classes/${classGroupId}`,
145
147
  method: 'DELETE',
146
148
  });
147
- toast.success('Class removed from enterprise.');
149
+ toast.success(t('messages.removeSuccess'));
148
150
  refetch();
149
151
  onMutate?.();
150
152
  } catch {
151
- toast.error('Failed to remove class.');
153
+ toast.error(t('messages.removeError'));
152
154
  }
153
155
  }
154
156
 
@@ -168,7 +170,7 @@ export function ClassesTab({
168
170
  refetch();
169
171
  onMutate?.();
170
172
  } catch {
171
- toast.error('Failed to add class.');
173
+ toast.error(t('messages.addError'));
172
174
  }
173
175
  }
174
176
 
@@ -180,7 +182,7 @@ export function ClassesTab({
180
182
  setSearch(v);
181
183
  setPage(1);
182
184
  }}
183
- searchPlaceholder="Search by code or course..."
185
+ searchPlaceholder={t('searchPlaceholder')}
184
186
  filters={
185
187
  <Select
186
188
  value={statusFilter}
@@ -190,19 +192,19 @@ export function ClassesTab({
190
192
  }}
191
193
  >
192
194
  <SelectTrigger className="w-44">
193
- <SelectValue placeholder="All statuses" />
195
+ <SelectValue placeholder={t('filters.allStatuses')} />
194
196
  </SelectTrigger>
195
197
  <SelectContent>
196
- <SelectItem value="all">All statuses</SelectItem>
197
- <SelectItem value="open">Open</SelectItem>
198
- <SelectItem value="ongoing">Ongoing</SelectItem>
199
- <SelectItem value="completed">Completed</SelectItem>
200
- <SelectItem value="cancelled">Cancelled</SelectItem>
198
+ <SelectItem value="all">{t('filters.allStatuses')}</SelectItem>
199
+ <SelectItem value="open">{t('status.open')}</SelectItem>
200
+ <SelectItem value="ongoing">{t('status.ongoing')}</SelectItem>
201
+ <SelectItem value="completed">{t('status.completed')}</SelectItem>
202
+ <SelectItem value="cancelled">{t('status.cancelled')}</SelectItem>
201
203
  </SelectContent>
202
204
  </Select>
203
205
  }
204
- pickerPlaceholder="Select a class..."
205
- pickerEntityLabel="class"
206
+ pickerPlaceholder={t('pickerPlaceholder')}
207
+ pickerEntityLabel={t('pickerEntityLabel')}
206
208
  pickerValue={pickerValue}
207
209
  onPickerChange={(value, option) => {
208
210
  setPickerValue(value);
@@ -236,9 +238,9 @@ export function ClassesTab({
236
238
  </div>
237
239
  )}
238
240
  onCreateNew={() => setCreateSheetOpen(true)}
239
- createLabel="Create new class"
241
+ createLabel={t('actions.create')}
240
242
  onAdd={handleAdd}
241
- addLabel="Add"
243
+ addLabel={t('actions.add')}
242
244
  addDisabled={!pickerClass}
243
245
  currentPage={page}
244
246
  pageSize={pageSize}
@@ -253,10 +255,10 @@ export function ClassesTab({
253
255
  <div className="flex flex-col items-center justify-center py-16 text-center">
254
256
  <CalendarDays className="mb-4 h-10 w-10 text-muted-foreground/30" />
255
257
  <p className="text-sm font-medium text-muted-foreground">
256
- No classes found.
258
+ {t('empty.title')}
257
259
  </p>
258
260
  <p className="mt-1 text-xs text-muted-foreground/60">
259
- Adjust the search or status filter to see results.
261
+ {t('empty.description')}
260
262
  </p>
261
263
  </div>
262
264
  ) : (
@@ -264,10 +266,10 @@ export function ClassesTab({
264
266
  <Table>
265
267
  <TableHeader>
266
268
  <TableRow>
267
- <TableHead>Class</TableHead>
268
- <TableHead>Status</TableHead>
269
- <TableHead>Period</TableHead>
270
- <TableHead className="text-center">Capacity</TableHead>
269
+ <TableHead>{t('table.class')}</TableHead>
270
+ <TableHead>{t('table.status')}</TableHead>
271
+ <TableHead>{t('table.period')}</TableHead>
272
+ <TableHead className="text-center">{t('table.capacity')}</TableHead>
271
273
  <TableHead className="w-10" />
272
274
  </TableRow>
273
275
  </TableHeader>
@@ -77,7 +77,7 @@ export function EnterpriseCourseCreateSheet({
77
77
  enterpriseId,
78
78
  onCreated,
79
79
  }: EnterpriseCourseCreateSheetProps) {
80
- const t = useTranslations('lms.CoursesPage');
80
+ const t = useTranslations('lms.EnterprisePage');
81
81
  const { request } = useApp();
82
82
  const [saving, setSaving] = useState(false);
83
83
 
@@ -159,11 +159,11 @@ export function EnterpriseCourseCreateSheet({
159
159
  data: { course_id: created.id },
160
160
  });
161
161
 
162
- toast.success(t('toasts.courseCreated'));
162
+ toast.success(t('form.toasts.courseCreated'));
163
163
  onCreated?.();
164
164
  onOpenChange(false);
165
165
  } catch {
166
- toast.error('Não foi possível criar o curso.');
166
+ toast.error(t('form.toasts.courseCreateError'));
167
167
  } finally {
168
168
  setSaving(false);
169
169
  }
@@ -22,6 +22,7 @@ import { formatDate } from '@/lib/format-date';
22
22
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
23
23
  import { BookOpen, SquareArrowOutUpRight, Trash2 } from 'lucide-react';
24
24
  import { useRouter } from 'next/navigation';
25
+ import { useTranslations } from 'next-intl';
25
26
  import { useCallback, useState } from 'react';
26
27
  import { toast } from 'sonner';
27
28
  import { EnterpriseCourseCreateSheet } from './enterprise-course-create-sheet';
@@ -89,6 +90,7 @@ export function CoursesTab({
89
90
  }) {
90
91
  const router = useRouter();
91
92
  const { request, getSettingValue, currentLocaleCode } = useApp();
93
+ const t = useTranslations('lms.EnterpriseDetailPage.relatedCourses');
92
94
 
93
95
  const [search, setSearch] = useState('');
94
96
  const [statusFilter, setStatusFilter] = useState<
@@ -142,11 +144,11 @@ export function CoursesTab({
142
144
  url: `/lms/enterprise/${enterpriseId}/courses/${courseId}`,
143
145
  method: 'DELETE',
144
146
  });
145
- toast.success('Course removed from enterprise.');
147
+ toast.success(t('messages.removeSuccess'));
146
148
  refetch();
147
149
  onMutate?.();
148
150
  } catch {
149
- toast.error('Failed to remove course.');
151
+ toast.error(t('messages.removeError'));
150
152
  }
151
153
  }
152
154
 
@@ -163,7 +165,7 @@ export function CoursesTab({
163
165
  refetch();
164
166
  onMutate?.();
165
167
  } catch {
166
- toast.error('Failed to add course.');
168
+ toast.error(t('messages.addError'));
167
169
  }
168
170
  }
169
171
 
@@ -175,7 +177,7 @@ export function CoursesTab({
175
177
  setSearch(v);
176
178
  setPage(1);
177
179
  }}
178
- searchPlaceholder="Search by course name..."
180
+ searchPlaceholder={t('searchPlaceholder')}
179
181
  filters={
180
182
  <Select
181
183
  value={statusFilter}
@@ -185,18 +187,18 @@ export function CoursesTab({
185
187
  }}
186
188
  >
187
189
  <SelectTrigger className="w-40">
188
- <SelectValue placeholder="All statuses" />
190
+ <SelectValue placeholder={t('filters.allStatuses')} />
189
191
  </SelectTrigger>
190
192
  <SelectContent>
191
- <SelectItem value="all">All statuses</SelectItem>
192
- <SelectItem value="published">Published</SelectItem>
193
- <SelectItem value="draft">Draft</SelectItem>
194
- <SelectItem value="archived">Archived</SelectItem>
193
+ <SelectItem value="all">{t('filters.allStatuses')}</SelectItem>
194
+ <SelectItem value="published">{t('status.published')}</SelectItem>
195
+ <SelectItem value="draft">{t('status.draft')}</SelectItem>
196
+ <SelectItem value="archived">{t('status.archived')}</SelectItem>
195
197
  </SelectContent>
196
198
  </Select>
197
199
  }
198
- pickerPlaceholder="Select a course..."
199
- pickerEntityLabel="course"
200
+ pickerPlaceholder={t('pickerPlaceholder')}
201
+ pickerEntityLabel={t('pickerEntityLabel')}
200
202
  pickerValue={pickerValue}
201
203
  onPickerChange={(value, option) => {
202
204
  setPickerValue(value);
@@ -228,9 +230,9 @@ export function CoursesTab({
228
230
  </div>
229
231
  )}
230
232
  onCreateNew={() => setCreateSheetOpen(true)}
231
- createLabel="Create new course"
233
+ createLabel={t('actions.create')}
232
234
  onAdd={handleAdd}
233
- addLabel="Add"
235
+ addLabel={t('actions.add')}
234
236
  addDisabled={!pickerCourse}
235
237
  currentPage={page}
236
238
  pageSize={pageSize}
@@ -245,10 +247,10 @@ export function CoursesTab({
245
247
  <div className="flex flex-col items-center justify-center py-16 text-center">
246
248
  <BookOpen className="mb-4 h-10 w-10 text-muted-foreground/30" />
247
249
  <p className="text-sm font-medium text-muted-foreground">
248
- No courses found.
250
+ {t('empty.title')}
249
251
  </p>
250
252
  <p className="mt-1 text-xs text-muted-foreground/60">
251
- Adjust the search or status filter to see results.
253
+ {t('empty.description')}
252
254
  </p>
253
255
  </div>
254
256
  ) : (
@@ -256,10 +258,10 @@ export function CoursesTab({
256
258
  <Table>
257
259
  <TableHeader>
258
260
  <TableRow>
259
- <TableHead>Course</TableHead>
260
- <TableHead>Modality</TableHead>
261
- <TableHead>Status</TableHead>
262
- <TableHead>Contracted</TableHead>
261
+ <TableHead>{t('table.course')}</TableHead>
262
+ <TableHead>{t('table.modality')}</TableHead>
263
+ <TableHead>{t('table.status')}</TableHead>
264
+ <TableHead>{t('table.contracted')}</TableHead>
263
265
  <TableHead className="w-10" />
264
266
  </TableRow>
265
267
  </TableHeader>
@@ -30,6 +30,7 @@ import { Switch } from '@/components/ui/switch';
30
30
  import { Textarea } from '@/components/ui/textarea';
31
31
  import { useApp } from '@hed-hog/next-app-provider';
32
32
  import { zodResolver } from '@hookform/resolvers/zod';
33
+ import { useTranslations } from 'next-intl';
33
34
  import { useEffect } from 'react';
34
35
  import { useForm } from 'react-hook-form';
35
36
  import { toast } from 'sonner';
@@ -86,6 +87,7 @@ export function EnterpriseSheet({
86
87
  }: EnterpriseSheetProps) {
87
88
  const isEditing = Boolean(editingAccount);
88
89
  const { request } = useApp();
90
+ const t = useTranslations('lms.EnterprisePage.sheet');
89
91
 
90
92
  const form = useForm<EnterpriseFormValues>({
91
93
  resolver: zodResolver(enterpriseSchema),
@@ -163,14 +165,12 @@ export function EnterpriseSheet({
163
165
 
164
166
  onSave?.(values, isEditing);
165
167
  toast.success(
166
- isEditing
167
- ? 'Account updated successfully.'
168
- : 'Account created successfully.'
168
+ isEditing ? t('messages.updateSuccess') : t('messages.createSuccess')
169
169
  );
170
170
  onOpenChange(false);
171
171
  } catch {
172
172
  toast.error(
173
- isEditing ? 'Failed to update account.' : 'Failed to create account.'
173
+ isEditing ? t('messages.updateError') : t('messages.createError')
174
174
  );
175
175
  }
176
176
  }
@@ -180,12 +180,10 @@ export function EnterpriseSheet({
180
180
  <SheetContent className="w-full overflow-y-auto sm:max-w-lg">
181
181
  <SheetHeader>
182
182
  <SheetTitle>
183
- {isEditing ? 'Edit enterprise account' : 'New enterprise account'}
183
+ {isEditing ? t('editTitle') : t('createTitle')}
184
184
  </SheetTitle>
185
185
  <SheetDescription>
186
- {isEditing
187
- ? 'Update the corporate account details below.'
188
- : 'Register a new corporate account in the LMS platform.'}
186
+ {isEditing ? t('editDescription') : t('createDescription')}
189
187
  </SheetDescription>
190
188
  </SheetHeader>
191
189
 
@@ -200,9 +198,9 @@ export function EnterpriseSheet({
200
198
  name="name"
201
199
  render={({ field }) => (
202
200
  <FormItem>
203
- <FormLabel>Name</FormLabel>
201
+ <FormLabel>{t('fields.name')}</FormLabel>
204
202
  <FormControl>
205
- <Input placeholder="e.g. Techcorp Brasil" {...field} />
203
+ <Input placeholder={t('fields.namePlaceholder')} {...field} />
206
204
  </FormControl>
207
205
  <FormMessage />
208
206
  </FormItem>
@@ -215,13 +213,12 @@ export function EnterpriseSheet({
215
213
  name="slug"
216
214
  render={({ field }) => (
217
215
  <FormItem>
218
- <FormLabel>Slug / Internal code</FormLabel>
216
+ <FormLabel>{t('fields.slug')}</FormLabel>
219
217
  <FormControl>
220
- <Input placeholder="techcorp-brasil" {...field} />
218
+ <Input placeholder={t('fields.slugPlaceholder')} {...field} />
221
219
  </FormControl>
222
220
  <FormDescription>
223
- Unique identifier used internally and in URLs. Auto-filled
224
- from the name when creating.
221
+ {t('fields.slugDescription')}
225
222
  </FormDescription>
226
223
  <FormMessage />
227
224
  </FormItem>
@@ -234,17 +231,17 @@ export function EnterpriseSheet({
234
231
  name="status"
235
232
  render={({ field }) => (
236
233
  <FormItem>
237
- <FormLabel>Status</FormLabel>
234
+ <FormLabel>{t('fields.status')}</FormLabel>
238
235
  <FormControl>
239
236
  <Select value={field.value} onValueChange={field.onChange}>
240
237
  <SelectTrigger className="w-full">
241
238
  <SelectValue />
242
239
  </SelectTrigger>
243
240
  <SelectContent>
244
- <SelectItem value="active">Active</SelectItem>
245
- <SelectItem value="trial">Trial</SelectItem>
246
- <SelectItem value="inactive">Inactive</SelectItem>
247
- <SelectItem value="suspended">Suspended</SelectItem>
241
+ <SelectItem value="active">{t('status.active')}</SelectItem>
242
+ <SelectItem value="trial">{t('status.trial')}</SelectItem>
243
+ <SelectItem value="inactive">{t('status.inactive')}</SelectItem>
244
+ <SelectItem value="suspended">{t('status.suspended')}</SelectItem>
248
245
  </SelectContent>
249
246
  </Select>
250
247
  </FormControl>
@@ -262,11 +259,11 @@ export function EnterpriseSheet({
262
259
  shouldValidate: true,
263
260
  })
264
261
  }
265
- label="Empresa"
266
- placeholder="Selecionar empresa..."
267
- searchPlaceholder="Buscar por nome..."
268
- emptyLabel="Nenhuma empresa vinculada"
269
- entityLabel="empresa"
262
+ label={t('fields.company')}
263
+ placeholder={t('fields.companyPlaceholder')}
264
+ searchPlaceholder={t('fields.companySearchPlaceholder')}
265
+ emptyLabel={t('fields.companyEmpty')}
266
+ entityLabel={t('fields.companyEntity')}
270
267
  clearable
271
268
  valueType="number"
272
269
  loadOptions={async ({ page, pageSize, search }) => {
@@ -290,8 +287,8 @@ export function EnterpriseSheet({
290
287
  createFields={[
291
288
  {
292
289
  name: 'name',
293
- label: 'Nome',
294
- placeholder: 'Ex: Techcorp Brasil Ltda',
290
+ label: t('fields.companyCreateName'),
291
+ placeholder: t('fields.companyCreateNamePlaceholder'),
295
292
  required: true,
296
293
  },
297
294
  ]}
@@ -317,11 +314,10 @@ export function EnterpriseSheet({
317
314
  <FormItem className="flex items-center justify-between rounded-lg border px-4 py-3">
318
315
  <div className="space-y-0.5">
319
316
  <FormLabel className="text-sm font-medium">
320
- Portal enabled
317
+ {t('fields.portalEnabled')}
321
318
  </FormLabel>
322
319
  <FormDescription className="text-xs">
323
- Allow this account to access the self-service learning
324
- portal.
320
+ {t('fields.portalEnabledDescription')}
325
321
  </FormDescription>
326
322
  </div>
327
323
  <FormControl>
@@ -340,12 +336,12 @@ export function EnterpriseSheet({
340
336
  name="licenseLimit"
341
337
  render={({ field }) => (
342
338
  <FormItem>
343
- <FormLabel>License limit</FormLabel>
339
+ <FormLabel>{t('fields.licenseLimit')}</FormLabel>
344
340
  <FormControl>
345
341
  <Input
346
342
  type="number"
347
343
  min={1}
348
- placeholder="e.g. 500 — leave blank for unlimited"
344
+ placeholder={t('fields.licenseLimitPlaceholder')}
349
345
  value={field.value ?? ''}
350
346
  onChange={(e) =>
351
347
  field.onChange(
@@ -355,7 +351,7 @@ export function EnterpriseSheet({
355
351
  />
356
352
  </FormControl>
357
353
  <FormDescription>
358
- Maximum number of active users. Leave blank for unlimited.
354
+ {t('fields.licenseLimitDescription')}
359
355
  </FormDescription>
360
356
  <FormMessage />
361
357
  </FormItem>
@@ -368,10 +364,10 @@ export function EnterpriseSheet({
368
364
  name="notes"
369
365
  render={({ field }) => (
370
366
  <FormItem>
371
- <FormLabel>Notes</FormLabel>
367
+ <FormLabel>{t('fields.notes')}</FormLabel>
372
368
  <FormControl>
373
369
  <Textarea
374
- placeholder="Internal notes about this account..."
370
+ placeholder={t('fields.notesPlaceholder')}
375
371
  rows={3}
376
372
  {...field}
377
373
  value={field.value ?? ''}
@@ -384,9 +380,11 @@ export function EnterpriseSheet({
384
380
 
385
381
  <FormActions
386
382
  sheet
387
- cancelLabel="Cancel"
383
+ cancelLabel={t('actions.cancel')}
388
384
  onCancel={() => onOpenChange(false)}
389
- submitLabel={isEditing ? 'Save changes' : 'Create account'}
385
+ submitLabel={
386
+ isEditing ? t('actions.saveChanges') : t('actions.create')
387
+ }
390
388
  submitType="submit"
391
389
  />
392
390
  </form>
@@ -21,6 +21,7 @@ import {
21
21
  import { useApp } from '@hed-hog/next-app-provider';
22
22
  import { zodResolver } from '@hookform/resolvers/zod';
23
23
  import { Loader2 } from 'lucide-react';
24
+ import { useTranslations } from 'next-intl';
24
25
  import { useEffect, useState } from 'react';
25
26
  import { useForm } from 'react-hook-form';
26
27
  import { toast } from 'sonner';
@@ -57,6 +58,7 @@ export function EnterpriseStudentCreateSheet({
57
58
  onOpenChange,
58
59
  onCreated,
59
60
  }: EnterpriseStudentCreateSheetProps) {
61
+ const t = useTranslations('lms.EnterprisePage');
60
62
  const { request } = useApp();
61
63
  const [saving, setSaving] = useState(false);
62
64
 
@@ -90,7 +92,7 @@ export function EnterpriseStudentCreateSheet({
90
92
  onCreated?.(personId);
91
93
  handleOpenChange(false);
92
94
  } catch {
93
- toast.error('Failed to create person.');
95
+ toast.error(t('sheet.studentCreated'));
94
96
  } finally {
95
97
  setSaving(false);
96
98
  }
@@ -15,6 +15,7 @@ import {
15
15
  import { formatDate } from '@/lib/format-date';
16
16
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
17
17
  import { Trash2 } from 'lucide-react';
18
+ import { useTranslations } from 'next-intl';
18
19
  import { useCallback, useState } from 'react';
19
20
  import { toast } from 'sonner';
20
21
  import {
@@ -39,6 +40,7 @@ export function StudentsTab({
39
40
  enterpriseId: number;
40
41
  onMutate?: () => void;
41
42
  }) {
43
+ const t = useTranslations('lms.EnterprisePage');
42
44
  const { request, getSettingValue, currentLocaleCode } = useApp();
43
45
  const [search, setSearch] = useState('');
44
46
  const [page, setPage] = useState(1);
@@ -93,7 +95,7 @@ export function StudentsTab({
93
95
  refetch();
94
96
  onMutate?.();
95
97
  } catch {
96
- toast.error('Failed to add student.');
98
+ toast.error(t('sheet.studentAddError'));
97
99
  }
98
100
  }
99
101
 
@@ -107,9 +109,9 @@ export function StudentsTab({
107
109
  onMutate?.();
108
110
  });
109
111
  toast.promise(promise, {
110
- loading: 'Removing student…',
111
- success: 'Student removed.',
112
- error: 'Failed to remove student.',
112
+ loading: t('sheet.studentRemoving'),
113
+ success: t('sheet.studentRemoved'),
114
+ error: t('sheet.studentRemoveError'),
113
115
  });
114
116
  try {
115
117
  await promise;
@@ -255,7 +257,7 @@ export function StudentsTab({
255
257
  data: { person_id: personId, status: 'active' },
256
258
  });
257
259
  } catch {
258
- toast.error('Person created but failed to link to enterprise.');
260
+ toast.error(t('sheet.studentLinkError'));
259
261
  }
260
262
  setCreateSheetOpen(false);
261
263
  refetch();