@hed-hog/lms 0.0.329 → 0.0.330

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 (37) 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 +7 -5
  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/classes/[id]/page.tsx.ejs +10 -10
  10. package/hedhog/frontend/app/classes/page.tsx.ejs +23 -15
  11. package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +5 -3
  12. package/hedhog/frontend/app/courses/[id]/structure/_components/confirm-dialog.tsx.ejs +5 -3
  13. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-panel.tsx.ejs +9 -7
  14. package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-skeleton.tsx.ejs +3 -1
  15. package/hedhog/frontend/app/courses/[id]/structure/_components/drag-handle.tsx.ejs +4 -2
  16. package/hedhog/frontend/app/courses/[id]/structure/_components/editor-bulk.tsx.ejs +24 -23
  17. package/hedhog/frontend/app/courses/[id]/structure/_components/multi-select-bar.tsx.ejs +21 -19
  18. package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +7 -5
  19. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-display-settings-popover.tsx.ejs +18 -16
  20. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-lesson.tsx.ejs +13 -11
  21. package/hedhog/frontend/app/courses/[id]/structure/_components/tree-row-session.tsx.ejs +5 -3
  22. package/hedhog/frontend/app/courses/[id]/structure/_components/use-course-structure-shortcuts.ts.ejs +14 -9
  23. package/hedhog/frontend/app/courses/[id]/structure/_data/use-course-structure-mutations.ts.ejs +42 -25
  24. package/hedhog/frontend/app/enterprise/_components/enterprise-admin-create-sheet.tsx.ejs +3 -1
  25. package/hedhog/frontend/app/enterprise/_components/enterprise-administrators-tab.tsx.ejs +10 -8
  26. package/hedhog/frontend/app/enterprise/_components/enterprise-classes-tab.tsx.ejs +22 -20
  27. package/hedhog/frontend/app/enterprise/_components/enterprise-course-create-sheet.tsx.ejs +3 -3
  28. package/hedhog/frontend/app/enterprise/_components/enterprise-courses-tab.tsx.ejs +21 -19
  29. package/hedhog/frontend/app/enterprise/_components/enterprise-sheet.tsx.ejs +34 -36
  30. package/hedhog/frontend/app/enterprise/_components/enterprise-student-create-sheet.tsx.ejs +3 -1
  31. package/hedhog/frontend/app/enterprise/_components/enterprise-students-tab.tsx.ejs +7 -5
  32. package/hedhog/frontend/app/enterprise/page.tsx.ejs +106 -54
  33. package/hedhog/frontend/app/instructor-skills/page.tsx.ejs +79 -59
  34. package/hedhog/frontend/app/instructors/page.tsx.ejs +4 -2
  35. package/hedhog/frontend/messages/en.json +619 -13
  36. package/hedhog/frontend/messages/pt.json +619 -13
  37. package/package.json +7 -7
@@ -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();
@@ -44,6 +44,7 @@ import {
44
44
  SquareArrowOutUpRight,
45
45
  Users,
46
46
  } from 'lucide-react';
47
+ import { useTranslations } from 'next-intl';
47
48
  import { useMemo, useState } from 'react';
48
49
  import { EnterpriseDetailSheet } from './_components/enterprise-detail-sheet';
49
50
  import { EnterpriseSheet } from './_components/enterprise-sheet';
@@ -69,16 +70,10 @@ const STATUS_VARIANT: Record<
69
70
  suspended: 'destructive',
70
71
  };
71
72
 
72
- const STATUS_LABEL: Record<EnterpriseStatus, string> = {
73
- active: 'Active',
74
- trial: 'Trial',
75
- inactive: 'Inactive',
76
- suspended: 'Suspended',
77
- };
78
-
79
73
  // ── Page ──────────────────────────────────────────────────────────────────────
80
74
 
