@hed-hog/core 0.0.300 → 0.0.302
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/ai/ai.service.d.ts +13 -2
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +104 -2
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +26 -9
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +11 -5
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +34 -10
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +196 -69
- package/dist/dashboard/dashboard-core/dashboard-core.service.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/integration/services/integration-link.service.d.ts +5 -1
- package/dist/integration/services/integration-link.service.d.ts.map +1 -1
- package/dist/integration/services/integration-link.service.js +141 -53
- package/dist/integration/services/integration-link.service.js.map +1 -1
- package/dist/mail/mail.service.d.ts +9 -2
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +56 -4
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts +6 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +188 -15
- package/dist/setting/setting.service.js.map +1 -1
- package/hedhog/data/setting_group.yaml +28 -0
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +305 -75
- package/hedhog/frontend/messages/en.json +15 -3
- package/hedhog/frontend/messages/pt.json +15 -3
- package/package.json +5 -5
- package/src/ai/ai.service.ts +129 -1
- package/src/dashboard/dashboard-core/dashboard-core.controller.ts +9 -2
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +276 -75
- package/src/index.ts +7 -6
- package/src/integration/services/integration-link.service.ts +190 -55
- package/src/mail/mail.service.ts +67 -3
- package/src/setting/setting.service.ts +222 -15
|
@@ -13,6 +13,14 @@ import {
|
|
|
13
13
|
} from '@/components/ui/alert-dialog';
|
|
14
14
|
import { Badge } from '@/components/ui/badge';
|
|
15
15
|
import { Button } from '@/components/ui/button';
|
|
16
|
+
import {
|
|
17
|
+
Command,
|
|
18
|
+
CommandEmpty,
|
|
19
|
+
CommandGroup,
|
|
20
|
+
CommandInput,
|
|
21
|
+
CommandItem,
|
|
22
|
+
CommandList,
|
|
23
|
+
} from '@/components/ui/command';
|
|
16
24
|
import {
|
|
17
25
|
ContextMenu,
|
|
18
26
|
ContextMenuContent,
|
|
@@ -31,10 +39,12 @@ import {
|
|
|
31
39
|
} from '@/components/ui/sheet';
|
|
32
40
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
33
41
|
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
42
|
+
import { useDebounce } from '@/hooks/use-debounce';
|
|
34
43
|
import { cn } from '@/lib/utils';
|
|
35
44
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
36
45
|
import * as LucideIcons from 'lucide-react';
|
|
37
46
|
import {
|
|
47
|
+
Check,
|
|
38
48
|
Home,
|
|
39
49
|
LayoutDashboard,
|
|
40
50
|
Loader2,
|
|
@@ -44,6 +54,7 @@ import {
|
|
|
44
54
|
Share2,
|
|
45
55
|
Trash2,
|
|
46
56
|
UserPlus,
|
|
57
|
+
X,
|
|
47
58
|
type LucideIcon,
|
|
48
59
|
} from 'lucide-react';
|
|
49
60
|
import { useTranslations } from 'next-intl';
|
|
@@ -68,6 +79,18 @@ type SharedUser = {
|
|
|
68
79
|
email: string | null;
|
|
69
80
|
isCurrentUser?: boolean;
|
|
70
81
|
isHome?: boolean;
|
|
82
|
+
hasRequiredRoles?: boolean;
|
|
83
|
+
accessStatus?: 'allowed' | 'missing-roles';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type PaginatedResponse<T> = {
|
|
87
|
+
data: T[];
|
|
88
|
+
total: number;
|
|
89
|
+
page: number;
|
|
90
|
+
pageSize: number;
|
|
91
|
+
lastPage: number;
|
|
92
|
+
prev: number | null;
|
|
93
|
+
next: number | null;
|
|
71
94
|
};
|
|
72
95
|
|
|
73
96
|
type DashboardTemplate = {
|
|
@@ -82,6 +105,15 @@ const getDashboardName = (dashboard: DashboardTab) =>
|
|
|
82
105
|
dashboard.name || dashboard.dashboard_locale?.[0]?.name || dashboard.slug;
|
|
83
106
|
|
|
84
107
|
const DASHBOARD_HOME_HEADER_ACTIONS_TARGET_ID = 'dashboard-home-header-actions';
|
|
108
|
+
const EMPTY_SHAREABLE_USERS_PAGE: PaginatedResponse<SharedUser> = {
|
|
109
|
+
data: [],
|
|
110
|
+
total: 0,
|
|
111
|
+
page: 1,
|
|
112
|
+
pageSize: 10,
|
|
113
|
+
lastPage: 1,
|
|
114
|
+
prev: null,
|
|
115
|
+
next: null,
|
|
116
|
+
};
|
|
85
117
|
|
|
86
118
|
const DASHBOARD_ICON_OPTIONS = [
|
|
87
119
|
'layout-dashboard',
|
|
@@ -266,12 +298,16 @@ export function DashboardHomeTabs() {
|
|
|
266
298
|
const [newDashboardIcon, setNewDashboardIcon] = useState('layout-dashboard');
|
|
267
299
|
const [selectedTemplateSlug, setSelectedTemplateSlug] = useState('');
|
|
268
300
|
const [shareSearch, setShareSearch] = useState('');
|
|
301
|
+
const [sharePage, setSharePage] = useState(1);
|
|
302
|
+
const [selectedShareUsers, setSelectedShareUsers] = useState<SharedUser[]>(
|
|
303
|
+
[]
|
|
304
|
+
);
|
|
269
305
|
const [renamingSlug, setRenamingSlug] = useState<string | null>(null);
|
|
270
306
|
const [renameValue, setRenameValue] = useState('');
|
|
271
307
|
const [isCreating, setIsCreating] = useState(false);
|
|
272
308
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
273
309
|
const [isRenaming, setIsRenaming] = useState(false);
|
|
274
|
-
const [
|
|
310
|
+
const [isSharingUsers, setIsSharingUsers] = useState(false);
|
|
275
311
|
const [revokingUserId, setRevokingUserId] = useState<number | null>(null);
|
|
276
312
|
const [isSettingHome, setIsSettingHome] = useState(false);
|
|
277
313
|
const [openAddWidgetSignal, setOpenAddWidgetSignal] = useState(0);
|
|
@@ -280,6 +316,8 @@ export function DashboardHomeTabs() {
|
|
|
280
316
|
const [iconValue, setIconValue] = useState('layout-dashboard');
|
|
281
317
|
const [isSavingIcon, setIsSavingIcon] = useState(false);
|
|
282
318
|
|
|
319
|
+
const debouncedShareSearch = useDebounce(shareSearch, 300);
|
|
320
|
+
|
|
283
321
|
const {
|
|
284
322
|
data: dashboards = [],
|
|
285
323
|
isLoading,
|
|
@@ -323,6 +361,16 @@ export function DashboardHomeTabs() {
|
|
|
323
361
|
[dashboardTemplates, selectedTemplateSlug]
|
|
324
362
|
);
|
|
325
363
|
|
|
364
|
+
const selectedShareUserIds = useMemo(
|
|
365
|
+
() => selectedShareUsers.map((user) => user.id),
|
|
366
|
+
[selectedShareUsers]
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
const selectedUsersWithoutRequiredRoles = useMemo(
|
|
370
|
+
() => selectedShareUsers.filter((user) => user.hasRequiredRoles === false),
|
|
371
|
+
[selectedShareUsers]
|
|
372
|
+
);
|
|
373
|
+
|
|
326
374
|
useEffect(() => {
|
|
327
375
|
if (dashboards.length === 0) {
|
|
328
376
|
setActiveSlug('');
|
|
@@ -365,34 +413,55 @@ export function DashboardHomeTabs() {
|
|
|
365
413
|
});
|
|
366
414
|
|
|
367
415
|
const {
|
|
368
|
-
data:
|
|
416
|
+
data: shareableUsersPage = EMPTY_SHAREABLE_USERS_PAGE,
|
|
369
417
|
isLoading: isLoadingShareableUsers,
|
|
370
418
|
refetch: refetchShareableUsers,
|
|
371
|
-
} = useQuery<SharedUser
|
|
372
|
-
queryKey: [
|
|
419
|
+
} = useQuery<PaginatedResponse<SharedUser>>({
|
|
420
|
+
queryKey: [
|
|
421
|
+
'dashboard-shareable-users',
|
|
422
|
+
activeSlug,
|
|
423
|
+
debouncedShareSearch,
|
|
424
|
+
sharePage,
|
|
425
|
+
shareOpen,
|
|
426
|
+
],
|
|
373
427
|
queryFn: async () => {
|
|
374
428
|
if (!activeSlug) {
|
|
375
|
-
return
|
|
429
|
+
return EMPTY_SHAREABLE_USERS_PAGE;
|
|
376
430
|
}
|
|
377
431
|
|
|
378
432
|
const params = new URLSearchParams();
|
|
379
|
-
|
|
380
|
-
|
|
433
|
+
params.set('page', String(sharePage));
|
|
434
|
+
params.set('pageSize', '10');
|
|
435
|
+
|
|
436
|
+
if (debouncedShareSearch.trim()) {
|
|
437
|
+
params.set('search', debouncedShareSearch.trim());
|
|
381
438
|
}
|
|
382
439
|
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}`,
|
|
440
|
+
const response = await request<
|
|
441
|
+
PaginatedResponse<SharedUser> | SharedUser[]
|
|
442
|
+
>({
|
|
443
|
+
url: `/dashboard-core/shareable-users/${activeSlug}?${params.toString()}`,
|
|
388
444
|
method: 'GET',
|
|
389
445
|
});
|
|
390
446
|
|
|
391
|
-
|
|
447
|
+
const payload = response.data;
|
|
448
|
+
|
|
449
|
+
if (Array.isArray(payload)) {
|
|
450
|
+
return {
|
|
451
|
+
...EMPTY_SHAREABLE_USERS_PAGE,
|
|
452
|
+
data: payload,
|
|
453
|
+
total: payload.length,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return payload ?? EMPTY_SHAREABLE_USERS_PAGE;
|
|
392
458
|
},
|
|
393
459
|
enabled: shareOpen && Boolean(activeSlug),
|
|
460
|
+
placeholderData: (previous) => previous ?? EMPTY_SHAREABLE_USERS_PAGE,
|
|
394
461
|
});
|
|
395
462
|
|
|
463
|
+
const shareableUsers = shareableUsersPage.data ?? [];
|
|
464
|
+
|
|
396
465
|
const handleSelectTemplate = (template: DashboardTemplate | null) => {
|
|
397
466
|
setSelectedTemplateSlug(template?.slug ?? '');
|
|
398
467
|
setNewDashboardName(template?.name ?? '');
|
|
@@ -510,6 +579,9 @@ export function DashboardHomeTabs() {
|
|
|
510
579
|
const openShareForDashboard = (dashboard: DashboardTab) => {
|
|
511
580
|
cancelRenameDashboard();
|
|
512
581
|
setActiveSlug(dashboard.slug);
|
|
582
|
+
setShareSearch('');
|
|
583
|
+
setSharePage(1);
|
|
584
|
+
setSelectedShareUsers([]);
|
|
513
585
|
setShareOpen(true);
|
|
514
586
|
};
|
|
515
587
|
|
|
@@ -593,24 +665,49 @@ export function DashboardHomeTabs() {
|
|
|
593
665
|
}
|
|
594
666
|
};
|
|
595
667
|
|
|
596
|
-
const
|
|
597
|
-
|
|
668
|
+
const toggleShareUserSelection = (user: SharedUser) => {
|
|
669
|
+
setSelectedShareUsers((current) => {
|
|
670
|
+
const alreadySelected = current.some((item) => item.id === user.id);
|
|
671
|
+
|
|
672
|
+
if (alreadySelected) {
|
|
673
|
+
return current.filter((item) => item.id !== user.id);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return [...current, user];
|
|
677
|
+
});
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
const removeSelectedShareUser = (userId: number) => {
|
|
681
|
+
setSelectedShareUsers((current) =>
|
|
682
|
+
current.filter((user) => user.id !== userId)
|
|
683
|
+
);
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
const handleShareSelectedUsers = async () => {
|
|
687
|
+
if (!activeDashboard || selectedShareUserIds.length === 0) {
|
|
598
688
|
return;
|
|
599
689
|
}
|
|
600
690
|
|
|
691
|
+
const selectionCount = selectedShareUserIds.length;
|
|
692
|
+
|
|
601
693
|
try {
|
|
602
|
-
|
|
694
|
+
setIsSharingUsers(true);
|
|
603
695
|
await request({
|
|
604
696
|
url: `/dashboard-core/dashboard/${activeDashboard.slug}/share`,
|
|
605
697
|
method: 'POST',
|
|
606
|
-
data: {
|
|
698
|
+
data: {
|
|
699
|
+
userIds: selectedShareUserIds,
|
|
700
|
+
},
|
|
607
701
|
});
|
|
608
702
|
await Promise.all([refetchShares(), refetchShareableUsers()]);
|
|
609
|
-
|
|
703
|
+
setSelectedShareUsers([]);
|
|
704
|
+
setShareSearch('');
|
|
705
|
+
setSharePage(1);
|
|
706
|
+
toast.success(t('dashboardSharedSelected', { count: selectionCount }));
|
|
610
707
|
} catch (error) {
|
|
611
708
|
toast.error(getErrorMessage(error, t('shareDashboardError')));
|
|
612
709
|
} finally {
|
|
613
|
-
|
|
710
|
+
setIsSharingUsers(false);
|
|
614
711
|
}
|
|
615
712
|
};
|
|
616
713
|
|
|
@@ -640,7 +737,7 @@ export function DashboardHomeTabs() {
|
|
|
640
737
|
isRenaming ||
|
|
641
738
|
isSettingHome ||
|
|
642
739
|
isSavingIcon ||
|
|
643
|
-
|
|
740
|
+
isSharingUsers ||
|
|
644
741
|
revokingUserId !== null;
|
|
645
742
|
|
|
646
743
|
const headerActions = (
|
|
@@ -1195,10 +1292,12 @@ export function DashboardHomeTabs() {
|
|
|
1195
1292
|
setShareOpen(open);
|
|
1196
1293
|
if (!open) {
|
|
1197
1294
|
setShareSearch('');
|
|
1295
|
+
setSharePage(1);
|
|
1296
|
+
setSelectedShareUsers([]);
|
|
1198
1297
|
}
|
|
1199
1298
|
}}
|
|
1200
1299
|
>
|
|
1201
|
-
<SheetContent className="w-full sm:max-w-
|
|
1300
|
+
<SheetContent className="w-full sm:max-w-4xl">
|
|
1202
1301
|
<SheetHeader>
|
|
1203
1302
|
<SheetTitle>
|
|
1204
1303
|
{t('shareDashboardTitle', {
|
|
@@ -1211,16 +1310,8 @@ export function DashboardHomeTabs() {
|
|
|
1211
1310
|
</SheetHeader>
|
|
1212
1311
|
|
|
1213
1312
|
<div className="flex flex-1 flex-col gap-4 overflow-hidden px-4 pb-4">
|
|
1214
|
-
<div className="
|
|
1215
|
-
|
|
1216
|
-
{t('searchUser')}
|
|
1217
|
-
</label>
|
|
1218
|
-
<Input
|
|
1219
|
-
id="share-search"
|
|
1220
|
-
value={shareSearch}
|
|
1221
|
-
onChange={(event) => setShareSearch(event.target.value)}
|
|
1222
|
-
placeholder={t('searchUserPlaceholder')}
|
|
1223
|
-
/>
|
|
1313
|
+
<div className="rounded-lg border border-dashed bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
|
|
1314
|
+
{t('shareRoleNotice')}
|
|
1224
1315
|
</div>
|
|
1225
1316
|
|
|
1226
1317
|
<div className="grid flex-1 gap-4 overflow-hidden lg:grid-cols-2">
|
|
@@ -1237,7 +1328,7 @@ export function DashboardHomeTabs() {
|
|
|
1237
1328
|
<Badge variant="outline">{sharedUsers.length}</Badge>
|
|
1238
1329
|
</div>
|
|
1239
1330
|
|
|
1240
|
-
<ScrollArea className="h-
|
|
1331
|
+
<ScrollArea className="h-80">
|
|
1241
1332
|
<div className="space-y-2 p-3">
|
|
1242
1333
|
{isLoadingShares ? (
|
|
1243
1334
|
<>
|
|
@@ -1254,8 +1345,8 @@ export function DashboardHomeTabs() {
|
|
|
1254
1345
|
key={user.id}
|
|
1255
1346
|
className="flex items-center justify-between rounded-lg border p-3"
|
|
1256
1347
|
>
|
|
1257
|
-
<div className="min-w-0">
|
|
1258
|
-
<div className="flex items-center gap-2">
|
|
1348
|
+
<div className="min-w-0 space-y-1">
|
|
1349
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
1259
1350
|
<p className="truncate text-sm font-medium">
|
|
1260
1351
|
{user.name}
|
|
1261
1352
|
</p>
|
|
@@ -1265,10 +1356,23 @@ export function DashboardHomeTabs() {
|
|
|
1265
1356
|
{user.isHome ? (
|
|
1266
1357
|
<Badge variant="outline">{t('home')}</Badge>
|
|
1267
1358
|
) : null}
|
|
1359
|
+
{user.hasRequiredRoles === false ? (
|
|
1360
|
+
<Badge
|
|
1361
|
+
variant="outline"
|
|
1362
|
+
className="border-amber-500/50 text-amber-700"
|
|
1363
|
+
>
|
|
1364
|
+
{t('roleRequiredBadge')}
|
|
1365
|
+
</Badge>
|
|
1366
|
+
) : null}
|
|
1268
1367
|
</div>
|
|
1269
1368
|
<p className="text-muted-foreground truncate text-xs">
|
|
1270
1369
|
{user.email || t('noPublicEmail')}
|
|
1271
1370
|
</p>
|
|
1371
|
+
{user.hasRequiredRoles === false ? (
|
|
1372
|
+
<p className="text-xs text-amber-700">
|
|
1373
|
+
{t('shareBlockedHint')}
|
|
1374
|
+
</p>
|
|
1375
|
+
) : null}
|
|
1272
1376
|
</div>
|
|
1273
1377
|
|
|
1274
1378
|
{!user.isCurrentUser ? (
|
|
@@ -1301,50 +1405,176 @@ export function DashboardHomeTabs() {
|
|
|
1301
1405
|
</p>
|
|
1302
1406
|
</div>
|
|
1303
1407
|
|
|
1304
|
-
<
|
|
1305
|
-
<div className="
|
|
1306
|
-
{
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
{t('
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
>
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1408
|
+
<div className="flex min-h-0 flex-1 flex-col gap-3 p-3">
|
|
1409
|
+
<div className="rounded-lg border">
|
|
1410
|
+
<Command shouldFilter={false}>
|
|
1411
|
+
<CommandInput
|
|
1412
|
+
value={shareSearch}
|
|
1413
|
+
onValueChange={(value) => {
|
|
1414
|
+
setShareSearch(value);
|
|
1415
|
+
setSharePage(1);
|
|
1416
|
+
}}
|
|
1417
|
+
placeholder={t('searchUserPlaceholder')}
|
|
1418
|
+
/>
|
|
1419
|
+
<CommandList>
|
|
1420
|
+
<CommandEmpty>
|
|
1421
|
+
{isLoadingShareableUsers
|
|
1422
|
+
? t('loadingUsers')
|
|
1423
|
+
: t('noUsersToShare')}
|
|
1424
|
+
</CommandEmpty>
|
|
1425
|
+
<CommandGroup>
|
|
1426
|
+
{shareableUsers.map((user) => {
|
|
1427
|
+
const isSelected = selectedShareUserIds.includes(
|
|
1428
|
+
user.id
|
|
1429
|
+
);
|
|
1430
|
+
|
|
1431
|
+
return (
|
|
1432
|
+
<CommandItem
|
|
1433
|
+
key={user.id}
|
|
1434
|
+
value={`${user.name}-${user.email ?? ''}-${user.id}`}
|
|
1435
|
+
className="cursor-pointer items-start gap-3 px-3 py-3"
|
|
1436
|
+
onSelect={() => toggleShareUserSelection(user)}
|
|
1437
|
+
>
|
|
1438
|
+
<div
|
|
1439
|
+
className={cn(
|
|
1440
|
+
'mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-sm border',
|
|
1441
|
+
isSelected
|
|
1442
|
+
? 'border-primary bg-primary text-primary-foreground'
|
|
1443
|
+
: 'border-muted-foreground/30'
|
|
1444
|
+
)}
|
|
1445
|
+
>
|
|
1446
|
+
<Check
|
|
1447
|
+
className={cn(
|
|
1448
|
+
'size-3',
|
|
1449
|
+
isSelected ? 'opacity-100' : 'opacity-0'
|
|
1450
|
+
)}
|
|
1451
|
+
/>
|
|
1452
|
+
</div>
|
|
1453
|
+
<div className="min-w-0 flex-1 space-y-1">
|
|
1454
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
1455
|
+
<p className="truncate text-sm font-medium">
|
|
1456
|
+
{user.name}
|
|
1457
|
+
</p>
|
|
1458
|
+
{user.hasRequiredRoles === false ? (
|
|
1459
|
+
<Badge
|
|
1460
|
+
variant="outline"
|
|
1461
|
+
className="border-amber-500/50 text-amber-700"
|
|
1462
|
+
>
|
|
1463
|
+
{t('roleRequiredBadge')}
|
|
1464
|
+
</Badge>
|
|
1465
|
+
) : null}
|
|
1466
|
+
</div>
|
|
1467
|
+
<p className="text-muted-foreground truncate text-xs">
|
|
1468
|
+
{user.email || t('noPublicEmail')}
|
|
1469
|
+
</p>
|
|
1470
|
+
</div>
|
|
1471
|
+
</CommandItem>
|
|
1472
|
+
);
|
|
1473
|
+
})}
|
|
1474
|
+
</CommandGroup>
|
|
1475
|
+
</CommandList>
|
|
1476
|
+
</Command>
|
|
1477
|
+
</div>
|
|
1329
1478
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1479
|
+
{selectedShareUsers.length > 0 ? (
|
|
1480
|
+
<div className="space-y-2 rounded-lg border p-3">
|
|
1481
|
+
<div className="flex items-center justify-between gap-2">
|
|
1482
|
+
<p className="text-sm font-semibold">
|
|
1483
|
+
{t('selectedUsers')}
|
|
1484
|
+
</p>
|
|
1485
|
+
<Badge variant="outline">
|
|
1486
|
+
{selectedShareUsers.length}
|
|
1487
|
+
</Badge>
|
|
1488
|
+
</div>
|
|
1489
|
+
|
|
1490
|
+
<div className="flex flex-wrap gap-2">
|
|
1491
|
+
{selectedShareUsers.map((user) => (
|
|
1492
|
+
<button
|
|
1493
|
+
key={`selected-${user.id}`}
|
|
1494
|
+
type="button"
|
|
1495
|
+
className="inline-flex cursor-pointer items-center gap-2 rounded-full border px-3 py-1 text-xs"
|
|
1496
|
+
onClick={() => removeSelectedShareUser(user.id)}
|
|
1335
1497
|
>
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1498
|
+
<span className="max-w-40 truncate">
|
|
1499
|
+
{user.name}
|
|
1500
|
+
</span>
|
|
1501
|
+
<X className="size-3" />
|
|
1502
|
+
</button>
|
|
1503
|
+
))}
|
|
1504
|
+
</div>
|
|
1505
|
+
|
|
1506
|
+
{selectedUsersWithoutRequiredRoles.length > 0 ? (
|
|
1507
|
+
<p className="text-xs text-amber-700">
|
|
1508
|
+
{t('selectedUsersWarning', {
|
|
1509
|
+
count: selectedUsersWithoutRequiredRoles.length,
|
|
1510
|
+
})}
|
|
1511
|
+
</p>
|
|
1512
|
+
) : (
|
|
1513
|
+
<p className="text-muted-foreground text-xs">
|
|
1514
|
+
{t('selectedUsersHint')}
|
|
1515
|
+
</p>
|
|
1516
|
+
)}
|
|
1517
|
+
</div>
|
|
1518
|
+
) : null}
|
|
1519
|
+
|
|
1520
|
+
<div className="mt-auto space-y-3 border-t pt-3">
|
|
1521
|
+
<div className="flex items-center justify-between gap-2 text-xs text-muted-foreground">
|
|
1522
|
+
<span>
|
|
1523
|
+
{t('sharePageStatus', {
|
|
1524
|
+
page: shareableUsersPage.page,
|
|
1525
|
+
totalPages: shareableUsersPage.lastPage,
|
|
1526
|
+
})}
|
|
1527
|
+
</span>
|
|
1528
|
+
<div className="flex items-center gap-2">
|
|
1529
|
+
<Button
|
|
1530
|
+
type="button"
|
|
1531
|
+
variant="outline"
|
|
1532
|
+
size="sm"
|
|
1533
|
+
className="cursor-pointer"
|
|
1534
|
+
onClick={() =>
|
|
1535
|
+
setSharePage((current) => Math.max(current - 1, 1))
|
|
1536
|
+
}
|
|
1537
|
+
disabled={
|
|
1538
|
+
isLoadingShareableUsers ||
|
|
1539
|
+
shareableUsersPage.prev === null
|
|
1540
|
+
}
|
|
1541
|
+
>
|
|
1542
|
+
{t('previousPage')}
|
|
1543
|
+
</Button>
|
|
1544
|
+
<Button
|
|
1545
|
+
type="button"
|
|
1546
|
+
variant="outline"
|
|
1547
|
+
size="sm"
|
|
1548
|
+
className="cursor-pointer"
|
|
1549
|
+
onClick={() => setSharePage((current) => current + 1)}
|
|
1550
|
+
disabled={
|
|
1551
|
+
isLoadingShareableUsers ||
|
|
1552
|
+
shareableUsersPage.next === null
|
|
1553
|
+
}
|
|
1554
|
+
>
|
|
1555
|
+
{t('nextPage')}
|
|
1556
|
+
</Button>
|
|
1557
|
+
</div>
|
|
1558
|
+
</div>
|
|
1559
|
+
|
|
1560
|
+
<Button
|
|
1561
|
+
className="w-full"
|
|
1562
|
+
onClick={() => void handleShareSelectedUsers()}
|
|
1563
|
+
disabled={
|
|
1564
|
+
isSharingUsers || selectedShareUserIds.length === 0
|
|
1565
|
+
}
|
|
1566
|
+
>
|
|
1567
|
+
{isSharingUsers ? (
|
|
1568
|
+
<Loader2 className="size-4 animate-spin" />
|
|
1569
|
+
) : (
|
|
1570
|
+
<UserPlus className="size-4" />
|
|
1571
|
+
)}
|
|
1572
|
+
{t('shareSelectedUsers', {
|
|
1573
|
+
count: selectedShareUserIds.length,
|
|
1574
|
+
})}
|
|
1575
|
+
</Button>
|
|
1346
1576
|
</div>
|
|
1347
|
-
</
|
|
1577
|
+
</div>
|
|
1348
1578
|
</div>
|
|
1349
1579
|
</div>
|
|
1350
1580
|
</div>
|
|
@@ -974,17 +974,29 @@
|
|
|
974
974
|
"suggestions": "Suggestions",
|
|
975
975
|
"saveIcon": "Save icon",
|
|
976
976
|
"shareDashboardTitle": "Share {name}",
|
|
977
|
-
"shareDashboardDescription": "
|
|
977
|
+
"shareDashboardDescription": "Sharing only adds people to this dashboard. It does not change their roles or guarantee access to protected widgets.",
|
|
978
|
+
"shareRoleNotice": "Shared users keep their current roles. They can open this dashboard and its widgets only if they already have the required permissions.",
|
|
978
979
|
"searchUser": "Search user",
|
|
979
980
|
"searchUserPlaceholder": "Type a name or email",
|
|
980
981
|
"usersWithAccess": "With access",
|
|
981
|
-
"usersWithAccessDescription": "People
|
|
982
|
+
"usersWithAccessDescription": "People currently listed on this dashboard share. Removing them revokes the share immediately.",
|
|
982
983
|
"noSharedUsers": "There are no shared users yet.",
|
|
983
984
|
"you": "You",
|
|
984
985
|
"home": "Home",
|
|
985
986
|
"noPublicEmail": "No public email",
|
|
987
|
+
"roleRequiredBadge": "Needs roles",
|
|
988
|
+
"shareBlockedHint": "This user is shared on the dashboard, but will be blocked when opening it until the required roles are granted.",
|
|
986
989
|
"addPeople": "Add people",
|
|
987
|
-
"addPeopleDescription": "
|
|
990
|
+
"addPeopleDescription": "Search users, select one or more, and confirm once. Sharing does not change roles.",
|
|
991
|
+
"loadingUsers": "Loading users...",
|
|
992
|
+
"selectedUsers": "Selected users",
|
|
993
|
+
"selectedUsersHint": "Select one or more users, then confirm once to grant dashboard sharing.",
|
|
994
|
+
"selectedUsersWarning": "{count} selected user(s) still need the required roles to open this dashboard.",
|
|
995
|
+
"sharePageStatus": "Page {page} of {totalPages}",
|
|
996
|
+
"previousPage": "Previous",
|
|
997
|
+
"nextPage": "Next",
|
|
998
|
+
"shareSelectedUsers": "Grant access to {count} user(s)",
|
|
999
|
+
"dashboardSharedSelected": "Dashboard shared with {count} user(s).",
|
|
988
1000
|
"noUsersToShare": "No users found to share.",
|
|
989
1001
|
"add": "Add",
|
|
990
1002
|
"cancel": "Cancel",
|
|
@@ -977,17 +977,29 @@
|
|
|
977
977
|
"suggestions": "Sugestões",
|
|
978
978
|
"saveIcon": "Salvar ícone",
|
|
979
979
|
"shareDashboardTitle": "Compartilhar {name}",
|
|
980
|
-
"shareDashboardDescription": "
|
|
980
|
+
"shareDashboardDescription": "O compartilhamento apenas adiciona pessoas a este dashboard. Isso não altera roles nem garante acesso a widgets protegidos.",
|
|
981
|
+
"shareRoleNotice": "Usuários compartilhados mantêm as roles atuais. Eles só conseguem abrir este dashboard e seus widgets se já tiverem as permissões necessárias.",
|
|
981
982
|
"searchUser": "Buscar usuário",
|
|
982
983
|
"searchUserPlaceholder": "Digite um nome ou e-mail",
|
|
983
984
|
"usersWithAccess": "Com acesso",
|
|
984
|
-
"usersWithAccessDescription": "
|
|
985
|
+
"usersWithAccessDescription": "Pessoas atualmente listadas neste compartilhamento. Ao remover, o compartilhamento é revogado imediatamente.",
|
|
985
986
|
"noSharedUsers": "Ainda não há usuários compartilhados.",
|
|
986
987
|
"you": "Você",
|
|
987
988
|
"home": "Home",
|
|
988
989
|
"noPublicEmail": "Sem e-mail público",
|
|
990
|
+
"roleRequiredBadge": "Precisa de roles",
|
|
991
|
+
"shareBlockedHint": "Este usuário continuará compartilhado, mas será bloqueado ao abrir o dashboard até receber as roles necessárias.",
|
|
989
992
|
"addPeople": "Adicionar pessoas",
|
|
990
|
-
"addPeopleDescription": "
|
|
993
|
+
"addPeopleDescription": "Busque usuários, selecione um ou mais e confirme uma única vez. Compartilhar não altera roles.",
|
|
994
|
+
"loadingUsers": "Carregando usuários...",
|
|
995
|
+
"selectedUsers": "Usuários selecionados",
|
|
996
|
+
"selectedUsersHint": "Selecione um ou mais usuários e confirme uma única vez para conceder o compartilhamento do dashboard.",
|
|
997
|
+
"selectedUsersWarning": "{count} usuário(s) selecionado(s) ainda precisam das roles necessárias para abrir este dashboard.",
|
|
998
|
+
"sharePageStatus": "Página {page} de {totalPages}",
|
|
999
|
+
"previousPage": "Anterior",
|
|
1000
|
+
"nextPage": "Próxima",
|
|
1001
|
+
"shareSelectedUsers": "Conceder acesso para {count} usuário(s)",
|
|
1002
|
+
"dashboardSharedSelected": "Dashboard compartilhado com {count} usuário(s).",
|
|
991
1003
|
"noUsersToShare": "Nenhum usuário encontrado para compartilhar.",
|
|
992
1004
|
"add": "Adicionar",
|
|
993
1005
|
"cancel": "Cancelar",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.302",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"sharp": "^0.34.2",
|
|
31
31
|
"speakeasy": "^2.0.0",
|
|
32
32
|
"uuid": "^11.1.0",
|
|
33
|
-
"@hed-hog/api-pagination": "0.0.7",
|
|
34
|
-
"@hed-hog/api-mail": "0.0.9",
|
|
35
33
|
"@hed-hog/api": "0.0.6",
|
|
36
|
-
"@hed-hog/api-
|
|
34
|
+
"@hed-hog/api-mail": "0.0.9",
|
|
35
|
+
"@hed-hog/api-types": "0.0.1",
|
|
37
36
|
"@hed-hog/api-prisma": "0.0.6",
|
|
38
|
-
"@hed-hog/api-
|
|
37
|
+
"@hed-hog/api-locale": "0.0.14",
|
|
38
|
+
"@hed-hog/api-pagination": "0.0.7"
|
|
39
39
|
},
|
|
40
40
|
"exports": {
|
|
41
41
|
".": {
|