@hed-hog/lms 0.0.330 → 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/course-form-sheet.tsx.ejs +3 -3
- package/hedhog/frontend/app/certificates/models/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/classes/[id]/page.tsx.ejs +17 -17
- package/hedhog/frontend/app/courses/[id]/_components/CourseMultiEntityPicker.tsx.ejs +2 -2
- package/hedhog/frontend/app/courses/[id]/page.tsx.ejs +3 -3
- package/hedhog/frontend/app/courses/[id]/structure/_components/course-tree-dnd.tsx.ejs +1 -1
- package/hedhog/frontend/app/courses/[id]/structure/_components/editor-lesson.tsx.ejs +228 -152
- package/hedhog/frontend/app/courses/[id]/structure/_components/shortcuts-help.tsx.ejs +71 -31
- package/hedhog/frontend/app/enterprise/[id]/page.tsx.ejs +37 -41
- 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/instructors/_components/instructor-form-sheet.tsx.ejs +145 -119
- package/hedhog/frontend/app/instructors/page.tsx.ejs +71 -52
- 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 +294 -46
- package/hedhog/frontend/messages/pt.json +289 -39
- 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
|
@@ -18,36 +18,7 @@ import { Separator } from '@/components/ui/separator';
|
|
|
18
18
|
const SHORTCUT_GROUPS: {
|
|
19
19
|
heading: string;
|
|
20
20
|
items: { keys: string[]; description: string }[];
|
|
21
|
-
}[] = [
|
|
22
|
-
{
|
|
23
|
-
heading: 'Navegação',
|
|
24
|
-
items: [
|
|
25
|
-
{ keys: ['↑', '↓'], description: 'Navegar entre itens' },
|
|
26
|
-
{ keys: ['→'], description: 'Expandir sessão selecionada' },
|
|
27
|
-
{ keys: ['←'], description: 'Recolher sessão / ir para pai' },
|
|
28
|
-
{ keys: ['Enter'], description: 'Focar primeiro campo do editor' },
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
heading: 'Ações',
|
|
33
|
-
items: [
|
|
34
|
-
{ keys: ['Ctrl', 'S'], description: 'Salvar formulário do painel' },
|
|
35
|
-
{ keys: ['Ctrl', 'N'], description: 'Criar nova sessão ou aula' },
|
|
36
|
-
{ keys: ['Ctrl', 'C'], description: 'Copiar item selecionado' },
|
|
37
|
-
{ keys: ['Ctrl', 'V'], description: 'Colar no contexto atual' },
|
|
38
|
-
{ keys: ['Ctrl', 'D'], description: 'Duplicar item' },
|
|
39
|
-
{ keys: ['Delete'], description: 'Excluir item(ns) selecionado(s)' },
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
heading: 'Busca & Interface',
|
|
44
|
-
items: [
|
|
45
|
-
{ keys: ['Ctrl', 'F'], description: 'Focar campo de busca' },
|
|
46
|
-
{ keys: ['Ctrl', '/'], description: 'Abrir ajuda de atalhos' },
|
|
47
|
-
{ keys: ['Esc'], description: 'Limpar busca / seleção / foco' },
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
];
|
|
21
|
+
}[] = [];
|
|
51
22
|
|
|
52
23
|
// ── Component ─────────────────────────────────────────────────────────────────
|
|
53
24
|
|
|
@@ -58,6 +29,75 @@ interface ShortcutsHelpProps {
|
|
|
58
29
|
|
|
59
30
|
export function ShortcutsHelp({ open, onOpenChange }: ShortcutsHelpProps) {
|
|
60
31
|
const t = useTranslations('lms.CoursesPage.StructurePage.shortcuts');
|
|
32
|
+
const shortcutGroups = [
|
|
33
|
+
{
|
|
34
|
+
heading: t('groups.navigation.heading'),
|
|
35
|
+
items: [
|
|
36
|
+
{
|
|
37
|
+
keys: ['↑', '↓'],
|
|
38
|
+
description: t('groups.navigation.items.navigate'),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
keys: ['→'],
|
|
42
|
+
description: t('groups.navigation.items.expand'),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
keys: ['←'],
|
|
46
|
+
description: t('groups.navigation.items.collapse'),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
keys: ['Enter'],
|
|
50
|
+
description: t('groups.navigation.items.focusEditor'),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
heading: t('groups.actions.heading'),
|
|
56
|
+
items: [
|
|
57
|
+
{
|
|
58
|
+
keys: ['Ctrl', 'S'],
|
|
59
|
+
description: t('groups.actions.items.savePanel'),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
keys: ['Ctrl', 'N'],
|
|
63
|
+
description: t('groups.actions.items.createItem'),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
keys: ['Ctrl', 'C'],
|
|
67
|
+
description: t('groups.actions.items.copyItem'),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
keys: ['Ctrl', 'V'],
|
|
71
|
+
description: t('groups.actions.items.pasteItem'),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
keys: ['Ctrl', 'D'],
|
|
75
|
+
description: t('groups.actions.items.duplicateItem'),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
keys: ['Delete'],
|
|
79
|
+
description: t('groups.actions.items.deleteItems'),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
heading: t('groups.search.heading'),
|
|
85
|
+
items: [
|
|
86
|
+
{
|
|
87
|
+
keys: ['Ctrl', 'F'],
|
|
88
|
+
description: t('groups.search.items.focusSearch'),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
keys: ['Ctrl', '/'],
|
|
92
|
+
description: t('groups.search.items.openHelp'),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
keys: ['Esc'],
|
|
96
|
+
description: t('groups.search.items.clearState'),
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
];
|
|
61
101
|
return (
|
|
62
102
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
63
103
|
<DialogContent className="max-w-sm">
|
|
@@ -69,7 +109,7 @@ export function ShortcutsHelp({ open, onOpenChange }: ShortcutsHelpProps) {
|
|
|
69
109
|
</DialogHeader>
|
|
70
110
|
|
|
71
111
|
<div className="flex flex-col gap-4 mt-1">
|
|
72
|
-
{
|
|
112
|
+
{shortcutGroups.map((group, gi) => (
|
|
73
113
|
<div key={gi}>
|
|
74
114
|
<p className="text-[0.65rem] font-semibold uppercase tracking-wider text-muted-foreground mb-2">
|
|
75
115
|
{group.heading}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
UserCheck,
|
|
21
21
|
Users,
|
|
22
22
|
} from 'lucide-react';
|
|
23
|
+
import { useTranslations } from 'next-intl';
|
|
23
24
|
import { notFound, useParams, useRouter } from 'next/navigation';
|
|
24
25
|
import { useState } from 'react';
|
|
25
26
|
import { AdministratorsTab } from '../_components/enterprise-administrators-tab';
|
|
@@ -44,13 +45,6 @@ const STATUS_VARIANT: Record<
|
|
|
44
45
|
suspended: 'destructive',
|
|
45
46
|
};
|
|
46
47
|
|
|
47
|
-
const STATUS_LABEL: Record<EnterpriseStatus, string> = {
|
|
48
|
-
active: 'Active',
|
|
49
|
-
trial: 'Trial',
|
|
50
|
-
inactive: 'Inactive',
|
|
51
|
-
suspended: 'Suspended',
|
|
52
|
-
};
|
|
53
|
-
|
|
54
48
|
// ── CRM Tab ───────────────────────────────────────────────────────────────────
|
|
55
49
|
|
|
56
50
|
function CrmTab({
|
|
@@ -62,6 +56,7 @@ function CrmTab({
|
|
|
62
56
|
crmAccountName: string | null;
|
|
63
57
|
onLinkCrm?: () => void;
|
|
64
58
|
}) {
|
|
59
|
+
const t = useTranslations('lms.EnterpriseDetailPage');
|
|
65
60
|
const router = useRouter();
|
|
66
61
|
if (!crmAccountId || !crmAccountName) {
|
|
67
62
|
return (
|
|
@@ -70,11 +65,10 @@ function CrmTab({
|
|
|
70
65
|
<Link2 className="h-12 w-12" />
|
|
71
66
|
</div>
|
|
72
67
|
<p className="text-sm font-medium text-muted-foreground">
|
|
73
|
-
|
|
68
|
+
{t('crm.noLinked')}
|
|
74
69
|
</p>
|
|
75
70
|
<p className="mt-1 max-w-xs text-xs text-muted-foreground/60">
|
|
76
|
-
|
|
77
|
-
account. Link one to keep both records in sync.
|
|
71
|
+
{t('crm.noLinkedDesc')}
|
|
78
72
|
</p>
|
|
79
73
|
<Button
|
|
80
74
|
variant="outline"
|
|
@@ -83,7 +77,7 @@ function CrmTab({
|
|
|
83
77
|
onClick={onLinkCrm}
|
|
84
78
|
>
|
|
85
79
|
<Link2 className="mr-2 h-4 w-4" />
|
|
86
|
-
|
|
80
|
+
{t('crm.linkButton')}
|
|
87
81
|
</Button>
|
|
88
82
|
</div>
|
|
89
83
|
);
|
|
@@ -100,7 +94,7 @@ function CrmTab({
|
|
|
100
94
|
<div>
|
|
101
95
|
<p className="text-base font-semibold">{crmAccountName}</p>
|
|
102
96
|
<p className="text-xs text-muted-foreground">
|
|
103
|
-
|
|
97
|
+
{t('crm.accountRef', { id: crmAccountId })}
|
|
104
98
|
</p>
|
|
105
99
|
</div>
|
|
106
100
|
</div>
|
|
@@ -111,7 +105,7 @@ function CrmTab({
|
|
|
111
105
|
className="shrink-0"
|
|
112
106
|
>
|
|
113
107
|
<ExternalLink className="mr-2 h-4 w-4" />
|
|
114
|
-
|
|
108
|
+
{t('crm.openButton')}
|
|
115
109
|
</Button>
|
|
116
110
|
</div>
|
|
117
111
|
|
|
@@ -120,22 +114,20 @@ function CrmTab({
|
|
|
120
114
|
<dl className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
121
115
|
<div>
|
|
122
116
|
<dt className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
123
|
-
|
|
117
|
+
{t('crm.accountNameLabel')}
|
|
124
118
|
</dt>
|
|
125
119
|
<dd className="mt-1 text-sm">{crmAccountName}</dd>
|
|
126
120
|
</div>
|
|
127
121
|
<div>
|
|
128
122
|
<dt className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
129
|
-
|
|
123
|
+
{t('crm.crmIdLabel')}
|
|
130
124
|
</dt>
|
|
131
125
|
<dd className="mt-1 font-mono text-sm">#{crmAccountId}</dd>
|
|
132
126
|
</div>
|
|
133
127
|
</dl>
|
|
134
128
|
|
|
135
129
|
<p className="mt-5 text-xs text-muted-foreground/60">
|
|
136
|
-
|
|
137
|
-
<span className="font-medium">Open in CRM</span> to manage the
|
|
138
|
-
commercial account directly.
|
|
130
|
+
{t('crm.readNote')}
|
|
139
131
|
</p>
|
|
140
132
|
</CardContent>
|
|
141
133
|
</Card>
|
|
@@ -145,35 +137,36 @@ function CrmTab({
|
|
|
145
137
|
// ── Overview Tab ──────────────────────────────────────────────────────────────
|
|
146
138
|
|
|
147
139
|
function OverviewTab({ account }: { account: EnterpriseAccount }) {
|
|
140
|
+
const t = useTranslations('lms.EnterpriseDetailPage');
|
|
148
141
|
const { currentLocaleCode, getSettingValue } = useApp();
|
|
149
142
|
|
|
150
143
|
const kpiItems: KpiCardItem[] = [
|
|
151
144
|
{
|
|
152
145
|
key: 'users',
|
|
153
|
-
title: '
|
|
146
|
+
title: t('kpis.users.label'),
|
|
154
147
|
value: account.usersCount,
|
|
155
|
-
description: '
|
|
148
|
+
description: t('kpis.users.description'),
|
|
156
149
|
icon: Users,
|
|
157
150
|
},
|
|
158
151
|
{
|
|
159
152
|
key: 'students',
|
|
160
|
-
title: '
|
|
153
|
+
title: t('kpis.students.label'),
|
|
161
154
|
value: account.studentsCount,
|
|
162
|
-
description: '
|
|
155
|
+
description: t('kpis.students.description'),
|
|
163
156
|
icon: UserCheck,
|
|
164
157
|
},
|
|
165
158
|
{
|
|
166
159
|
key: 'classes',
|
|
167
|
-
title: '
|
|
160
|
+
title: t('kpis.contractedClasses.label'),
|
|
168
161
|
value: account.classesCount,
|
|
169
|
-
description: '
|
|
162
|
+
description: t('kpis.contractedClasses.description'),
|
|
170
163
|
icon: CalendarDays,
|
|
171
164
|
},
|
|
172
165
|
{
|
|
173
166
|
key: 'courses',
|
|
174
|
-
title: '
|
|
167
|
+
title: t('kpis.courses.label'),
|
|
175
168
|
value: account.coursesCount,
|
|
176
|
-
description: '
|
|
169
|
+
description: t('kpis.courses.description'),
|
|
177
170
|
icon: BookOpen,
|
|
178
171
|
},
|
|
179
172
|
];
|
|
@@ -183,19 +176,19 @@ function OverviewTab({ account }: { account: EnterpriseAccount }) {
|
|
|
183
176
|
<KpiCardsGrid items={kpiItems} />
|
|
184
177
|
<div className="flex flex-wrap items-center gap-x-6 gap-y-1 border-t pt-3 text-xs text-muted-foreground">
|
|
185
178
|
<span>
|
|
186
|
-
|
|
179
|
+
{t('overview.slug')}{' '}
|
|
187
180
|
<span className="font-mono text-foreground/70">{account.slug}</span>
|
|
188
181
|
</span>
|
|
189
182
|
<span>
|
|
190
|
-
|
|
183
|
+
{t('overview.created')}{' '}
|
|
191
184
|
{formatDate(account.createdAt, getSettingValue, currentLocaleCode)}
|
|
192
185
|
</span>
|
|
193
186
|
<span>
|
|
194
|
-
|
|
187
|
+
{t('overview.updated')}{' '}
|
|
195
188
|
{formatDate(account.updatedAt, getSettingValue, currentLocaleCode)}
|
|
196
189
|
</span>
|
|
197
190
|
{account.portalEnabled && (
|
|
198
|
-
<span className="text-primary/70">
|
|
191
|
+
<span className="text-primary/70">{t('overview.portalEnabled')}</span>
|
|
199
192
|
)}
|
|
200
193
|
</div>
|
|
201
194
|
</div>
|
|
@@ -207,6 +200,7 @@ function OverviewTab({ account }: { account: EnterpriseAccount }) {
|
|
|
207
200
|
export default function EnterpriseDetailPage() {
|
|
208
201
|
const { id } = useParams<{ id: string }>();
|
|
209
202
|
const { request } = useApp();
|
|
203
|
+
const t = useTranslations('lms.EnterpriseDetailPage');
|
|
210
204
|
const [isSheetOpen, setIsSheetOpen] = useState(false);
|
|
211
205
|
|
|
212
206
|
const {
|
|
@@ -233,9 +227,9 @@ export default function EnterpriseDetailPage() {
|
|
|
233
227
|
<Page>
|
|
234
228
|
<PageHeader
|
|
235
229
|
breadcrumbs={[
|
|
236
|
-
{ label: '
|
|
237
|
-
{ label: '
|
|
238
|
-
{ label: '
|
|
230
|
+
{ label: t('breadcrumbs.home'), href: '/' },
|
|
231
|
+
{ label: t('breadcrumbs.lms'), href: '/lms' },
|
|
232
|
+
{ label: t('breadcrumbs.enterprise'), href: '/lms/enterprise' },
|
|
239
233
|
{
|
|
240
234
|
label: isLoading ? '…' : (account?.name ?? ''),
|
|
241
235
|
},
|
|
@@ -243,7 +237,7 @@ export default function EnterpriseDetailPage() {
|
|
|
243
237
|
extraContent={
|
|
244
238
|
account ? (
|
|
245
239
|
<Badge variant={STATUS_VARIANT[account.status]}>
|
|
246
|
-
{
|
|
240
|
+
{t(`status.${account.status}`)}
|
|
247
241
|
</Badge>
|
|
248
242
|
) : (
|
|
249
243
|
<Skeleton className="h-5 w-16 rounded-full" />
|
|
@@ -251,7 +245,7 @@ export default function EnterpriseDetailPage() {
|
|
|
251
245
|
}
|
|
252
246
|
actions={[
|
|
253
247
|
{
|
|
254
|
-
label: '
|
|
248
|
+
label: t('actions.edit'),
|
|
255
249
|
onClick: () => setIsSheetOpen(true),
|
|
256
250
|
icon: <Pencil className="h-4 w-4" />,
|
|
257
251
|
variant: 'outline',
|
|
@@ -268,12 +262,14 @@ export default function EnterpriseDetailPage() {
|
|
|
268
262
|
) : (
|
|
269
263
|
<Tabs defaultValue="overview">
|
|
270
264
|
<TabsList>
|
|
271
|
-
<TabsTrigger value="overview">
|
|
272
|
-
<TabsTrigger value="crm">
|
|
273
|
-
<TabsTrigger value="classes">
|
|
274
|
-
<TabsTrigger value="courses">
|
|
275
|
-
<TabsTrigger value="students">
|
|
276
|
-
<TabsTrigger value="administrators">
|
|
265
|
+
<TabsTrigger value="overview">{t('tabs.overview')}</TabsTrigger>
|
|
266
|
+
<TabsTrigger value="crm">{t('tabs.crm')}</TabsTrigger>
|
|
267
|
+
<TabsTrigger value="classes">{t('tabs.classes')}</TabsTrigger>
|
|
268
|
+
<TabsTrigger value="courses">{t('tabs.courses')}</TabsTrigger>
|
|
269
|
+
<TabsTrigger value="students">{t('tabs.students')}</TabsTrigger>
|
|
270
|
+
<TabsTrigger value="administrators">
|
|
271
|
+
{t('tabs.administrators')}
|
|
272
|
+
</TabsTrigger>
|
|
277
273
|
</TabsList>
|
|
278
274
|
|
|
279
275
|
<TabsContent value="overview" className="mt-6">
|
|
@@ -106,7 +106,7 @@ export function EvaluationTopicFormSheet({
|
|
|
106
106
|
|
|
107
107
|
return (
|
|
108
108
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
109
|
-
<SheetContent side="right" className="w-full overflow-y-auto sm:max-w-md">
|
|
109
|
+
<SheetContent side="right" className="w-full overflow-y-auto sm:max-w-md">
|
|
110
110
|
<SheetHeader>
|
|
111
111
|
<SheetTitle>
|
|
112
112
|
{editingItem ? t('form.editTitle') : t('form.createTitle')}
|
|
@@ -1179,7 +1179,9 @@ export default function ExamesPage() {
|
|
|
1179
1179
|
</Field>
|
|
1180
1180
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
1181
1181
|
<Field>
|
|
1182
|
-
<FieldLabel htmlFor="primaryColor">
|
|
1182
|
+
<FieldLabel htmlFor="primaryColor">
|
|
1183
|
+
{t('form.fields.primaryColor.label')}
|
|
1184
|
+
</FieldLabel>
|
|
1183
1185
|
<Controller
|
|
1184
1186
|
name="primaryColor"
|
|
1185
1187
|
control={form.control}
|
|
@@ -1206,7 +1208,9 @@ export default function ExamesPage() {
|
|
|
1206
1208
|
</Field>
|
|
1207
1209
|
|
|
1208
1210
|
<Field>
|
|
1209
|
-
<FieldLabel htmlFor="secondaryColor">
|
|
1211
|
+
<FieldLabel htmlFor="secondaryColor">
|
|
1212
|
+
{t('form.fields.secondaryColor.label')}
|
|
1213
|
+
</FieldLabel>
|
|
1210
1214
|
<Controller
|
|
1211
1215
|
name="secondaryColor"
|
|
1212
1216
|
control={form.control}
|