81
75
  export default function EnterprisePage() {
76
+ const t = useTranslations('lms.EnterprisePage');
82
77
  const [search, setSearch] = useState('');
83
78
  const [statusFilter, setStatusFilter] = useState('all');
84
79
  const [crmFilter, setCrmFilter] = useState('all');
@@ -205,42 +200,48 @@ export default function EnterprisePage() {
205
200
  return [
206
201
  {
207
202
  key: 'total',
208
- title: 'Total Accounts',
203
+ title: t('kpis.total.label'),
209
204
  value: total,
210
- description: 'All enterprise accounts',
205
+ description: t('kpis.total.description'),
211
206
  icon: Building2,
212
207
  accentClassName: 'from-slate-500/20 via-slate-400/10 to-transparent',
213
208
  iconContainerClassName: 'bg-slate-100 text-slate-700',
214
209
  },
215
210
  {
216
211
  key: 'active',
217
- title: 'Active',
212
+ title: t('kpis.active.label'),
218
213
  value: active,
219
- description: 'Currently active accounts',
214
+ description: t('kpis.active.description'),
220
215
  icon: Users,
221
216
  accentClassName: 'from-green-500/20 via-emerald-500/10 to-transparent',
222
217
  iconContainerClassName: 'bg-green-50 text-green-600',
223
218
  },
224
219
  {
225
220
  key: 'trial',
226
- title: 'Trial',
221
+ title: t.has('kpis.trial.label') ? t('kpis.trial.label') : 'Trial',
227
222
  value: trial,
228
- description: 'In trial period',
223
+ description: t.has('kpis.trial.description')
224
+ ? t('kpis.trial.description')
225
+ : 'In trial period',
229
226
  icon: CalendarDays,
230
227
  accentClassName: 'from-amber-500/20 via-orange-500/10 to-transparent',
231
228
  iconContainerClassName: 'bg-amber-50 text-amber-600',
232
229
  },
233
230
  {
234
231
  key: 'portal',
235
- title: 'Portal Enabled',
232
+ title: t.has('kpis.portal.label')
233
+ ? t('kpis.portal.label')
234
+ : 'Portal Enabled',
236
235
  value: withPortal,
237
- description: 'Self-service portal access',
236
+ description: t.has('kpis.portal.description')
237
+ ? t('kpis.portal.description')
238
+ : 'Self-service portal access',
238
239
  icon: Globe,
239
240
  accentClassName: 'from-blue-500/20 via-cyan-500/10 to-transparent',
240
241
  iconContainerClassName: 'bg-blue-50 text-blue-600',
241
242
  },
242
243
  ];
243
- }, [stats]);
244
+ }, [stats, t]);
244
245
 
245
246
  const accounts = enterpriseList?.data ?? [];
246
247
  const totalItems = enterpriseList?.total ?? 0;
@@ -271,11 +272,19 @@ export default function EnterprisePage() {
271
272
  value: statusFilter,
272
273
  onChange: handleStatusFilter,
273
274
  options: [
274
- { value: 'all', label: 'All statuses' },
275
- { value: 'active', label: 'Active' },
276
- { value: 'trial', label: 'Trial' },
277
- { value: 'inactive', label: 'Inactive' },
278
- { value: 'suspended', label: 'Suspended' },
275
+ { value: 'all', label: t('filters.allStatuses') },
276
+ { value: 'active', label: t('status.active') },
277
+ {
278
+ value: 'trial',
279
+ label: t.has('status.trial') ? t('status.trial') : 'Trial',
280
+ },
281
+ { value: 'inactive', label: t('status.inactive') },
282
+ {
283
+ value: 'suspended',
284
+ label: t.has('status.suspended')
285
+ ? t('status.suspended')
286
+ : 'Suspended',
287
+ },
279
288
  ],
280
289
  },
281
290
  {
@@ -283,7 +292,15 @@ export default function EnterprisePage() {
283
292
  type: 'select',
284
293
  value: crmFilter,
285
294
  onChange: handleCrmFilter,
286
- options: [{ value: 'all', label: 'All CRM accounts' }, ...crmOptions],
295
+ options: [
296
+ {
297
+ value: 'all',
298
+ label: t.has('filters.allCrmAccounts')
299
+ ? t('filters.allCrmAccounts')
300
+ : 'All CRM accounts',
301
+ },
302
+ ...crmOptions,
303
+ ],
287
304
  // options populated from API
288
305
  },
289
306
  ];
