@hed-hog/core 0.0.361 → 0.0.364
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/access-log/access-log.controller.d.ts +1 -1
- package/dist/access-log/access-log.service.d.ts +1 -1
- package/dist/access-log/access-log.service.d.ts.map +1 -1
- package/dist/access-log/access-log.service.js +43 -1
- package/dist/access-log/access-log.service.js.map +1 -1
- package/dist/auth/auth.controller.d.ts +1 -0
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +6 -2
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.service.d.ts +1 -0
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +15 -5
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/guards/auth.guard.d.ts +1 -0
- package/dist/auth/guards/auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/auth.guard.js +11 -4
- package/dist/auth/guards/auth.guard.js.map +1 -1
- package/dist/file/file.module.d.ts.map +1 -1
- package/dist/file/file.module.js +3 -2
- package/dist/file/file.module.js.map +1 -1
- package/dist/file/file.service.d.ts +71 -1
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +180 -32
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/image-optimization.service.d.ts +20 -0
- package/dist/file/image-optimization.service.d.ts.map +1 -0
- package/dist/file/image-optimization.service.js +126 -0
- package/dist/file/image-optimization.service.js.map +1 -0
- package/dist/file/provider/local.provider.d.ts +1 -1
- package/dist/file/provider/local.provider.d.ts.map +1 -1
- package/dist/file/provider/local.provider.js +2 -9
- package/dist/file/provider/local.provider.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/core-users.mcp-tools.d.ts +1 -1
- package/dist/mcp/tools/core-users.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-users.mcp-tools.js +2 -2
- package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +44 -23
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +7 -2
- package/dist/session/session.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 -6
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +1 -1
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +4 -3
- package/dist/user/user.controller.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 +6 -6
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/route.yaml +2 -0
- package/hedhog/data/setting_group.yaml +117 -0
- package/hedhog/data/setting_subgroup.yaml +8 -0
- package/hedhog/frontend/app/access-log/page.tsx.ejs +170 -11
- package/hedhog/frontend/app/account/components/remove-mfa-dialog.tsx.ejs +1 -1
- package/hedhog/frontend/app/account/components/verify-before-add-dialog.tsx.ejs +1 -1
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +112 -5
- package/hedhog/frontend/app/users/page.tsx.ejs +7 -7
- package/hedhog/frontend/app/users/permissions.tsx.ejs +137 -83
- package/hedhog/frontend/app/users/user-access-sheet.tsx.ejs +28 -26
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +207 -75
- package/hedhog/frontend/messages/en.json +9 -2
- package/hedhog/frontend/messages/pt.json +9 -2
- package/package.json +9 -4
- package/src/access-log/access-log.service.ts +19 -1
- package/src/auth/auth.controller.ts +5 -0
- package/src/auth/auth.service.ts +14 -4
- package/src/auth/guards/auth.guard.ts +10 -3
- package/src/file/file.module.ts +3 -2
- package/src/file/file.service.ts +361 -47
- package/src/file/image-optimization.service.ts +123 -0
- package/src/file/provider/local.provider.ts +2 -9
- package/src/index.ts +1 -0
- package/src/mcp/tools/core-users.mcp-tools.ts +267 -267
- package/src/profile/profile.service.ts +40 -22
- package/src/session/session.service.ts +10 -2
- package/src/token/token.service.ts +26 -6
- package/src/user/user.controller.ts +5 -2
- package/src/user/user.service.ts +11 -8
- package/dist/menu/menu.service.spec.d.ts +0 -1
- package/dist/menu/menu.service.spec.d.ts.map +0 -1
- package/dist/menu/menu.service.spec.js +0 -208
- package/dist/menu/menu.service.spec.js.map +0 -1
- package/dist/route/route.service.spec.d.ts +0 -1
- package/dist/route/route.service.spec.d.ts.map +0 -1
- package/dist/route/route.service.spec.js +0 -262
- package/dist/route/route.service.spec.js.map +0 -1
- package/dist/screen/screen.service.spec.d.ts +0 -1
- package/dist/screen/screen.service.spec.d.ts.map +0 -1
- package/dist/screen/screen.service.spec.js +0 -259
- package/dist/screen/screen.service.spec.js.map +0 -1
- package/dist/user/user.service.spec.d.ts +0 -1
- package/dist/user/user.service.spec.d.ts.map +0 -1
- package/dist/user/user.service.spec.js +0 -261
- package/dist/user/user.service.spec.js.map +0 -1
|
@@ -87,6 +87,7 @@ function getLocalizedIntegrationName(
|
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
const MAIL_INTEGRATION_PROFILE_SETTING_SLUG = 'mail-integration-profile-id';
|
|
90
91
|
const DAILY_REPORT_EMAIL_INTEGRATION_SETTING_SLUG =
|
|
91
92
|
'operations.daily-report.email-integration-profile-id';
|
|
92
93
|
const DAILY_REPORT_WHATSAPP_INTEGRATION_SETTING_SLUG =
|
|
@@ -635,7 +636,10 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
635
636
|
setting.slug
|
|
636
637
|
);
|
|
637
638
|
|
|
638
|
-
if (
|
|
639
|
+
if (
|
|
640
|
+
setting.slug === 'file-storage-profile-id' ||
|
|
641
|
+
setting.slug === 'lms-bulk-upload-storage-profile-id'
|
|
642
|
+
) {
|
|
639
643
|
const loadStorageTypeId = async () => {
|
|
640
644
|
const typesResponse = await request<IntegrationTypeOption[]>({
|
|
641
645
|
url: '/integration-profile/types',
|
|
@@ -972,10 +976,113 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
972
976
|
);
|
|
973
977
|
}
|
|
974
978
|
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
+
if (setting.slug === MAIL_INTEGRATION_PROFILE_SETTING_SLUG) {
|
|
980
|
+
return (
|
|
981
|
+
<>
|
|
982
|
+
<div className="flex w-full min-w-0 items-center gap-2 md:max-w-72">
|
|
983
|
+
<EntityPicker<IntegrationProfileOption & Record<string, unknown>>
|
|
984
|
+
placeholder={t('selectMailProfile')}
|
|
985
|
+
emptyStateDescription={t('mailProfileEmptyState')}
|
|
986
|
+
value={parsedValue ? Number(parsedValue) : null}
|
|
987
|
+
onChange={(id, option) => {
|
|
988
|
+
setStorageProfileSelectedLabel(option?.name ?? '');
|
|
989
|
+
handleChangeValue(id != null ? String(id) : '', true);
|
|
990
|
+
}}
|
|
991
|
+
valueType="number"
|
|
992
|
+
initialSelectedLabel={storageProfileSelectedLabel}
|
|
993
|
+
showCreateButton={false}
|
|
994
|
+
clearable={false}
|
|
995
|
+
loadOptions={async ({ page, pageSize, search }) => {
|
|
996
|
+
const qs = new URLSearchParams({
|
|
997
|
+
page: String(page),
|
|
998
|
+
pageSize: String(pageSize),
|
|
999
|
+
typeSlug: 'email',
|
|
1000
|
+
});
|
|
1001
|
+
if (search) qs.set('search', search);
|
|
1002
|
+
const response = await request<
|
|
1003
|
+
ApiPaginated<
|
|
1004
|
+
IntegrationProfileOption & Record<string, unknown>
|
|
1005
|
+
>
|
|
1006
|
+
>({
|
|
1007
|
+
url: `/integration-profile?${qs}`,
|
|
1008
|
+
method: 'GET',
|
|
1009
|
+
});
|
|
1010
|
+
const paged = response.data;
|
|
1011
|
+
return {
|
|
1012
|
+
items: paged?.data ?? [],
|
|
1013
|
+
hasMore: (paged?.page ?? 0) < (paged?.lastPage ?? 0),
|
|
1014
|
+
};
|
|
1015
|
+
}}
|
|
1016
|
+
getOptionValue={(opt) => opt.id}
|
|
1017
|
+
getOptionLabel={(opt) => String(opt.name ?? '')}
|
|
1018
|
+
getOptionDescription={(opt) => {
|
|
1019
|
+
const providerSlug = String(
|
|
1020
|
+
opt.integration_provider?.slug ?? ''
|
|
1021
|
+
);
|
|
1022
|
+
const providerName = getLocalizedIntegrationName(
|
|
1023
|
+
opt.integration_provider?.integration_provider_locale,
|
|
1024
|
+
currentLocaleCode
|
|
1025
|
+
);
|
|
1026
|
+
return [providerName || providerSlug, opt.slug]
|
|
1027
|
+
.filter(Boolean)
|
|
1028
|
+
.join(' • ');
|
|
1029
|
+
}}
|
|
1030
|
+
className={cn('flex-1 min-w-0', className)}
|
|
1031
|
+
/>
|
|
1032
|
+
|
|
1033
|
+
<Button
|
|
1034
|
+
type="button"
|
|
1035
|
+
variant="outline"
|
|
1036
|
+
size="icon"
|
|
1037
|
+
className="h-10 w-10 shrink-0"
|
|
1038
|
+
onClick={() => {
|
|
1039
|
+
setStorageProfileEditingId(
|
|
1040
|
+
parsedValue ? Number(parsedValue) : null
|
|
1041
|
+
);
|
|
1042
|
+
setIsStorageProfileSheetOpen(true);
|
|
1043
|
+
}}
|
|
1044
|
+
disabled={!parsedValue}
|
|
1045
|
+
aria-label="Editar perfil de email"
|
|
1046
|
+
title="Editar perfil de email"
|
|
1047
|
+
>
|
|
1048
|
+
<Pencil className="h-4 w-4" />
|
|
1049
|
+
</Button>
|
|
1050
|
+
|
|
1051
|
+
<Button
|
|
1052
|
+
type="button"
|
|
1053
|
+
variant="outline"
|
|
1054
|
+
size="icon"
|
|
1055
|
+
className="h-10 w-10 shrink-0"
|
|
1056
|
+
onClick={() => {
|
|
1057
|
+
setStorageProfileEditingId(null);
|
|
1058
|
+
setIsStorageProfileSheetOpen(true);
|
|
1059
|
+
}}
|
|
1060
|
+
aria-label="Criar perfil de email"
|
|
1061
|
+
title="Criar perfil de email"
|
|
1062
|
+
>
|
|
1063
|
+
<Plus className="h-4 w-4" />
|
|
1064
|
+
</Button>
|
|
1065
|
+
</div>
|
|
1066
|
+
|
|
1067
|
+
<IntegrationProfileSheet
|
|
1068
|
+
open={isStorageProfileSheetOpen}
|
|
1069
|
+
onOpenChange={(nextOpen) => {
|
|
1070
|
+
setIsStorageProfileSheetOpen(nextOpen);
|
|
1071
|
+
if (!nextOpen) setStorageProfileEditingId(null);
|
|
1072
|
+
}}
|
|
1073
|
+
profileId={storageProfileEditingId}
|
|
1074
|
+
initialTypeSlug="email"
|
|
1075
|
+
lockedTypeSlug="email"
|
|
1076
|
+
onSaved={(profile: IntegrationProfileSheetSavedProfile) => {
|
|
1077
|
+
setStorageProfileSelectedLabel(profile.name ?? '');
|
|
1078
|
+
handleChangeValue(String(profile.id), true);
|
|
1079
|
+
}}
|
|
1080
|
+
/>
|
|
1081
|
+
</>
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (!dailyReportIntegrationTypeSlug) {
|
|
979
1086
|
return (
|
|
980
1087
|
<Input
|
|
981
1088
|
type="text"
|
|
@@ -17,6 +17,12 @@ import {
|
|
|
17
17
|
CardHeader,
|
|
18
18
|
CardTitle,
|
|
19
19
|
} from '@/components/ui/card';
|
|
20
|
+
import {
|
|
21
|
+
DropdownMenu,
|
|
22
|
+
DropdownMenuContent,
|
|
23
|
+
DropdownMenuItem,
|
|
24
|
+
DropdownMenuTrigger,
|
|
25
|
+
} from '@/components/ui/dropdown-menu';
|
|
20
26
|
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
21
27
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
22
28
|
import {
|
|
@@ -53,12 +59,6 @@ import {
|
|
|
53
59
|
import { useTranslations } from 'next-intl';
|
|
54
60
|
import { useEffect, useMemo, useState } from 'react';
|
|
55
61
|
import { UserSheet } from './user-sheet';
|
|
56
|
-
import {
|
|
57
|
-
DropdownMenu,
|
|
58
|
-
DropdownMenuContent,
|
|
59
|
-
DropdownMenuItem,
|
|
60
|
-
DropdownMenuTrigger,
|
|
61
|
-
} from '@/components/ui/dropdown-menu';
|
|
62
62
|
|
|
63
63
|
const USER_VIEW_STORAGE_KEY = 'core-user-view-mode';
|
|
64
64
|
type UserViewMode = 'table' | 'cards';
|
|
@@ -366,7 +366,7 @@ export default function UserPage() {
|
|
|
366
366
|
viewMode === 'cards' ? (
|
|
367
367
|
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
|
368
368
|
{Array.from({ length: 6 }).map((_, i) => (
|
|
369
|
-
<Card key={i} className="overflow-hidden
|
|
369
|
+
<Card key={i} className="overflow-hidden">
|
|
370
370
|
<CardContent className="space-y-3 p-4">
|
|
371
371
|
<div className="flex items-center gap-3">
|
|
372
372
|
<Skeleton className="h-12 w-12 rounded-full" />
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { EmptyState } from '@/components/entity-list';
|
|
4
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
5
|
import { Button } from '@/components/ui/button';
|
|
5
|
-
import { Card, CardContent } from '@/components/ui/card';
|
|
6
6
|
import { Input } from '@/components/ui/input';
|
|
7
|
-
import { Label } from '@/components/ui/label';
|
|
8
7
|
import { Switch } from '@/components/ui/switch';
|
|
8
|
+
import {
|
|
9
|
+
Table,
|
|
10
|
+
TableBody,
|
|
11
|
+
TableCell,
|
|
12
|
+
TableHead,
|
|
13
|
+
TableHeader,
|
|
14
|
+
TableRow,
|
|
15
|
+
} from '@/components/ui/table';
|
|
16
|
+
import {
|
|
17
|
+
Tooltip,
|
|
18
|
+
TooltipContent,
|
|
19
|
+
TooltipTrigger,
|
|
20
|
+
} from '@/components/ui/tooltip';
|
|
9
21
|
import { Role } from '@hed-hog/api-types';
|
|
10
22
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
11
23
|
import { CheckCheck, Eye, Loader2, Search, ShieldCheck, X } from 'lucide-react';
|
|
@@ -167,36 +179,50 @@ export function PermissionsSection({
|
|
|
167
179
|
/>
|
|
168
180
|
</div>
|
|
169
181
|
{hasUnassigned && (
|
|
170
|
-
<
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
<Tooltip>
|
|
183
|
+
<TooltipTrigger asChild>
|
|
184
|
+
<Button
|
|
185
|
+
variant="outline"
|
|
186
|
+
size="icon"
|
|
187
|
+
className="h-8 w-8 shrink-0"
|
|
188
|
+
onClick={handleAssignAll}
|
|
189
|
+
disabled={isDisabled}
|
|
190
|
+
aria-label={t('addAllRoles')}
|
|
191
|
+
>
|
|
192
|
+
{isBulkLoading ? (
|
|
193
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
194
|
+
) : (
|
|
195
|
+
<CheckCheck className="h-4 w-4" />
|
|
196
|
+
)}
|
|
197
|
+
</Button>
|
|
198
|
+
</TooltipTrigger>
|
|
199
|
+
<TooltipContent>
|
|
200
|
+
<p>{t('addAllRoles')}</p>
|
|
201
|
+
</TooltipContent>
|
|
202
|
+
</Tooltip>
|
|
184
203
|
)}
|
|
185
204
|
{hasAssigned && (
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
<Tooltip>
|
|
206
|
+
<TooltipTrigger asChild>
|
|
207
|
+
<Button
|
|
208
|
+
variant="outline"
|
|
209
|
+
size="icon"
|
|
210
|
+
className="h-8 w-8 shrink-0"
|
|
211
|
+
onClick={handleRemoveAll}
|
|
212
|
+
disabled={isDisabled}
|
|
213
|
+
aria-label={t('removeAllRoles')}
|
|
214
|
+
>
|
|
215
|
+
{isBulkLoading ? (
|
|
216
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
217
|
+
) : (
|
|
218
|
+
<X className="h-4 w-4" />
|
|
219
|
+
)}
|
|
220
|
+
</Button>
|
|
221
|
+
</TooltipTrigger>
|
|
222
|
+
<TooltipContent>
|
|
223
|
+
<p>{t('removeAllRoles')}</p>
|
|
224
|
+
</TooltipContent>
|
|
225
|
+
</Tooltip>
|
|
200
226
|
)}
|
|
201
227
|
<Button
|
|
202
228
|
variant="outline"
|
|
@@ -215,61 +241,89 @@ export function PermissionsSection({
|
|
|
215
241
|
<p className="text-sm text-muted-foreground">{t('noRolesMatch')}</p>
|
|
216
242
|
</div>
|
|
217
243
|
) : (
|
|
218
|
-
<div className="
|
|
219
|
-
|
|
220
|
-
|
|
244
|
+
<div className="overflow-hidden rounded-lg border border-border/70 bg-background">
|
|
245
|
+
<Table className="table-fixed">
|
|
246
|
+
<TableHeader className="bg-muted/20">
|
|
247
|
+
<TableRow className="hover:bg-transparent">
|
|
248
|
+
<TableHead className="h-8 px-2 text-[11px] font-medium text-muted-foreground">
|
|
249
|
+
Cargo
|
|
250
|
+
</TableHead>
|
|
251
|
+
<TableHead className="h-8 w-37.5 px-2 text-[11px] font-medium text-muted-foreground">
|
|
252
|
+
Slug
|
|
253
|
+
</TableHead>
|
|
254
|
+
<TableHead className="h-8 w-19.5 px-2 text-right text-[11px] font-medium text-muted-foreground">
|
|
255
|
+
Acesso
|
|
256
|
+
</TableHead>
|
|
257
|
+
</TableRow>
|
|
258
|
+
</TableHeader>
|
|
259
|
+
<TableBody>
|
|
260
|
+
{filteredRoles.map((role, index) => {
|
|
261
|
+
const isToggling = togglingRoleId === role.id;
|
|
221
262
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}`}
|
|
230
|
-
>
|
|
231
|
-
<CardContent className="flex items-center justify-between px-4">
|
|
232
|
-
<div className="flex items-center gap-2 flex-1">
|
|
233
|
-
<div
|
|
234
|
-
className={`rounded-md p-2 ${
|
|
235
|
-
role.isAssigned ? 'bg-primary/10' : 'bg-muted'
|
|
236
|
-
}`}
|
|
237
|
-
>
|
|
238
|
-
<ShieldCheck
|
|
239
|
-
className={`h-5 w-5 ${
|
|
240
|
-
role.isAssigned
|
|
241
|
-
? 'text-primary'
|
|
242
|
-
: 'text-muted-foreground'
|
|
243
|
-
}`}
|
|
244
|
-
/>
|
|
245
|
-
</div>
|
|
246
|
-
<div className="flex-1">
|
|
247
|
-
<Label
|
|
248
|
-
htmlFor={`role-${role.id}`}
|
|
249
|
-
className="text-sm font-medium cursor-pointer"
|
|
250
|
-
>
|
|
251
|
-
{role.name} ({role.slug})
|
|
252
|
-
</Label>
|
|
253
|
-
{role.description && (
|
|
254
|
-
<p className="text-xs text-muted-foreground">
|
|
255
|
-
{role.description}
|
|
256
|
-
</p>
|
|
257
|
-
)}
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
<Switch
|
|
261
|
-
id={`role-${role.id}`}
|
|
262
|
-
checked={role.isAssigned}
|
|
263
|
-
disabled={isToggling || isDisabled}
|
|
264
|
-
onCheckedChange={() =>
|
|
265
|
-
handleToggleRole(role.id!, role.isAssigned)
|
|
263
|
+
return (
|
|
264
|
+
<TableRow
|
|
265
|
+
key={role.id ?? `role-${index}`}
|
|
266
|
+
className={
|
|
267
|
+
role.isAssigned
|
|
268
|
+
? 'bg-primary/5 hover:bg-primary/5'
|
|
269
|
+
: 'hover:bg-muted/20'
|
|
266
270
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
>
|
|
272
|
+
<TableCell className="px-2 py-1.5 align-middle">
|
|
273
|
+
<div className="flex min-w-0 items-start gap-1.5">
|
|
274
|
+
<div
|
|
275
|
+
className={`mt-0.5 rounded-md p-0.5 ${
|
|
276
|
+
role.isAssigned ? 'bg-primary/10' : 'bg-muted/70'
|
|
277
|
+
}`}
|
|
278
|
+
>
|
|
279
|
+
<ShieldCheck
|
|
280
|
+
className={`h-3.5 w-3.5 ${
|
|
281
|
+
role.isAssigned
|
|
282
|
+
? 'text-primary'
|
|
283
|
+
: 'text-muted-foreground'
|
|
284
|
+
}`}
|
|
285
|
+
/>
|
|
286
|
+
</div>
|
|
287
|
+
<div className="min-w-0 flex-1">
|
|
288
|
+
<div className="truncate text-[13px] font-semibold leading-4 text-foreground">
|
|
289
|
+
{role.name}
|
|
290
|
+
</div>
|
|
291
|
+
{role.description && (
|
|
292
|
+
<p className="truncate text-[11px] leading-3 text-muted-foreground">
|
|
293
|
+
{role.description}
|
|
294
|
+
</p>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</TableCell>
|
|
299
|
+
<TableCell className="px-2 py-1.5 align-middle">
|
|
300
|
+
{role.slug ? (
|
|
301
|
+
<Badge
|
|
302
|
+
variant="outline"
|
|
303
|
+
className="h-4 rounded-sm border-border/70 bg-muted/40 px-1 font-mono text-[9px] font-medium lowercase leading-none tracking-[0.02em] text-muted-foreground"
|
|
304
|
+
>
|
|
305
|
+
{role.slug}
|
|
306
|
+
</Badge>
|
|
307
|
+
) : null}
|
|
308
|
+
</TableCell>
|
|
309
|
+
<TableCell className="px-2 py-1.5 align-middle">
|
|
310
|
+
<div className="flex justify-end">
|
|
311
|
+
<Switch
|
|
312
|
+
id={`role-${role.id}`}
|
|
313
|
+
checked={role.isAssigned}
|
|
314
|
+
disabled={isToggling || isDisabled}
|
|
315
|
+
onCheckedChange={() =>
|
|
316
|
+
handleToggleRole(role.id!, role.isAssigned)
|
|
317
|
+
}
|
|
318
|
+
className="scale-90 origin-center"
|
|
319
|
+
/>
|
|
320
|
+
</div>
|
|
321
|
+
</TableCell>
|
|
322
|
+
</TableRow>
|
|
323
|
+
);
|
|
324
|
+
})}
|
|
325
|
+
</TableBody>
|
|
326
|
+
</Table>
|
|
273
327
|
</div>
|
|
274
328
|
)}
|
|
275
329
|
|
|
@@ -203,32 +203,34 @@ export function UserAccessSheet({
|
|
|
203
203
|
<div className="w-full border-t" />
|
|
204
204
|
|
|
205
205
|
<Tabs defaultValue="menu" className="px-6 pt-4">
|
|
206
|
-
<
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
206
|
+
<div className="sticky top-0 z-20 -mx-6 bg-background/95 px-6 backdrop-blur supports-backdrop-filter:bg-background/80">
|
|
207
|
+
<TabsList className="mb-4 w-full">
|
|
208
|
+
<TabsTrigger
|
|
209
|
+
value="menu"
|
|
210
|
+
className="flex-1 flex items-center gap-2"
|
|
211
|
+
>
|
|
212
|
+
<Menu className="h-4 w-4" />
|
|
213
|
+
{t('accessTabMenu')}
|
|
214
|
+
{!loadingMenus && (
|
|
215
|
+
<Badge variant="secondary" className="ml-1 text-xs">
|
|
216
|
+
{menus.length}
|
|
217
|
+
</Badge>
|
|
218
|
+
)}
|
|
219
|
+
</TabsTrigger>
|
|
220
|
+
<TabsTrigger
|
|
221
|
+
value="routes"
|
|
222
|
+
className="flex-1 flex items-center gap-2"
|
|
223
|
+
>
|
|
224
|
+
<Globe className="h-4 w-4" />
|
|
225
|
+
{t('accessTabRoutes')}
|
|
226
|
+
{!loadingRoutes && (
|
|
227
|
+
<Badge variant="secondary" className="ml-1 text-xs">
|
|
228
|
+
{routes.length}
|
|
229
|
+
</Badge>
|
|
230
|
+
)}
|
|
231
|
+
</TabsTrigger>
|
|
232
|
+
</TabsList>
|
|
233
|
+
</div>
|
|
232
234
|
|
|
233
235
|
<TabsContent value="menu" className="mt-0">
|
|
234
236
|
{loadingMenus ? (
|