@hed-hog/core 0.0.237 → 0.0.238
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/dist/dashboard/dashboard/dashboard.controller.d.ts +102 -0
- package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard/dashboard.service.d.ts +102 -0
- package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard/dashboard.service.js +26 -0
- package/dist/dashboard/dashboard/dashboard.service.js.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component/dashboard-component.service.js +39 -0
- package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +7 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.js +11 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +7 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +31 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dto/create-batch.dto.d.ts +5 -0
- package/dist/dashboard/dashboard-component-role/dto/create-batch.dto.d.ts.map +1 -0
- package/dist/dashboard/dashboard-component-role/dto/create-batch.dto.js +27 -0
- package/dist/dashboard/dashboard-component-role/dto/create-batch.dto.js.map +1 -0
- package/dist/dashboard/dashboard-component-role/dto/index.d.ts +1 -0
- package/dist/dashboard/dashboard-component-role/dto/index.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dto/index.js +1 -0
- package/dist/dashboard/dashboard-component-role/dto/index.js.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +7 -1
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.controller.js +11 -0
- package/dist/dashboard/dashboard-role/dashboard-role.controller.js.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +7 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.js +31 -0
- package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-role/dto/create-batch.dto.d.ts +5 -0
- package/dist/dashboard/dashboard-role/dto/create-batch.dto.d.ts.map +1 -0
- package/dist/dashboard/dashboard-role/dto/create-batch.dto.js +27 -0
- package/dist/dashboard/dashboard-role/dto/create-batch.dto.js.map +1 -0
- package/dist/dashboard/dashboard-role/dto/index.d.ts +1 -0
- package/dist/dashboard/dashboard-role/dto/index.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dto/index.js +1 -0
- package/dist/dashboard/dashboard-role/dto/index.js.map +1 -1
- package/dist/file/file.controller.d.ts +4 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +47 -8
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +1 -0
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +7 -0
- package/dist/file/file.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +1 -0
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +13 -0
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +1 -0
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +20 -0
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/route.yaml +24 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +21 -3
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +344 -5
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +358 -2
- package/hedhog/frontend/messages/en.json +19 -0
- package/hedhog/frontend/messages/pt.json +19 -0
- package/package.json +4 -4
- package/src/dashboard/dashboard/dashboard.service.ts +26 -0
- package/src/dashboard/dashboard-component/dashboard-component.service.ts +39 -0
- package/src/dashboard/dashboard-component-role/dashboard-component-role.controller.ts +12 -1
- package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +44 -1
- package/src/dashboard/dashboard-component-role/dto/create-batch.dto.ts +11 -0
- package/src/dashboard/dashboard-component-role/dto/index.ts +2 -0
- package/src/dashboard/dashboard-role/dashboard-role.controller.ts +22 -11
- package/src/dashboard/dashboard-role/dashboard-role.service.ts +48 -5
- package/src/dashboard/dashboard-role/dto/create-batch.dto.ts +11 -0
- package/src/dashboard/dashboard-role/dto/index.ts +2 -0
- package/src/file/file.controller.ts +50 -7
- package/src/file/file.service.ts +10 -0
- package/src/user/user.controller.ts +12 -1
- package/src/user/user.service.ts +32 -5
package/hedhog/data/route.yaml
CHANGED
|
@@ -446,12 +446,24 @@
|
|
|
446
446
|
role:
|
|
447
447
|
- where:
|
|
448
448
|
slug: admin
|
|
449
|
+
- url: /file/open/:token
|
|
450
|
+
method: GET
|
|
451
|
+
relations:
|
|
452
|
+
role:
|
|
453
|
+
- where:
|
|
454
|
+
slug: admin
|
|
449
455
|
- url: /file/download/:token
|
|
450
456
|
method: GET
|
|
451
457
|
relations:
|
|
452
458
|
role:
|
|
453
459
|
- where:
|
|
454
460
|
slug: admin
|
|
461
|
+
- url: /file/open/:id
|
|
462
|
+
method: PUT
|
|
463
|
+
relations:
|
|
464
|
+
role:
|
|
465
|
+
- where:
|
|
466
|
+
slug: admin
|
|
455
467
|
- url: /file/download/:id
|
|
456
468
|
method: PUT
|
|
457
469
|
relations:
|
|
@@ -766,6 +778,12 @@
|
|
|
766
778
|
role:
|
|
767
779
|
- where:
|
|
768
780
|
slug: admin
|
|
781
|
+
- url: /dashboard-role/batch
|
|
782
|
+
method: POST
|
|
783
|
+
relations:
|
|
784
|
+
role:
|
|
785
|
+
- where:
|
|
786
|
+
slug: admin
|
|
769
787
|
- url: /dashboard-role/:id
|
|
770
788
|
method: DELETE
|
|
771
789
|
relations:
|
|
@@ -790,6 +808,12 @@
|
|
|
790
808
|
role:
|
|
791
809
|
- where:
|
|
792
810
|
slug: admin
|
|
811
|
+
- url: /dashboard-component-role/batch
|
|
812
|
+
method: POST
|
|
813
|
+
relations:
|
|
814
|
+
role:
|
|
815
|
+
- where:
|
|
816
|
+
slug: admin
|
|
793
817
|
- url: /dashboard-component-role/:id
|
|
794
818
|
method: DELETE
|
|
795
819
|
relations:
|
|
@@ -298,10 +298,28 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
298
298
|
},
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
+
const fileId = response.data?.id;
|
|
302
|
+
|
|
303
|
+
if (!fileId) {
|
|
304
|
+
throw new Error('File ID not returned');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const openResponse: any = await request({
|
|
308
|
+
url: `/file/open/${fileId}`,
|
|
309
|
+
method: 'PUT',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const openUrl = openResponse?.data?.url;
|
|
313
|
+
|
|
314
|
+
if (!openUrl) {
|
|
315
|
+
throw new Error('Temporary open URL not returned');
|
|
316
|
+
}
|
|
317
|
+
|
|
301
318
|
const fileUrl =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
319
|
+
typeof openUrl === 'string' && openUrl.startsWith('http')
|
|
320
|
+
? openUrl
|
|
321
|
+
: `${String(process.env.NEXT_PUBLIC_API_BASE_URL || '')}${openUrl}`;
|
|
322
|
+
|
|
305
323
|
handleChangeValue(formatValue(fileUrl));
|
|
306
324
|
showToastHandler('success', t('fileUploaded'));
|
|
307
325
|
} catch (error) {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
AlertDialogHeader,
|
|
11
11
|
AlertDialogTitle,
|
|
12
12
|
} from '@/components/ui/alert-dialog';
|
|
13
|
+
import { Badge } from '@/components/ui/badge';
|
|
13
14
|
import { Button } from '@/components/ui/button';
|
|
14
15
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
15
16
|
import {
|
|
@@ -30,6 +31,14 @@ import {
|
|
|
30
31
|
SelectTrigger,
|
|
31
32
|
SelectValue,
|
|
32
33
|
} from '@/components/ui/select';
|
|
34
|
+
import {
|
|
35
|
+
Sheet,
|
|
36
|
+
SheetContent,
|
|
37
|
+
SheetDescription,
|
|
38
|
+
SheetFooter,
|
|
39
|
+
SheetHeader,
|
|
40
|
+
SheetTitle,
|
|
41
|
+
} from '@/components/ui/sheet';
|
|
33
42
|
import {
|
|
34
43
|
Table,
|
|
35
44
|
TableBody,
|
|
@@ -38,6 +47,11 @@ import {
|
|
|
38
47
|
TableHeader,
|
|
39
48
|
TableRow,
|
|
40
49
|
} from '@/components/ui/table';
|
|
50
|
+
import {
|
|
51
|
+
Tooltip,
|
|
52
|
+
TooltipContent,
|
|
53
|
+
TooltipTrigger,
|
|
54
|
+
} from '@/components/ui/tooltip';
|
|
41
55
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
42
56
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
43
57
|
import * as TablerIcons from '@tabler/icons-react';
|
|
@@ -50,6 +64,7 @@ import {
|
|
|
50
64
|
IconGlobe,
|
|
51
65
|
IconPlus,
|
|
52
66
|
IconTrash,
|
|
67
|
+
IconX,
|
|
53
68
|
} from '@tabler/icons-react';
|
|
54
69
|
import { useTranslations } from 'next-intl';
|
|
55
70
|
import { useState } from 'react';
|
|
@@ -74,6 +89,25 @@ interface Component {
|
|
|
74
89
|
name: string;
|
|
75
90
|
description?: string;
|
|
76
91
|
}>;
|
|
92
|
+
dashboard_component_role?: Array<{
|
|
93
|
+
role_id?: number;
|
|
94
|
+
role: {
|
|
95
|
+
id?: number;
|
|
96
|
+
role_id?: number;
|
|
97
|
+
slug: string;
|
|
98
|
+
role_locale?: Array<{
|
|
99
|
+
locale: { code: string };
|
|
100
|
+
name: string;
|
|
101
|
+
}>;
|
|
102
|
+
};
|
|
103
|
+
}>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface RoleOption {
|
|
107
|
+
id?: number;
|
|
108
|
+
role_id?: number;
|
|
109
|
+
slug: string;
|
|
110
|
+
name?: string;
|
|
77
111
|
}
|
|
78
112
|
|
|
79
113
|
export function ComponentsTab() {
|
|
@@ -87,9 +121,20 @@ export function ComponentsTab() {
|
|
|
87
121
|
const [componentToDelete, setComponentToDelete] = useState<number | null>(
|
|
88
122
|
null
|
|
89
123
|
);
|
|
124
|
+
const [removeRoleDialogOpen, setRemoveRoleDialogOpen] = useState(false);
|
|
125
|
+
const [roleToRemove, setRoleToRemove] = useState<{
|
|
126
|
+
componentId: number;
|
|
127
|
+
roleId: number;
|
|
128
|
+
roleName: string;
|
|
129
|
+
} | null>(null);
|
|
130
|
+
const [roleSheetOpen, setRoleSheetOpen] = useState(false);
|
|
131
|
+
const [componentForRoleSheet, setComponentForRoleSheet] =
|
|
132
|
+
useState<Component | null>(null);
|
|
133
|
+
const [selectedRoleIds, setSelectedRoleIds] = useState<number[]>([]);
|
|
90
134
|
const [page, setPage] = useState(1);
|
|
91
135
|
const [pageSize, setPageSize] = useState(10);
|
|
92
136
|
const [searchQuery, setSearchQuery] = useState('');
|
|
137
|
+
const [roleSearchQuery, setRoleSearchQuery] = useState('');
|
|
93
138
|
const debouncedSearch = useDebounce(searchQuery, 500);
|
|
94
139
|
|
|
95
140
|
const renderIcon = (iconName?: string) => {
|
|
@@ -145,7 +190,23 @@ export function ComponentsTab() {
|
|
|
145
190
|
},
|
|
146
191
|
});
|
|
147
192
|
|
|
193
|
+
const { data: rolesData, isLoading: isLoadingRoles } = useQuery<{
|
|
194
|
+
data: RoleOption[];
|
|
195
|
+
total: number;
|
|
196
|
+
}>({
|
|
197
|
+
queryKey: ['roles-for-component-sheet', currentLocaleCode],
|
|
198
|
+
queryFn: async () => {
|
|
199
|
+
const response = await request<{ data: RoleOption[]; total: number }>({
|
|
200
|
+
url: '/role?page=1&pageSize=1000',
|
|
201
|
+
method: 'GET',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return response.data;
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
148
208
|
const components = paginatedData?.data ?? [];
|
|
209
|
+
const availableRoles = rolesData?.data ?? [];
|
|
149
210
|
const total = paginatedData?.total ?? 0;
|
|
150
211
|
const totalPages = Math.ceil(total / pageSize);
|
|
151
212
|
|
|
@@ -291,6 +352,81 @@ export function ComponentsTab() {
|
|
|
291
352
|
}
|
|
292
353
|
};
|
|
293
354
|
|
|
355
|
+
const handleOpenRoleSheet = (component: Component) => {
|
|
356
|
+
setComponentForRoleSheet(component);
|
|
357
|
+
setSelectedRoleIds([]);
|
|
358
|
+
setRoleSheetOpen(true);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const handleRoleSelection = (roleId: number, checked: boolean) => {
|
|
362
|
+
setSelectedRoleIds((prev) => {
|
|
363
|
+
if (checked) {
|
|
364
|
+
if (prev.includes(roleId)) return prev;
|
|
365
|
+
return [...prev, roleId];
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return prev.filter((id) => id !== roleId);
|
|
369
|
+
});
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const handleAddRolesToComponent = async () => {
|
|
373
|
+
if (!componentForRoleSheet) return;
|
|
374
|
+
|
|
375
|
+
if (selectedRoleIds.length === 0) {
|
|
376
|
+
toast.error(t('selectRoleRequired'));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
await request({
|
|
382
|
+
url: '/dashboard-component-role/batch',
|
|
383
|
+
method: 'POST',
|
|
384
|
+
data: {
|
|
385
|
+
component_id: componentForRoleSheet.id,
|
|
386
|
+
role_ids: selectedRoleIds,
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
toast.success(t('roleAdded'));
|
|
391
|
+
setRoleSheetOpen(false);
|
|
392
|
+
setComponentForRoleSheet(null);
|
|
393
|
+
setSelectedRoleIds([]);
|
|
394
|
+
refetch();
|
|
395
|
+
} catch (error: any) {
|
|
396
|
+
console.error('Erro ao adicionar permissões ao componente:', error);
|
|
397
|
+
toast.error(t('errorAddingRole'));
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const handleRoleRemoveClick = (
|
|
402
|
+
componentId: number,
|
|
403
|
+
roleId: number,
|
|
404
|
+
roleName: string
|
|
405
|
+
) => {
|
|
406
|
+
setRoleToRemove({ componentId, roleId, roleName });
|
|
407
|
+
setRemoveRoleDialogOpen(true);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const handleRoleRemoveConfirm = async () => {
|
|
411
|
+
if (!roleToRemove) return;
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
await request({
|
|
415
|
+
url: `/dashboard-component-role/component/${roleToRemove.componentId}/role/${roleToRemove.roleId}`,
|
|
416
|
+
method: 'DELETE',
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
toast.success(t('roleDeleted'));
|
|
420
|
+
refetch();
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error('Erro ao remover permissão do componente:', error);
|
|
423
|
+
toast.error(t('errorDeletingRole'));
|
|
424
|
+
} finally {
|
|
425
|
+
setRemoveRoleDialogOpen(false);
|
|
426
|
+
setRoleToRemove(null);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
294
430
|
return (
|
|
295
431
|
<div className="space-y-4">
|
|
296
432
|
<div className="flex justify-between items-center">
|
|
@@ -581,16 +717,16 @@ export function ComponentsTab() {
|
|
|
581
717
|
<TableRow>
|
|
582
718
|
<TableHead>{t('slug')}</TableHead>
|
|
583
719
|
<TableHead>{t('name')}</TableHead>
|
|
584
|
-
<TableHead>{t('descriptionLabel')}</TableHead>
|
|
585
720
|
<TableHead>{t('size')}</TableHead>
|
|
586
721
|
<TableHead>{t('resizable')}</TableHead>
|
|
722
|
+
<TableHead>{t('role')}</TableHead>
|
|
587
723
|
<TableHead className="w-[100px]">{t('actions')}</TableHead>
|
|
588
724
|
</TableRow>
|
|
589
725
|
</TableHeader>
|
|
590
726
|
<TableBody>
|
|
591
727
|
{isLoading ? (
|
|
592
728
|
<TableRow>
|
|
593
|
-
<TableCell colSpan={
|
|
729
|
+
<TableCell colSpan={6} className="text-center">
|
|
594
730
|
{t('loading')}
|
|
595
731
|
</TableCell>
|
|
596
732
|
</TableRow>
|
|
@@ -605,19 +741,111 @@ export function ComponentsTab() {
|
|
|
605
741
|
(l) => l.locale.code === currentLocaleCode
|
|
606
742
|
)?.description;
|
|
607
743
|
|
|
744
|
+
const roleEntries = (
|
|
745
|
+
component.dashboard_component_role?.map((componentRole) => {
|
|
746
|
+
const roleId =
|
|
747
|
+
componentRole.role_id ??
|
|
748
|
+
componentRole.role.role_id ??
|
|
749
|
+
componentRole.role.id;
|
|
750
|
+
|
|
751
|
+
if (!roleId) return null;
|
|
752
|
+
|
|
753
|
+
const localeRoleName = componentRole.role.role_locale?.find(
|
|
754
|
+
(roleLocale) =>
|
|
755
|
+
roleLocale.locale.code === currentLocaleCode
|
|
756
|
+
)?.name;
|
|
757
|
+
|
|
758
|
+
return {
|
|
759
|
+
id: roleId,
|
|
760
|
+
name: localeRoleName || componentRole.role.slug,
|
|
761
|
+
};
|
|
762
|
+
}) || []
|
|
763
|
+
).filter(
|
|
764
|
+
(
|
|
765
|
+
roleEntry,
|
|
766
|
+
index,
|
|
767
|
+
array
|
|
768
|
+
): roleEntry is { id: number; name: string } =>
|
|
769
|
+
!!roleEntry &&
|
|
770
|
+
array.findIndex((item) => item?.id === roleEntry.id) ===
|
|
771
|
+
index
|
|
772
|
+
);
|
|
773
|
+
|
|
608
774
|
return (
|
|
609
775
|
<TableRow key={component.id}>
|
|
610
776
|
<TableCell className="font-mono">
|
|
611
777
|
{component.slug}
|
|
612
778
|
</TableCell>
|
|
613
|
-
<TableCell>
|
|
614
|
-
|
|
779
|
+
<TableCell>
|
|
780
|
+
<div className="flex flex-col">
|
|
781
|
+
<span>{name}</span>
|
|
782
|
+
{description && (
|
|
783
|
+
<span className="text-muted-foreground text-xs">
|
|
784
|
+
{description}
|
|
785
|
+
</span>
|
|
786
|
+
)}
|
|
787
|
+
</div>
|
|
788
|
+
</TableCell>
|
|
615
789
|
<TableCell>
|
|
616
790
|
{component.width}x{component.height}
|
|
617
791
|
</TableCell>
|
|
618
792
|
<TableCell>
|
|
619
793
|
{component.is_resizable ? t('yes') : t('no')}
|
|
620
794
|
</TableCell>
|
|
795
|
+
<TableCell>
|
|
796
|
+
<div className="flex flex-wrap items-center gap-1">
|
|
797
|
+
{roleEntries.length > 0 ? (
|
|
798
|
+
roleEntries.map((roleEntry) => (
|
|
799
|
+
<Badge
|
|
800
|
+
key={`${component.id}-${roleEntry.id}`}
|
|
801
|
+
variant="outline"
|
|
802
|
+
className="gap-1 pr-1"
|
|
803
|
+
>
|
|
804
|
+
<span>{roleEntry.name}</span>
|
|
805
|
+
<Tooltip>
|
|
806
|
+
<TooltipTrigger asChild>
|
|
807
|
+
<button
|
|
808
|
+
type="button"
|
|
809
|
+
className="rounded-sm p-0.5 hover:bg-muted"
|
|
810
|
+
onClick={() =>
|
|
811
|
+
handleRoleRemoveClick(
|
|
812
|
+
component.id,
|
|
813
|
+
roleEntry.id,
|
|
814
|
+
roleEntry.name
|
|
815
|
+
)
|
|
816
|
+
}
|
|
817
|
+
aria-label={`${t('delete')} ${roleEntry.name}`}
|
|
818
|
+
>
|
|
819
|
+
<IconX className="size-3" />
|
|
820
|
+
</button>
|
|
821
|
+
</TooltipTrigger>
|
|
822
|
+
<TooltipContent>
|
|
823
|
+
{`${t('delete')} ${roleEntry.name}`}
|
|
824
|
+
</TooltipContent>
|
|
825
|
+
</Tooltip>
|
|
826
|
+
</Badge>
|
|
827
|
+
))
|
|
828
|
+
) : (
|
|
829
|
+
<span className="text-muted-foreground text-xs mr-1">
|
|
830
|
+
{t('noRoles')}
|
|
831
|
+
</span>
|
|
832
|
+
)}
|
|
833
|
+
<Tooltip>
|
|
834
|
+
<TooltipTrigger asChild>
|
|
835
|
+
<Button
|
|
836
|
+
type="button"
|
|
837
|
+
size="icon"
|
|
838
|
+
variant="outline"
|
|
839
|
+
className="size-6"
|
|
840
|
+
onClick={() => handleOpenRoleSheet(component)}
|
|
841
|
+
>
|
|
842
|
+
<IconPlus className="size-3" />
|
|
843
|
+
</Button>
|
|
844
|
+
</TooltipTrigger>
|
|
845
|
+
<TooltipContent>{t('addRole')}</TooltipContent>
|
|
846
|
+
</Tooltip>
|
|
847
|
+
</div>
|
|
848
|
+
</TableCell>
|
|
621
849
|
<TableCell>
|
|
622
850
|
<div className="flex gap-2">
|
|
623
851
|
<Button
|
|
@@ -641,7 +869,7 @@ export function ComponentsTab() {
|
|
|
641
869
|
})
|
|
642
870
|
) : (
|
|
643
871
|
<TableRow>
|
|
644
|
-
<TableCell colSpan={
|
|
872
|
+
<TableCell colSpan={6} className="text-center">
|
|
645
873
|
{t('noComponents')}
|
|
646
874
|
</TableCell>
|
|
647
875
|
</TableRow>
|
|
@@ -748,6 +976,117 @@ export function ComponentsTab() {
|
|
|
748
976
|
</AlertDialogFooter>
|
|
749
977
|
</AlertDialogContent>
|
|
750
978
|
</AlertDialog>
|
|
979
|
+
|
|
980
|
+
<Dialog
|
|
981
|
+
open={removeRoleDialogOpen}
|
|
982
|
+
onOpenChange={setRemoveRoleDialogOpen}
|
|
983
|
+
>
|
|
984
|
+
<DialogContent className="max-w-md">
|
|
985
|
+
<DialogHeader>
|
|
986
|
+
<DialogTitle>{t('confirmDelete')}</DialogTitle>
|
|
987
|
+
<DialogDescription>
|
|
988
|
+
{roleToRemove
|
|
989
|
+
? `${t('deleteDescription')} (${roleToRemove.roleName})`
|
|
990
|
+
: t('deleteDescription')}
|
|
991
|
+
</DialogDescription>
|
|
992
|
+
</DialogHeader>
|
|
993
|
+
<DialogFooter>
|
|
994
|
+
<Button
|
|
995
|
+
variant="outline"
|
|
996
|
+
onClick={() => setRemoveRoleDialogOpen(false)}
|
|
997
|
+
>
|
|
998
|
+
{t('cancel')}
|
|
999
|
+
</Button>
|
|
1000
|
+
<Button variant="destructive" onClick={handleRoleRemoveConfirm}>
|
|
1001
|
+
{t('delete')}
|
|
1002
|
+
</Button>
|
|
1003
|
+
</DialogFooter>
|
|
1004
|
+
</DialogContent>
|
|
1005
|
+
</Dialog>
|
|
1006
|
+
|
|
1007
|
+
<Sheet
|
|
1008
|
+
open={roleSheetOpen}
|
|
1009
|
+
onOpenChange={(openValue) => {
|
|
1010
|
+
setRoleSheetOpen(openValue);
|
|
1011
|
+
if (!openValue) {
|
|
1012
|
+
setComponentForRoleSheet(null);
|
|
1013
|
+
setSelectedRoleIds([]);
|
|
1014
|
+
}
|
|
1015
|
+
}}
|
|
1016
|
+
>
|
|
1017
|
+
<SheetContent side="right" className="sm:max-w-lg overflow-y-auto">
|
|
1018
|
+
<SheetHeader>
|
|
1019
|
+
<SheetTitle>{t('addRole')}</SheetTitle>
|
|
1020
|
+
<SheetDescription>
|
|
1021
|
+
{componentForRoleSheet
|
|
1022
|
+
? `${t('component')}: ${componentForRoleSheet.slug}`
|
|
1023
|
+
: t('manageComponentRoles')}
|
|
1024
|
+
</SheetDescription>
|
|
1025
|
+
</SheetHeader>
|
|
1026
|
+
|
|
1027
|
+
<div className="space-y-3 py-4">
|
|
1028
|
+
{isLoadingRoles ? (
|
|
1029
|
+
<p className="text-sm text-muted-foreground">{t('loading')}</p>
|
|
1030
|
+
) : availableRoles.length > 0 ? (
|
|
1031
|
+
availableRoles.map((role) => {
|
|
1032
|
+
const roleId = role.role_id ?? role.id;
|
|
1033
|
+
if (!roleId) return null;
|
|
1034
|
+
|
|
1035
|
+
const roleName = role.name || role.slug;
|
|
1036
|
+
const alreadyLinked =
|
|
1037
|
+
componentForRoleSheet?.dashboard_component_role?.some(
|
|
1038
|
+
(componentRole) =>
|
|
1039
|
+
(componentRole.role_id ??
|
|
1040
|
+
componentRole.role.role_id ??
|
|
1041
|
+
componentRole.role.id) === roleId
|
|
1042
|
+
) ?? false;
|
|
1043
|
+
|
|
1044
|
+
const isSelected = selectedRoleIds.includes(roleId);
|
|
1045
|
+
|
|
1046
|
+
return (
|
|
1047
|
+
<div
|
|
1048
|
+
key={`available-role-${roleId}`}
|
|
1049
|
+
className={`flex items-center gap-3 rounded-md border p-3 transition-colors ${
|
|
1050
|
+
alreadyLinked
|
|
1051
|
+
? 'cursor-not-allowed opacity-70'
|
|
1052
|
+
: 'cursor-pointer hover:bg-muted/40'
|
|
1053
|
+
} ${isSelected ? 'border-primary bg-primary/5' : ''}`}
|
|
1054
|
+
onClick={() => {
|
|
1055
|
+
if (alreadyLinked) return;
|
|
1056
|
+
handleRoleSelection(roleId, !isSelected);
|
|
1057
|
+
}}
|
|
1058
|
+
>
|
|
1059
|
+
<div onClick={(event) => event.stopPropagation()}>
|
|
1060
|
+
<Checkbox
|
|
1061
|
+
checked={alreadyLinked || isSelected}
|
|
1062
|
+
onCheckedChange={(checked) =>
|
|
1063
|
+
handleRoleSelection(roleId, checked === true)
|
|
1064
|
+
}
|
|
1065
|
+
disabled={alreadyLinked}
|
|
1066
|
+
/>
|
|
1067
|
+
</div>
|
|
1068
|
+
<div className="min-w-0">
|
|
1069
|
+
<p className="text-sm font-medium truncate">{roleName}</p>
|
|
1070
|
+
<p className="text-xs text-muted-foreground truncate">
|
|
1071
|
+
{role.slug}
|
|
1072
|
+
</p>
|
|
1073
|
+
</div>
|
|
1074
|
+
</div>
|
|
1075
|
+
);
|
|
1076
|
+
})
|
|
1077
|
+
) : (
|
|
1078
|
+
<p className="text-sm text-muted-foreground">{t('noRoles')}</p>
|
|
1079
|
+
)}
|
|
1080
|
+
</div>
|
|
1081
|
+
|
|
1082
|
+
<SheetFooter>
|
|
1083
|
+
<Button variant="outline" onClick={() => setRoleSheetOpen(false)}>
|
|
1084
|
+
{t('cancel')}
|
|
1085
|
+
</Button>
|
|
1086
|
+
<Button onClick={handleAddRolesToComponent}>{t('save')}</Button>
|
|
1087
|
+
</SheetFooter>
|
|
1088
|
+
</SheetContent>
|
|
1089
|
+
</Sheet>
|
|
751
1090
|
</div>
|
|
752
1091
|
);
|
|
753
1092
|
}
|