@@ -291,14 +308,16 @@ export default function EnterprisePage() {
291
308
  return (
292
309
  <Page>
293
310
  <PageHeader
311
+ title={t('title')}
312
+ description={t('description')}
294
313
  breadcrumbs={[
295
- { label: 'Home', href: '/' },
296
- { label: 'LMS', href: '/lms' },
297
- { label: 'Enterprise' },
314
+ { label: t('breadcrumbs.home'), href: '/' },
315
+ { label: t('breadcrumbs.lms'), href: '/lms' },
316
+ { label: t('breadcrumbs.enterprise') },
298
317
  ]}
299
318
  actions={[
300
319
  {
301
- label: 'New Account',
320
+ label: t('actions.new'),
302
321
  onClick: handleOpenCreate,
303
322
  icon: <Plus className="h-4 w-4" />,
304
323
  variant: 'default',
@@ -314,13 +333,13 @@ export default function EnterprisePage() {
314
333
  searchQuery={search}
315
334
  onSearchChange={handleSearch}
316
335
  onSearch={() => {}}
317
- placeholder="Search by name or CRM account..."
336
+ placeholder={t('filters.searchPlaceholder')}
318
337
  controls={controls}
319
338
  />
320
339
  </div>
321
340
  <div className="flex items-center gap-3">
322
341
  <span className="text-xs font-medium text-muted-foreground">
323
- View
342
+ {t.has('view.label') ? t('view.label') : 'View'}
324
343
  </span>
325
344
  <ToggleGroup
326
345
  type="single"
@@ -328,15 +347,23 @@ export default function EnterprisePage() {
328
347
  onValueChange={handleViewModeChange}
329
348
  variant="outline"
330
349
  size="sm"
331
- aria-label="View mode"
350
+ aria-label={
351
+ t.has('view.modeAriaLabel')
352
+ ? t('view.modeAriaLabel')
353
+ : 'View mode'
354
+ }
332
355
  >
333
356
  <ToggleGroupItem value="table" className="gap-1.5 px-2.5">
334
357
  <List className="h-4 w-4" />
335
- <span className="hidden sm:inline">Table</span>
358
+ <span className="hidden sm:inline">
359
+ {t.has('view.table') ? t('view.table') : 'Table'}
360
+ </span>
336
361
  </ToggleGroupItem>
337
362
  <ToggleGroupItem value="cards" className="gap-1.5 px-2.5">
338
363
  <LayoutGrid className="h-4 w-4" />
339
- <span className="hidden sm:inline">Cards</span>
364
+ <span className="hidden sm:inline">
365
+ {t.has('view.cards') ? t('view.cards') : 'Cards'}
366
+ </span>
340
367
  </ToggleGroupItem>
341
368
  </ToggleGroup>
342
369
  </div>
@@ -345,9 +372,9 @@ export default function EnterprisePage() {
345
372
  {accounts.length === 0 && !isLoading ? (
346
373
  <EmptyState
347
374
  icon={<Building2 className="h-10 w-10" />}
348
- title="No enterprise accounts found"
349
- description="Adjust your filters or create a new enterprise account."
350
- actionLabel="New Account"
375
+ title={t('empty.title')}
376
+ description={t('empty.description')}
377
+ actionLabel={t('actions.new')}
351
378
  onAction={handleOpenCreate}
352
379
  actionIcon={<Plus className="h-4 w-4" />}
353
380
  />
@@ -356,13 +383,25 @@ export default function EnterprisePage() {
356
383
  <Table>
357
384
  <TableHeader>
358
385
  <TableRow>
359
- <TableHead>Name</TableHead>
360
- <TableHead>Status</TableHead>
361
- <TableHead>CRM Account</TableHead>
362
- <TableHead className="text-center">Classes</TableHead>
363
- <TableHead className="text-center">Courses</TableHead>
364
- <TableHead className="text-center">Users</TableHead>
365
- <TableHead>Updated</TableHead>
386
+ <TableHead>{t('table.name')}</TableHead>
387
+ <TableHead>{t('table.status')}</TableHead>
388
+ <TableHead>
389
+ {t.has('table.crmAccount')
390
+ ? t('table.crmAccount')
391
+ : 'CRM Account'}
392
+ </TableHead>
393
+ <TableHead className="text-center">
394
+ {t.has('table.classes') ? t('table.classes') : 'Classes'}
395
+ </TableHead>
396
+ <TableHead className="text-center">
397
+ {t('table.activeCourses')}
398
+ </TableHead>
399
+ <TableHead className="text-center">
400
+ {t.has('table.users') ? t('table.users') : 'Users'}
401
+ </TableHead>
402
+ <TableHead>
403
+ {t.has('table.updated') ? t('table.updated') : 'Updated'}
404
+ </TableHead>
366
405
  <TableHead className="w-10" />
367
406
  </TableRow>
368
407
  </TableHeader>
@@ -381,14 +420,20 @@ export default function EnterprisePage() {
381
420
  <>
382
421
  <span className="opacity-30">·</span>
383
422
  <Globe className="h-3 w-3 text-primary/60" />
384
- <span className="text-primary/70">Portal</span>
423
+ <span className="text-primary/70">
424
+ {t.has('labels.portal')
425
+ ? t('labels.portal')
426
+ : 'Portal'}
427
+ </span>
385
428
  </>
386
429
  )}
387
430
  </p>
388
431
  </TableCell>
389
432
  <TableCell>
390
433
  <Badge variant={STATUS_VARIANT[account.status]}>
391
- {STATUS_LABEL[account.status]}
434
+ {t.has(`status.${account.status}`)
435
+ ? t(`status.${account.status}`)
436
+ : account.status}
392
437
  </Badge>
393
438
  </TableCell>
394
439
  <TableCell className="text-sm text-muted-foreground">
@@ -422,14 +467,14 @@ export default function EnterprisePage() {
422
467
  onClick={() => handleViewDetails(account)}
423
468
  >
424
469
  <SquareArrowOutUpRight className="mr-2 h-4 w-4" />
425
- View details
470
+ {t('actions.view')}
426
471
  </DropdownMenuItem>
427
472
  <DropdownMenuSeparator />
428
473
  <DropdownMenuItem
429
474
  onClick={() => handleOpenEdit(account)}
430
475
  >
431
476
  <Pencil className="mr-2 h-4 w-4" />
432
- Edit
477
+ {t('actions.edit')}
433
478
  </DropdownMenuItem>
434
479
  </DropdownMenuContent>
435
480
  </DropdownMenu>
@@ -453,7 +498,9 @@ export default function EnterprisePage() {
453
498
  <div className="min-w-0 flex-1">
454
499
  <div className="mb-1 flex flex-wrap items-center gap-1.5">
455
500
  <Badge variant={STATUS_VARIANT[account.status]}>
456
- {STATUS_LABEL[account.status]}
501
+ {t.has(`status.${account.status}`)
502
+ ? t(`status.${account.status}`)
503
+ : account.status}
457
504
  </Badge>
458
505
  {account.portalEnabled && (
459
506
  <Badge
@@ -461,7 +508,9 @@ export default function EnterprisePage() {
461
508
  className="border-blue-500/20 bg-blue-500/10 px-1.5 text-blue-600"
462
509
  >
463
510
  <Globe className="mr-1 h-3 w-3" />
464
- Portal
511
+ {t.has('labels.portal')
512
+ ? t('labels.portal')
513
+ : 'Portal'}
465
514
  </Badge>
466
515
  )}
467
516
  </div>
@@ -491,14 +540,14 @@ export default function EnterprisePage() {
491
540
  onClick={() => handleViewDetails(account)}
492
541
  >
493
542
  <SquareArrowOutUpRight className="mr-2 h-4 w-4" />
494
- View details
543
+ {t('actions.view')}
495
544
  </DropdownMenuItem>
496
545
  <DropdownMenuSeparator />
497
546
  <DropdownMenuItem
498
547
  onClick={() => handleOpenEdit(account)}
499
548
  >
500
549
  <Pencil className="mr-2 h-4 w-4" />
501
- Edit
550
+ {t('actions.edit')}
502
551
  </DropdownMenuItem>
503
552
  </DropdownMenuContent>
504
553
  </DropdownMenu>
@@ -508,7 +557,9 @@ export default function EnterprisePage() {
508
557
  {/* CRM */}
509
558
  {account.crmAccountName && (
510
559
  <p className="truncate text-xs text-muted-foreground">
511
- <span className="font-medium">CRM:</span>{' '}
560
+ <span className="font-medium">
561
+ {t.has('labels.crm') ? t('labels.crm') : 'CRM'}:
562
+ </span>{' '}
512
563
  {account.crmAccountName}
513
564
  </p>
514
565
  )}
@@ -521,7 +572,7 @@ export default function EnterprisePage() {
521
572
  {account.classesCount}
522
573
  </span>
523
574
  <span className="text-[10px] text-muted-foreground">
524
- Classes
575
+ {t.has('table.classes') ? t('table.classes') : 'Classes'}
525
576
  </span>
526
577
  </div>
527
578
  <div className="flex flex-col py-2">
@@ -530,7 +581,7 @@ export default function EnterprisePage() {
530
581
  {account.coursesCount}
531
582
  </span>
532
583
  <span className="text-[10px] text-muted-foreground">
533
- Courses
584
+ {t('table.activeCourses')}
534
585
  </span>
535
586
  </div>
536
587
  <div className="flex flex-col py-2">
@@ -539,14 +590,15 @@ export default function EnterprisePage() {
539
590
  {account.usersCount}
540
591
  </span>
541
592
  <span className="text-[10px] text-muted-foreground">
542
- Users
593
+ {t.has('table.users') ? t('table.users') : 'Users'}
543
594
  </span>
544
595
  </div>
545
596
  </div>
546
597
 
547
598
  {/* Footer */}
548
599
  <p className="text-right text-[10px] text-muted-foreground/60">
549
- Updated{' '}
600
+ {(t.has('labels.updated') ? t('labels.updated') : 'Updated') +
601
+ ' '}
550
602
  {formatDate(
551
603
  account.updatedAt,
552
604
  getSettingValue,