@hed-hog/core 0.0.304 → 0.0.305
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/README.md +84 -36
- package/dist/auth/auth.controller.d.ts +0 -3
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +4 -7
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +6 -3
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +9 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +12 -0
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -0
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +45 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +9 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js +49 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -0
- package/dist/mail-sent/mail-sent.controller.d.ts +24 -2
- package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.controller.js +5 -3
- package/dist/mail-sent/mail-sent.controller.js.map +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +28 -3
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +108 -3
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/oauth/oauth-callback-coordinator.service.d.ts +18 -0
- package/dist/oauth/oauth-callback-coordinator.service.d.ts.map +1 -0
- package/dist/oauth/oauth-callback-coordinator.service.js +137 -0
- package/dist/oauth/oauth-callback-coordinator.service.js.map +1 -0
- package/dist/oauth/oauth.controller.d.ts +4 -2
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +39 -11
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.errors.d.ts +16 -0
- package/dist/oauth/oauth.errors.d.ts.map +1 -0
- package/dist/oauth/oauth.errors.js +30 -0
- package/dist/oauth/oauth.errors.js.map +1 -0
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +10 -1
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +9 -1
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +97 -24
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts +6 -0
- package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
- package/dist/oauth/providers/abstract.provider.js +34 -0
- package/dist/oauth/providers/abstract.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +3 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +40 -22
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/security/security.service.d.ts +1 -0
- package/dist/security/security.service.d.ts.map +1 -1
- package/dist/security/security.service.js +9 -10
- package/dist/security/security.service.js.map +1 -1
- package/dist/session/session.service.d.ts +5 -1
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +76 -10
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/setting.service.js +4 -4
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/token/token.service.d.ts +1 -0
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +23 -12
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.service.d.ts +1 -1
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +3 -0
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/dashboard.yaml +2 -2
- package/hedhog/data/route.yaml +8 -0
- package/hedhog/frontend/app/account/components/change-email-form.tsx.ejs +87 -8
- package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +90 -10
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +145 -31
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +67 -2
- package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +13 -9
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +250 -45
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +111 -15
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +110 -15
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +113 -15
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +481 -70
- package/hedhog/frontend/app/menu/page.tsx.ejs +202 -30
- package/hedhog/frontend/app/roles/menus.tsx.ejs +1 -1
- package/hedhog/frontend/app/roles/page.tsx.ejs +288 -104
- package/hedhog/frontend/app/users/page.tsx.ejs +168 -23
- package/hedhog/frontend/messages/en.json +45 -6
- package/hedhog/frontend/messages/pt.json +45 -6
- package/package.json +5 -5
- package/src/auth/auth.controller.ts +15 -19
- package/src/auth/auth.service.ts +13 -8
- package/src/dashboard/dashboard-core/dashboard-core.controller.ts +22 -9
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +75 -1
- package/src/mail-sent/dto/query-mail-sent-list.dto.ts +30 -0
- package/src/mail-sent/mail-sent.controller.ts +14 -2
- package/src/mail-sent/mail-sent.service.ts +159 -4
- package/src/oauth/oauth-callback-coordinator.service.ts +158 -0
- package/src/oauth/oauth.controller.ts +58 -1
- package/src/oauth/oauth.errors.ts +32 -0
- package/src/oauth/oauth.module.ts +10 -1
- package/src/oauth/oauth.service.ts +178 -28
- package/src/oauth/providers/abstract.provider.ts +64 -0
- package/src/oauth/providers/microsoft-entra-id.provider.ts +92 -56
- package/src/security/security.service.ts +12 -10
- package/src/session/session.service.ts +84 -9
- package/src/setting/setting.service.ts +10 -10
- package/src/token/token.service.ts +29 -11
- package/src/user/user.service.ts +10 -6
|
@@ -52,9 +52,13 @@ import {
|
|
|
52
52
|
} from '@/components/ui/sheet';
|
|
53
53
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
54
54
|
import { Textarea } from '@/components/ui/textarea';
|
|
55
|
+
import { useFormDraft } from '@/hooks/use-form-draft';
|
|
56
|
+
import { formatDateTime } from '@/lib/format-date';
|
|
55
57
|
import { Role } from '@hed-hog/api-types';
|
|
56
58
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
57
59
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
60
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
61
|
+
import { enUS, ptBR } from 'date-fns/locale';
|
|
58
62
|
import {
|
|
59
63
|
FileText,
|
|
60
64
|
Menu,
|
|
@@ -65,8 +69,8 @@ import {
|
|
|
65
69
|
Users,
|
|
66
70
|
} from 'lucide-react';
|
|
67
71
|
import { useTranslations } from 'next-intl';
|
|
68
|
-
import { useEffect, useState } from 'react';
|
|
69
|
-
import { useForm } from 'react-hook-form';
|
|
72
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
73
|
+
import { useForm, useWatch } from 'react-hook-form';
|
|
70
74
|
import { toast } from 'sonner';
|
|
71
75
|
import { z } from 'zod';
|
|
72
76
|
import { RoleMenusSection } from './menus';
|
|
@@ -107,9 +111,53 @@ type RoleDetail = {
|
|
|
107
111
|
locale?: Record<string, RoleLocale>;
|
|
108
112
|
};
|
|
109
113
|
|
|
114
|
+
type RoleListItem = Role & {
|
|
115
|
+
role_id: number;
|
|
116
|
+
user_count?: number;
|
|
117
|
+
menu_count?: number;
|
|
118
|
+
route_count?: number;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
type RoleDraftPayload = {
|
|
122
|
+
mode: 'create' | 'edit';
|
|
123
|
+
roleId: number | null;
|
|
124
|
+
localeCode: string;
|
|
125
|
+
values: {
|
|
126
|
+
slug: string;
|
|
127
|
+
name: string;
|
|
128
|
+
description: string;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const ROLE_CREATE_DRAFT_STORAGE_KEY = 'core-role-create-draft';
|
|
133
|
+
const ROLE_EDIT_DRAFT_STORAGE_KEY = 'core-role-edit-draft';
|
|
134
|
+
|
|
135
|
+
function getErrorMessage(error: unknown, fallbackMessage: string) {
|
|
136
|
+
if (error && typeof error === 'object') {
|
|
137
|
+
const record = error as {
|
|
138
|
+
message?: string;
|
|
139
|
+
response?: {
|
|
140
|
+
data?: {
|
|
141
|
+
message?: string;
|
|
142
|
+
error?: string;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
record.response?.data?.message ||
|
|
149
|
+
record.response?.data?.error ||
|
|
150
|
+
record.message ||
|
|
151
|
+
fallbackMessage
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return fallbackMessage;
|
|
156
|
+
}
|
|
157
|
+
|
|
110
158
|
export default function RolePage() {
|
|
111
159
|
const t = useTranslations('core.RolePage');
|
|
112
|
-
const { request, currentLocaleCode, locales } = useApp();
|
|
160
|
+
const { request, currentLocaleCode, locales, getSettingValue } = useApp();
|
|
113
161
|
|
|
114
162
|
const [searchQuery, setSearchQuery] = useState('');
|
|
115
163
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
@@ -127,7 +175,7 @@ export default function RolePage() {
|
|
|
127
175
|
data: rolesResponse,
|
|
128
176
|
isLoading,
|
|
129
177
|
refetch,
|
|
130
|
-
} = useQuery<PaginatedResponse<
|
|
178
|
+
} = useQuery<PaginatedResponse<RoleListItem>>({
|
|
131
179
|
queryKey: ['roles', page, pageSize, searchQuery, currentLocaleCode],
|
|
132
180
|
queryFn: async () => {
|
|
133
181
|
const params = new URLSearchParams();
|
|
@@ -135,7 +183,7 @@ export default function RolePage() {
|
|
|
135
183
|
params.set('pageSize', String(pageSize));
|
|
136
184
|
if (searchQuery) params.set('search', searchQuery);
|
|
137
185
|
|
|
138
|
-
const response = await request<PaginatedResponse<
|
|
186
|
+
const response = await request<PaginatedResponse<RoleListItem>>({
|
|
139
187
|
url: `/role?${params.toString()}`,
|
|
140
188
|
method: 'GET',
|
|
141
189
|
});
|
|
@@ -177,23 +225,151 @@ export default function RolePage() {
|
|
|
177
225
|
},
|
|
178
226
|
});
|
|
179
227
|
|
|
228
|
+
const watchedCreateValues = useWatch({
|
|
229
|
+
control: form.control,
|
|
230
|
+
});
|
|
231
|
+
const watchedEditValues = useWatch({
|
|
232
|
+
control: editForm.control,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const {
|
|
236
|
+
clearDraft: clearCreateDraft,
|
|
237
|
+
loadDraft: loadCreateDraft,
|
|
238
|
+
hasDraft: hasCreateDraft,
|
|
239
|
+
savedAt: createDraftSavedAt,
|
|
240
|
+
} = useFormDraft<RoleDraftPayload>({
|
|
241
|
+
storageKey: ROLE_CREATE_DRAFT_STORAGE_KEY,
|
|
242
|
+
value: {
|
|
243
|
+
mode: 'create',
|
|
244
|
+
roleId: null,
|
|
245
|
+
localeCode: currentLocaleCode,
|
|
246
|
+
values: {
|
|
247
|
+
slug: watchedCreateValues.slug ?? '',
|
|
248
|
+
name: watchedCreateValues.name ?? '',
|
|
249
|
+
description: watchedCreateValues.description ?? '',
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
hasData: Boolean(
|
|
253
|
+
(watchedCreateValues.slug ?? '').trim() ||
|
|
254
|
+
(watchedCreateValues.name ?? '').trim() ||
|
|
255
|
+
(watchedCreateValues.description ?? '').trim()
|
|
256
|
+
),
|
|
257
|
+
enabled: isDialogOpen,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const {
|
|
261
|
+
clearDraft: clearEditDraft,
|
|
262
|
+
loadDraft: loadEditDraft,
|
|
263
|
+
hasDraft: hasEditDraft,
|
|
264
|
+
savedAt: editDraftSavedAt,
|
|
265
|
+
} = useFormDraft<RoleDraftPayload>({
|
|
266
|
+
storageKey: ROLE_EDIT_DRAFT_STORAGE_KEY,
|
|
267
|
+
value: {
|
|
268
|
+
mode: 'edit',
|
|
269
|
+
roleId: editingRole?.id ?? null,
|
|
270
|
+
localeCode: selectedLocale,
|
|
271
|
+
values: {
|
|
272
|
+
slug: watchedEditValues.slug ?? '',
|
|
273
|
+
name: watchedEditValues.name ?? '',
|
|
274
|
+
description: watchedEditValues.description ?? '',
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
hasData: Boolean(
|
|
278
|
+
(watchedEditValues.slug ?? '').trim() ||
|
|
279
|
+
(watchedEditValues.name ?? '').trim() ||
|
|
280
|
+
(watchedEditValues.description ?? '').trim()
|
|
281
|
+
),
|
|
282
|
+
enabled: Boolean(editingRole),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const createDraftStatusContent = useMemo(() => {
|
|
286
|
+
if (!hasCreateDraft || !createDraftSavedAt) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const savedDate = new Date(createDraftSavedAt);
|
|
291
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
296
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
297
|
+
addSuffix: true,
|
|
298
|
+
locale,
|
|
299
|
+
});
|
|
300
|
+
const absoluteLabel = formatDateTime(
|
|
301
|
+
savedDate,
|
|
302
|
+
getSettingValue,
|
|
303
|
+
currentLocaleCode
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
return currentLocaleCode.startsWith('pt')
|
|
307
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
308
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
309
|
+
}, [createDraftSavedAt, currentLocaleCode, getSettingValue, hasCreateDraft]);
|
|
310
|
+
|
|
311
|
+
const editDraftStatusContent = useMemo(() => {
|
|
312
|
+
if (!hasEditDraft || !editDraftSavedAt) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const savedDate = new Date(editDraftSavedAt);
|
|
317
|
+
if (Number.isNaN(savedDate.getTime())) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const locale = currentLocaleCode.startsWith('pt') ? ptBR : enUS;
|
|
322
|
+
const relativeLabel = formatDistanceToNow(savedDate, {
|
|
323
|
+
addSuffix: true,
|
|
324
|
+
locale,
|
|
325
|
+
});
|
|
326
|
+
const absoluteLabel = formatDateTime(
|
|
327
|
+
savedDate,
|
|
328
|
+
getSettingValue,
|
|
329
|
+
currentLocaleCode
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
return currentLocaleCode.startsWith('pt')
|
|
333
|
+
? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
|
|
334
|
+
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
335
|
+
}, [editDraftSavedAt, currentLocaleCode, getSettingValue, hasEditDraft]);
|
|
336
|
+
|
|
337
|
+
const openCreateDialog = () => {
|
|
338
|
+
const storedDraft = loadCreateDraft();
|
|
339
|
+
|
|
340
|
+
form.reset(
|
|
341
|
+
storedDraft?.payload.mode === 'create'
|
|
342
|
+
? storedDraft.payload.values
|
|
343
|
+
: {
|
|
344
|
+
slug: '',
|
|
345
|
+
name: '',
|
|
346
|
+
description: '',
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
setFormError(null);
|
|
350
|
+
setIsDialogOpen(true);
|
|
351
|
+
};
|
|
352
|
+
|
|
180
353
|
useEffect(() => {
|
|
181
354
|
if (editingRole && editingRole.locale && selectedLocale) {
|
|
355
|
+
const storedDraft = loadEditDraft();
|
|
356
|
+
const shouldRestoreDraft =
|
|
357
|
+
storedDraft?.payload.mode === 'edit' &&
|
|
358
|
+
storedDraft.payload.roleId === editingRole.id &&
|
|
359
|
+
storedDraft.payload.localeCode === selectedLocale;
|
|
182
360
|
const localeData = editingRole.locale[selectedLocale];
|
|
183
|
-
editForm.reset({
|
|
184
|
-
slug: editingRole.slug || '',
|
|
185
|
-
name: localeData?.name || '',
|
|
186
|
-
description: localeData?.description || '',
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}, [editingRole, selectedLocale]);
|
|
190
361
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
362
|
+
editForm.reset(
|
|
363
|
+
shouldRestoreDraft
|
|
364
|
+
? storedDraft.payload.values
|
|
365
|
+
: {
|
|
366
|
+
slug: editingRole.slug || '',
|
|
367
|
+
name: localeData?.name || '',
|
|
368
|
+
description: localeData?.description || '',
|
|
369
|
+
}
|
|
370
|
+
);
|
|
195
371
|
}
|
|
196
|
-
}, [editingRole
|
|
372
|
+
}, [editForm, editingRole, loadEditDraft, selectedLocale]);
|
|
197
373
|
|
|
198
374
|
const onSubmit = async (values: z.infer<typeof addRoleSchema>) => {
|
|
199
375
|
try {
|
|
@@ -212,23 +388,18 @@ export default function RolePage() {
|
|
|
212
388
|
},
|
|
213
389
|
});
|
|
214
390
|
|
|
391
|
+
clearCreateDraft();
|
|
215
392
|
form.reset();
|
|
216
393
|
refetch();
|
|
217
394
|
setIsDialogOpen(false);
|
|
218
395
|
setFormError(null);
|
|
219
396
|
toast.success(t('roleCreatedSuccess'));
|
|
220
|
-
} catch (err:
|
|
221
|
-
|
|
222
|
-
err?.response?.data?.message ||
|
|
223
|
-
err?.response?.data?.error ||
|
|
224
|
-
err?.message ||
|
|
225
|
-
t('serverError');
|
|
226
|
-
|
|
227
|
-
setFormError(String(msg));
|
|
397
|
+
} catch (err: unknown) {
|
|
398
|
+
setFormError(String(getErrorMessage(err, t('serverError'))));
|
|
228
399
|
}
|
|
229
400
|
};
|
|
230
401
|
|
|
231
|
-
const handleEdit = async (role:
|
|
402
|
+
const handleEdit = async (role: RoleListItem) => {
|
|
232
403
|
setEditFormError(null);
|
|
233
404
|
|
|
234
405
|
try {
|
|
@@ -240,7 +411,7 @@ export default function RolePage() {
|
|
|
240
411
|
const fullRole = response.data;
|
|
241
412
|
const localeData: Record<string, RoleLocale> = {};
|
|
242
413
|
if (fullRole.role_locale && Array.isArray(fullRole.role_locale)) {
|
|
243
|
-
fullRole.role_locale.forEach((rl
|
|
414
|
+
fullRole.role_locale.forEach((rl) => {
|
|
244
415
|
const localeCode = rl.locale?.code;
|
|
245
416
|
if (localeCode) {
|
|
246
417
|
localeData[localeCode] = {
|
|
@@ -260,6 +431,15 @@ export default function RolePage() {
|
|
|
260
431
|
}
|
|
261
432
|
});
|
|
262
433
|
|
|
434
|
+
const storedDraft = loadEditDraft();
|
|
435
|
+
const nextLocale =
|
|
436
|
+
storedDraft?.payload.mode === 'edit' &&
|
|
437
|
+
storedDraft.payload.roleId === fullRole.id
|
|
438
|
+
? storedDraft.payload.localeCode
|
|
439
|
+
: currentLocaleCode;
|
|
440
|
+
|
|
441
|
+
setSelectedLocale(nextLocale);
|
|
442
|
+
setActiveTab('basic-info');
|
|
263
443
|
setEditingRole({
|
|
264
444
|
...fullRole,
|
|
265
445
|
locale: localeData,
|
|
@@ -291,17 +471,13 @@ export default function RolePage() {
|
|
|
291
471
|
},
|
|
292
472
|
});
|
|
293
473
|
|
|
474
|
+
clearEditDraft();
|
|
294
475
|
toast.success(t('roleUpdatedSuccess'));
|
|
295
476
|
setEditFormError(null);
|
|
296
477
|
await refetch();
|
|
297
478
|
setEditingRole(null);
|
|
298
|
-
} catch (err:
|
|
299
|
-
|
|
300
|
-
err?.response?.data?.message ||
|
|
301
|
-
err?.response?.data?.error ||
|
|
302
|
-
err?.message ||
|
|
303
|
-
t('serverError');
|
|
304
|
-
setEditFormError(String(msg));
|
|
479
|
+
} catch (err: unknown) {
|
|
480
|
+
setEditFormError(String(getErrorMessage(err, t('serverError'))));
|
|
305
481
|
}
|
|
306
482
|
};
|
|
307
483
|
|
|
@@ -319,13 +495,8 @@ export default function RolePage() {
|
|
|
319
495
|
setEditingRole(null);
|
|
320
496
|
setEditFormError(null);
|
|
321
497
|
toast.success(t('roleDeletedSuccess'));
|
|
322
|
-
} catch (err:
|
|
323
|
-
|
|
324
|
-
err?.response?.data?.message ||
|
|
325
|
-
err?.response?.data?.error ||
|
|
326
|
-
err?.message ||
|
|
327
|
-
t('serverError');
|
|
328
|
-
setEditFormError(String(msg));
|
|
498
|
+
} catch (err: unknown) {
|
|
499
|
+
setEditFormError(String(getErrorMessage(err, t('serverError'))));
|
|
329
500
|
}
|
|
330
501
|
};
|
|
331
502
|
|
|
@@ -342,7 +513,7 @@ export default function RolePage() {
|
|
|
342
513
|
const localeData: Record<string, RoleLocale> = {};
|
|
343
514
|
|
|
344
515
|
if (fullRole.role_locale && Array.isArray(fullRole.role_locale)) {
|
|
345
|
-
fullRole.role_locale.forEach((rl
|
|
516
|
+
fullRole.role_locale.forEach((rl) => {
|
|
346
517
|
const localeCode = rl.locale?.code;
|
|
347
518
|
if (localeCode) {
|
|
348
519
|
localeData[localeCode] = {
|
|
@@ -378,7 +549,7 @@ export default function RolePage() {
|
|
|
378
549
|
actions={[
|
|
379
550
|
{
|
|
380
551
|
label: t('buttonAddRole'),
|
|
381
|
-
onClick:
|
|
552
|
+
onClick: openCreateDialog,
|
|
382
553
|
variant: 'default',
|
|
383
554
|
},
|
|
384
555
|
]}
|
|
@@ -433,71 +604,65 @@ export default function RolePage() {
|
|
|
433
604
|
title={t('noRolesFound')}
|
|
434
605
|
description={t('description')}
|
|
435
606
|
actionLabel={t('buttonAddRole')}
|
|
436
|
-
onAction={
|
|
607
|
+
onAction={openCreateDialog}
|
|
437
608
|
/>
|
|
438
609
|
) : (
|
|
439
610
|
<div className="grid gap-4 grid-cols-1 lg:grid-cols-2 xl:grid-cols-3">
|
|
440
|
-
{(rolesResponse?.data as
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
>
|
|
447
|
-
<
|
|
448
|
-
<div className="flex items-center
|
|
449
|
-
<
|
|
450
|
-
|
|
611
|
+
{(rolesResponse?.data as RoleListItem[])?.map((role) => (
|
|
612
|
+
<Card
|
|
613
|
+
key={String(role.role_id)}
|
|
614
|
+
onDoubleClick={() => handleEdit(role)}
|
|
615
|
+
className="cursor-pointer rounded-md flex flex-col justify-between gap-2 border border-border/60 bg-card p-4 shadow-sm transition hover:border-primary"
|
|
616
|
+
>
|
|
617
|
+
<CardHeader className="flex items-start justify-between gap-4 p-0">
|
|
618
|
+
<div className="flex items-center gap-3 flex-1">
|
|
619
|
+
<div className="h-12 w-12 shrink-0 rounded-full bg-primary/10 flex items-center justify-center">
|
|
620
|
+
<ShieldCheck className="h-6 w-6 text-primary" />
|
|
621
|
+
</div>
|
|
622
|
+
<div className="flex-1">
|
|
623
|
+
<CardTitle className="text-sm font-semibold">
|
|
624
|
+
{role.name}
|
|
625
|
+
</CardTitle>
|
|
626
|
+
<CardDescription className="text-xs text-muted-foreground">
|
|
627
|
+
{role.slug}
|
|
628
|
+
</CardDescription>
|
|
629
|
+
</div>
|
|
630
|
+
</div>
|
|
631
|
+
<Button
|
|
632
|
+
variant="outline"
|
|
633
|
+
size="sm"
|
|
634
|
+
onClick={() => handleEdit(role)}
|
|
635
|
+
>
|
|
636
|
+
{t('buttonEditRole')}
|
|
637
|
+
</Button>
|
|
638
|
+
</CardHeader>
|
|
639
|
+
{role.description && (
|
|
640
|
+
<CardContent className="p-0">
|
|
641
|
+
<p className="text-xs text-muted-foreground line-clamp-2">
|
|
642
|
+
{role.description}
|
|
643
|
+
</p>
|
|
644
|
+
<div className="text-xs line-clamp-2 flex gap-2 py-2">
|
|
645
|
+
<div>
|
|
646
|
+
{role.user_count ?? 0}{' '}
|
|
647
|
+
{(role.user_count ?? 0) === 1 ? t('user') : t('users')}
|
|
451
648
|
</div>
|
|
452
|
-
<div
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
<CardDescription className="text-xs text-muted-foreground">
|
|
457
|
-
{role.slug}
|
|
458
|
-
</CardDescription>
|
|
649
|
+
<div>•</div>
|
|
650
|
+
<div>
|
|
651
|
+
{role.menu_count ?? 0}{' '}
|
|
652
|
+
{(role.menu_count ?? 0) === 1 ? t('menu') : t('menus')}
|
|
459
653
|
</div>
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
{t('buttonEditRole')}
|
|
467
|
-
</Button>
|
|
468
|
-
</CardHeader>
|
|
469
|
-
{role.description && (
|
|
470
|
-
<CardContent className="p-0">
|
|
471
|
-
<p className="text-xs text-muted-foreground line-clamp-2">
|
|
472
|
-
{role.description}
|
|
473
|
-
</p>
|
|
474
|
-
<div className="text-xs line-clamp-2 flex gap-2 py-2">
|
|
475
|
-
<div>
|
|
476
|
-
{(role as any).user_count}{' '}
|
|
477
|
-
{(role as any).user_count === 1
|
|
478
|
-
? t('user')
|
|
479
|
-
: t('users')}
|
|
480
|
-
</div>
|
|
481
|
-
<div>•</div>
|
|
482
|
-
<div>
|
|
483
|
-
{(role as any).menu_count}{' '}
|
|
484
|
-
{(role as any).menu_count === 1
|
|
485
|
-
? t('menu')
|
|
486
|
-
: t('menus')}
|
|
487
|
-
</div>
|
|
488
|
-
<div>•</div>
|
|
489
|
-
<div>
|
|
490
|
-
{(role as any).route_count}{' '}
|
|
491
|
-
{(role as any).route_count === 1
|
|
492
|
-
? t('route')
|
|
493
|
-
: t('routes')}
|
|
494
|
-
</div>
|
|
654
|
+
<div>•</div>
|
|
655
|
+
<div>
|
|
656
|
+
{role.route_count ?? 0}{' '}
|
|
657
|
+
{(role.route_count ?? 0) === 1
|
|
658
|
+
? t('route')
|
|
659
|
+
: t('routes')}
|
|
495
660
|
</div>
|
|
496
|
-
</
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
)}
|
|
661
|
+
</div>
|
|
662
|
+
</CardContent>
|
|
663
|
+
)}
|
|
664
|
+
</Card>
|
|
665
|
+
))}
|
|
501
666
|
</div>
|
|
502
667
|
)}
|
|
503
668
|
|
|
@@ -515,7 +680,15 @@ export default function RolePage() {
|
|
|
515
680
|
/>
|
|
516
681
|
</div>
|
|
517
682
|
|
|
518
|
-
<Sheet
|
|
683
|
+
<Sheet
|
|
684
|
+
open={isDialogOpen}
|
|
685
|
+
onOpenChange={(open) => {
|
|
686
|
+
setIsDialogOpen(open);
|
|
687
|
+
if (!open) {
|
|
688
|
+
setFormError(null);
|
|
689
|
+
}
|
|
690
|
+
}}
|
|
691
|
+
>
|
|
519
692
|
<SheetContent className="w-full sm:max-w-lg overflow-y-auto gap-0">
|
|
520
693
|
<SheetHeader>
|
|
521
694
|
<SheetTitle>{t('dialogAddRoleTitle')}</SheetTitle>
|
|
@@ -591,6 +764,12 @@ export default function RolePage() {
|
|
|
591
764
|
</Alert>
|
|
592
765
|
)}
|
|
593
766
|
|
|
767
|
+
{createDraftStatusContent ? (
|
|
768
|
+
<p className="text-xs text-muted-foreground">
|
|
769
|
+
{createDraftStatusContent}
|
|
770
|
+
</p>
|
|
771
|
+
) : null}
|
|
772
|
+
|
|
594
773
|
<Button type="submit" className="w-full">
|
|
595
774
|
{t('buttonAddRole')}
|
|
596
775
|
</Button>
|
|
@@ -671,7 +850,7 @@ export default function RolePage() {
|
|
|
671
850
|
<SelectValue placeholder={t('selectLocale')} />
|
|
672
851
|
</SelectTrigger>
|
|
673
852
|
<SelectContent>
|
|
674
|
-
{locales?.map((locale:
|
|
853
|
+
{locales?.map((locale: Locale) => (
|
|
675
854
|
<SelectItem key={locale.code} value={locale.code}>
|
|
676
855
|
{locale.name}
|
|
677
856
|
</SelectItem>
|
|
@@ -740,6 +919,11 @@ export default function RolePage() {
|
|
|
740
919
|
)}
|
|
741
920
|
|
|
742
921
|
<div className="flex flex-col w-full gap-2 pt-2">
|
|
922
|
+
{editDraftStatusContent ? (
|
|
923
|
+
<p className="text-xs text-muted-foreground">
|
|
924
|
+
{editDraftStatusContent}
|
|
925
|
+
</p>
|
|
926
|
+
) : null}
|
|
743
927
|
<Button type="submit" className="w-full">
|
|
744
928
|
{t('saveChanges')}
|
|
745
929
|
</Button>
|