@svadmin/ui 0.69.1 → 0.72.0
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/actions.js +1 -1
- package/dist/app.css +102 -63
- package/dist/app.theme.css +102 -63
- package/dist/component-registry.svelte.d.ts +33 -19
- package/dist/component-registry.svelte.js +6 -4
- package/dist/components/AdminApp.svelte +55 -53
- package/dist/components/AdminApp.svelte.d.ts +2 -2
- package/dist/components/ApiSettings.svelte +19 -1
- package/dist/components/AuditLogDrawer.svelte +14 -3
- package/dist/components/AutoForm.svelte +146 -48
- package/dist/components/AutoForm.svelte.d.ts +2 -0
- package/dist/components/AutoTable.svelte +358 -258
- package/dist/components/AutoTable.svelte.d.ts +1 -0
- package/dist/components/BoundRecordDetailDrawer.svelte +166 -0
- package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +9 -0
- package/dist/components/Breadcrumbs.svelte +7 -4
- package/dist/components/CancelTaskButton.svelte +9 -5
- package/dist/components/ColumnHeaderFilter.svelte +4 -3
- package/dist/components/ColumnSettings.svelte +1 -0
- package/dist/components/ComboboxField.svelte +90 -30
- package/dist/components/ComboboxField.svelte.d.ts +2 -2
- package/dist/components/CreatePage.svelte +3 -1
- package/dist/components/DetailDrawer.svelte +4 -0
- package/dist/components/DetailDrawer.svelte.d.ts +1 -0
- package/dist/components/DevTools.svelte +1 -1
- package/dist/components/DraggableGrid.svelte +1 -0
- package/dist/components/DraggableHeader.svelte +5 -9
- package/dist/components/DraggableHeader.svelte.d.ts +43 -18
- package/dist/components/DraggableRowTable.svelte +2 -2
- package/dist/components/DynamicFormList.svelte +17 -17
- package/dist/components/DynamicFormList.svelte.d.ts +2 -1
- package/dist/components/EditPage.svelte +3 -1
- package/dist/components/EditableTable.svelte +4 -0
- package/dist/components/ErrorPage.svelte +3 -1
- package/dist/components/FieldDisplay.svelte +14 -0
- package/dist/components/FieldDisplay.svelte.d.ts +7 -0
- package/dist/components/FieldRenderer.svelte +9 -5
- package/dist/components/FilterBuilder.svelte +19 -8
- package/dist/components/FilterDropdown.svelte.d.ts +1 -1
- package/dist/components/Header.svelte +5 -6
- package/dist/components/ImportWizard.svelte +3 -2
- package/dist/components/InfiniteList.svelte +22 -8
- package/dist/components/InfiniteList.svelte.d.ts +5 -3
- package/dist/components/InlineEdit.svelte +114 -44
- package/dist/components/JsonSchemaForm.svelte +19 -14
- package/dist/components/Layout.svelte +19 -15
- package/dist/components/LazyTaskQueueDrawer.svelte +26 -0
- package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +8 -0
- package/dist/components/ListPage.svelte +7 -6
- package/dist/components/MasterDetailView.svelte +3 -3
- package/dist/components/MediaLibraryModal.svelte +5 -2
- package/dist/components/MentionsInput.svelte +4 -3
- package/dist/components/NavigateToResource.svelte +3 -2
- package/dist/components/PermissionMatrix.svelte +4 -3
- package/dist/components/ProfilePage.svelte +2 -2
- package/dist/components/QuickEditDrawer.svelte +39 -0
- package/dist/components/QuickEditDrawer.svelte.d.ts +8 -0
- package/dist/components/RecordDetailDrawer.svelte +14 -108
- package/dist/components/RecordRowActions.svelte +41 -0
- package/dist/components/RecordRowActions.svelte.d.ts +14 -0
- package/dist/components/ResourceAccessGuard.svelte +7 -5
- package/dist/components/RetryTaskButton.svelte +9 -5
- package/dist/components/RowActions.svelte +5 -3
- package/dist/components/SensitiveDataMask.svelte +3 -3
- package/dist/components/SettingsPage.svelte +1 -1
- package/dist/components/ShowPage.svelte +44 -38
- package/dist/components/Sidebar.svelte +10 -8
- package/dist/components/SplitPaneLayout.svelte +4 -2
- package/dist/components/StepsForm.svelte +161 -44
- package/dist/components/StepsForm.svelte.d.ts +1 -0
- package/dist/components/TaskDetails.svelte +20 -7
- package/dist/components/TaskList.svelte +19 -7
- package/dist/components/TaskQueueDrawer.svelte +21 -22
- package/dist/components/Toast.svelte +4 -2
- package/dist/components/TooltipButton.svelte +1 -1
- package/dist/components/TreeSelect.svelte +4 -2
- package/dist/components/ValidatedField.svelte +27 -0
- package/dist/components/ValidatedField.svelte.d.ts +35 -0
- package/dist/components/account/ImportMembersPage.svelte +2 -2
- package/dist/components/account/SecurityLogPage.svelte +3 -1
- package/dist/components/account/SettingsEnterprisePage.svelte +7 -5
- package/dist/components/account/TeamMembersPage.svelte +1 -1
- package/dist/components/buttons/CloneButton.svelte +6 -4
- package/dist/components/buttons/DeleteButton.svelte +95 -29
- package/dist/components/buttons/DeleteButton.svelte.d.ts +2 -0
- package/dist/components/buttons/EditButton.svelte +6 -4
- package/dist/components/buttons/ExportButton.svelte +22 -4
- package/dist/components/buttons/ImportButton.svelte +83 -38
- package/dist/components/buttons/ImportButton.svelte.d.ts +6 -10
- package/dist/components/buttons/RefreshButton.svelte +48 -8
- package/dist/components/buttons/ShowButton.svelte +6 -4
- package/dist/components/content/ApiKeyList.svelte +3 -1
- package/dist/components/content/ContentPageShell.svelte +3 -1
- package/dist/components/content/FileList.svelte +3 -1
- package/dist/components/content/MemberList.svelte +3 -1
- package/dist/components/content/NetworkTable.svelte +3 -1
- package/dist/components/content/SecurityEventTable.svelte +3 -1
- package/dist/components/content/StatusTabs.svelte +2 -2
- package/dist/components/fieldComponentMap.d.ts +18 -13
- package/dist/components/fieldComponentMap.js +95 -47
- package/dist/components/fields/AvatarField.svelte +1 -1
- package/dist/components/fields/ChoiceField.svelte +16 -0
- package/dist/components/fields/ChoiceField.svelte.d.ts +10 -0
- package/dist/components/fields/CodeField.svelte +11 -12
- package/dist/components/fields/CodeField.svelte.d.ts +1 -1
- package/dist/components/fields/DateRangeField.svelte +2 -4
- package/dist/components/fields/ImageField.svelte +3 -1
- package/dist/components/fields/ImagesField.svelte +23 -0
- package/dist/components/fields/ImagesField.svelte.d.ts +6 -0
- package/dist/components/fields/JsonField.svelte +19 -13
- package/dist/components/fields/PasswordField.svelte +5 -0
- package/dist/components/fields/PasswordField.svelte.d.ts +6 -0
- package/dist/components/fields/json-value.d.ts +11 -0
- package/dist/components/fields/json-value.js +28 -0
- package/dist/components/profile/ActivityTimeline.svelte +2 -2
- package/dist/components/record-detail.svelte.d.ts +16 -0
- package/dist/components/record-detail.svelte.js +28 -0
- package/dist/components/saved-list-views.js +29 -29
- package/dist/components/table-contract.d.ts +13 -0
- package/dist/components/table-contract.js +42 -0
- package/dist/components/task-utils.js +4 -2
- package/dist/components/ui/alert/alert-variants.d.ts +2 -3
- package/dist/components/ui/alert/alert-variants.js +1 -2
- package/dist/components/ui/alert/alert.svelte +4 -5
- package/dist/components/ui/alert/alert.svelte.d.ts +3 -8
- package/dist/components/ui/alert/index.js +0 -2
- package/dist/components/ui/avatar/avatar-variants.d.ts +2 -3
- package/dist/components/ui/avatar/avatar-variants.js +1 -2
- package/dist/components/ui/avatar/avatar.svelte +5 -6
- package/dist/components/ui/avatar/avatar.svelte.d.ts +3 -8
- package/dist/components/ui/badge/badge-variants.d.ts +2 -3
- package/dist/components/ui/badge/badge-variants.js +1 -2
- package/dist/components/ui/badge/badge.svelte +4 -5
- package/dist/components/ui/badge/badge.svelte.d.ts +3 -8
- package/dist/components/ui/badge/index.js +0 -2
- package/dist/components/ui/button/button-variants.d.ts +2 -3
- package/dist/components/ui/button/button-variants.js +1 -2
- package/dist/components/ui/button/button.svelte +100 -44
- package/dist/components/ui/button/button.svelte.d.ts +8 -12
- package/dist/components/ui/button/index.js +0 -2
- package/dist/components/ui/command/command-dialog.svelte +3 -1
- package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
- package/dist/components/ui/command/command-group.svelte +4 -2
- package/dist/components/ui/command/command-item.svelte +4 -2
- package/dist/components/ui/command/command-separator.svelte +3 -1
- package/dist/components/ui/command/index.d.ts +5 -5
- package/dist/components/ui/input/input.svelte +40 -20
- package/dist/components/ui/input/input.svelte.d.ts +5 -4
- package/dist/components/ui/sheet/sheet.svelte +9 -3
- package/dist/components/ui/sheet/sheet.svelte.d.ts +1 -0
- package/dist/components/ui/tooltip/tooltip.svelte +4 -2
- package/dist/index.d.ts +1 -1
- package/dist/inline-value.d.ts +5 -0
- package/dist/inline-value.js +24 -0
- package/dist/numeric-input.d.ts +2 -0
- package/dist/numeric-input.js +9 -0
- package/dist/types.d.ts +3 -3
- package/package.json +11 -5
|
@@ -1,121 +1,27 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { definedOptions } from '@svadmin/core/options';
|
|
3
|
+
import { captureAdminContext } from '@svadmin/core';
|
|
3
4
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
4
|
-
import
|
|
5
|
-
import CanAccess from './CanAccess.svelte';
|
|
5
|
+
import BoundRecordDetailDrawer from './BoundRecordDetailDrawer.svelte';
|
|
6
6
|
import DetailDrawer from './DetailDrawer.svelte';
|
|
7
|
-
import { getDisplayComponent } from './fieldComponentMap';
|
|
8
|
-
import { Button } from './ui/button/index.js';
|
|
9
|
-
import { Skeleton } from './ui/skeleton/index.js';
|
|
10
7
|
|
|
11
|
-
let {
|
|
12
|
-
resourceName,
|
|
13
|
-
open = $bindable(false),
|
|
14
|
-
recordId,
|
|
15
|
-
onClose,
|
|
16
|
-
} = $props<{
|
|
8
|
+
let { resourceName, open = $bindable(false), recordId, onClose }: {
|
|
17
9
|
resourceName: string;
|
|
18
10
|
open?: boolean;
|
|
19
11
|
recordId?: string | number;
|
|
20
12
|
onClose?: () => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
13
|
+
} = $props();
|
|
14
|
+
const context = captureAdminContext();
|
|
23
15
|
const i18n = useTranslation();
|
|
24
|
-
const adminContext = captureAdminContext();
|
|
25
|
-
const navigation = useNavigation();
|
|
26
|
-
const resource = $derived(getResource(resourceName));
|
|
27
|
-
const showFields = $derived(resource.fields.filter((field) => field.showInShow !== false));
|
|
28
|
-
const accessControlEnabled = $derived(!!adminContext.accessControlProvider);
|
|
29
|
-
const showPermission = useCan(() => ({
|
|
30
|
-
resource: resourceName,
|
|
31
|
-
action: 'show',
|
|
32
|
-
params: recordId == null ? undefined : { id: recordId },
|
|
33
|
-
queryOptions: { enabled: open && recordId != null && accessControlEnabled },
|
|
34
|
-
}));
|
|
35
|
-
const permissionPending = $derived(
|
|
36
|
-
accessControlEnabled && open && recordId != null && showPermission.isLoading
|
|
37
|
-
);
|
|
38
|
-
const canReadRecord = $derived(
|
|
39
|
-
resource.canShow !== false && (!accessControlEnabled || (!permissionPending && showPermission.allowed))
|
|
40
|
-
);
|
|
41
|
-
const query = useShow({
|
|
42
|
-
get resource() { return resourceName; },
|
|
43
|
-
get id() { return recordId; },
|
|
44
|
-
get queryOptions() { return { enabled: open && recordId != null && canReadRecord }; },
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
function navigateTo(action: 'show' | 'edit'): void {
|
|
48
|
-
if (recordId == null) return;
|
|
49
|
-
navigation[action](resourceName, recordId);
|
|
50
|
-
open = false;
|
|
51
|
-
}
|
|
52
16
|
</script>
|
|
53
17
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
description={recordId == null ? undefined : `#${recordId}`}
|
|
58
|
-
closeLabel={i18n.t('common.close')}
|
|
59
|
-
width="w-full sm:max-w-xl"
|
|
60
|
-
{onClose}
|
|
61
|
-
data-svadmin-record-detail
|
|
62
|
-
>
|
|
63
|
-
{#if permissionPending || (canReadRecord && query.isLoading)}
|
|
64
|
-
<div class="svadmin-u-fa6acbf81d74 svadmin-u-d2c3932343f5" role="status" aria-live="polite" aria-label={i18n.t('common.loading')} aria-busy="true">
|
|
65
|
-
{#each showFields.slice(0, 6) as field (field.key)}
|
|
66
|
-
<div class="svadmin-u-f3c543ad5fe9 svadmin-u-77a2a20e90d4 svadmin-u-cb11fec3bb46 svadmin-u-d9bdd3d643a2 svadmin-u-022e8076dfea">
|
|
67
|
-
<Skeleton class="svadmin-u-11e59c6d5f6b svadmin-u-69da7e4ff95d" />
|
|
68
|
-
<Skeleton class="svadmin-u-11e59c6d5f6b svadmin-u-6da6a3c3f741 svadmin-u-35b2f3aff60f" />
|
|
69
|
-
</div>
|
|
70
|
-
{/each}
|
|
71
|
-
</div>
|
|
72
|
-
{:else if !canReadRecord}
|
|
73
|
-
<div class="svadmin-u-61357c0c2f29 svadmin-u-ca6bf63030aa" data-svadmin-access-denied>
|
|
74
|
-
<p class="svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{i18n.t('common.accessDenied')}</p>
|
|
75
|
-
</div>
|
|
76
|
-
{:else if query.data?.data}
|
|
77
|
-
{@const record = query.data.data as Record<string, unknown>}
|
|
78
|
-
<dl class="svadmin-u-fa6acbf81d74 svadmin-u-d2c3932343f5">
|
|
79
|
-
{#each showFields as field (field.key)}
|
|
80
|
-
{@const value = record[field.key]}
|
|
81
|
-
{@const DisplayComponent = getDisplayComponent(field.type)}
|
|
82
|
-
<div class="svadmin-u-f3c543ad5fe9 svadmin-u-58284b4ea568 svadmin-u-cb11fec3bb46 svadmin-u-d9bdd3d643a2 svadmin-u-022e8076dfea">
|
|
83
|
-
<dt class="svadmin-u-359090c2d529 svadmin-u-2689f3958069 svadmin-u-bfa603190748 svadmin-u-e2327d142859">{field.label}</dt>
|
|
84
|
-
<dd class="svadmin-u-7e0b7cdf1a94 svadmin-u-170cee3ff4e4 svadmin-u-fc7473ca09eb svadmin-u-d4108abe6359">
|
|
85
|
-
{#if DisplayComponent && value != null}
|
|
86
|
-
<DisplayComponent {value} options={field.options} resourceName={field.resource} />
|
|
87
|
-
{:else}
|
|
88
|
-
{value != null ? String(value) : '—'}
|
|
89
|
-
{/if}
|
|
90
|
-
</dd>
|
|
91
|
-
</div>
|
|
92
|
-
{/each}
|
|
93
|
-
</dl>
|
|
18
|
+
{#if open}
|
|
19
|
+
{#if recordId != null}
|
|
20
|
+
<BoundRecordDetailDrawer {resourceName} {recordId} bind:open {...definedOptions({ onClose })} />
|
|
94
21
|
{:else}
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
{/if}
|
|
100
|
-
</div>
|
|
22
|
+
<DetailDrawer bind:open title="{context.getResource(resourceName).label} {i18n.t('common.detail')}"
|
|
23
|
+
closeLabel={i18n.t('common.close')} {...definedOptions({ onClose })} data-svadmin-record-detail>
|
|
24
|
+
<p>{i18n.t('common.noData')}</p>
|
|
25
|
+
</DetailDrawer>
|
|
101
26
|
{/if}
|
|
102
|
-
|
|
103
|
-
{#snippet footer()}
|
|
104
|
-
{#if canReadRecord && recordId != null && resource.canShow !== false}
|
|
105
|
-
<CanAccess resource={resourceName} action="show" params={{ id: recordId }}>
|
|
106
|
-
<Button variant="outline" onclick={() => navigateTo('show')}>
|
|
107
|
-
<Eye class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" data-icon="inline-start" aria-hidden="true" />
|
|
108
|
-
{i18n.t('common.openFullDetail')}
|
|
109
|
-
</Button>
|
|
110
|
-
</CanAccess>
|
|
111
|
-
{/if}
|
|
112
|
-
{#if canReadRecord && recordId != null && resource.canEdit !== false}
|
|
113
|
-
<CanAccess resource={resourceName} action="edit" params={{ id: recordId }}>
|
|
114
|
-
<Button onclick={() => navigateTo('edit')}>
|
|
115
|
-
<Pencil class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" data-icon="inline-start" aria-hidden="true" />
|
|
116
|
-
{i18n.t('common.edit')}
|
|
117
|
-
</Button>
|
|
118
|
-
</CanAccess>
|
|
119
|
-
{/if}
|
|
120
|
-
{/snippet}
|
|
121
|
-
</DetailDrawer>
|
|
27
|
+
{/if}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useCan } from '@svadmin/core';
|
|
3
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
4
|
+
import { Eye, Pencil, PanelRight, Trash2 } from '@lucide/svelte';
|
|
5
|
+
import RowActions, { type RowActionItem } from './RowActions.svelte';
|
|
6
|
+
import TooltipButton from './TooltipButton.svelte';
|
|
7
|
+
|
|
8
|
+
let { resourceName, id, canShow, canEdit, canDelete, onShow, onEdit, onQuickEdit, onDelete }: {
|
|
9
|
+
resourceName: string;
|
|
10
|
+
id: string | number;
|
|
11
|
+
canShow: boolean;
|
|
12
|
+
canEdit: boolean;
|
|
13
|
+
canDelete: boolean;
|
|
14
|
+
onShow: () => void;
|
|
15
|
+
onEdit: () => void;
|
|
16
|
+
onQuickEdit: () => void;
|
|
17
|
+
onDelete: () => void;
|
|
18
|
+
} = $props();
|
|
19
|
+
const i18n = useTranslation();
|
|
20
|
+
const show = useCan(() => ({ resource: resourceName, action: 'show', id, queryOptions: { enabled: canShow } }));
|
|
21
|
+
const edit = useCan(() => ({ resource: resourceName, action: 'edit', id, queryOptions: { enabled: canEdit } }));
|
|
22
|
+
const remove = useCan(() => ({ resource: resourceName, action: 'delete', id, queryOptions: { enabled: canDelete } }));
|
|
23
|
+
// 编辑保持为可见主按钮;删除和快速编辑放入更多菜单。
|
|
24
|
+
const actions = $derived<RowActionItem[]>([
|
|
25
|
+
{ label: i18n.t('common.quickEdit'), icon: PanelRight, hidden: !canEdit || edit.allowed !== true, onclick: onQuickEdit },
|
|
26
|
+
{ label: i18n.t('common.delete'), icon: Trash2, hidden: !canDelete || remove.allowed !== true, danger: true, onclick: onDelete },
|
|
27
|
+
]);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<RowActions {actions} maxVisible={0} moreLabel={i18n.t('common.moreActions')}>
|
|
31
|
+
{#if canShow && show.allowed === true}
|
|
32
|
+
<TooltipButton tooltip={i18n.t('common.detail')} variant="ghost" size="icon-sm" onclick={onShow}>
|
|
33
|
+
<Eye class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" />
|
|
34
|
+
</TooltipButton>
|
|
35
|
+
{/if}
|
|
36
|
+
{#if canEdit && edit.allowed === true}
|
|
37
|
+
<TooltipButton tooltip={i18n.t('common.edit')} variant="ghost" size="icon-sm" onclick={onEdit}>
|
|
38
|
+
<Pencil class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" />
|
|
39
|
+
</TooltipButton>
|
|
40
|
+
{/if}
|
|
41
|
+
</RowActions>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
resourceName: string;
|
|
3
|
+
id: string | number;
|
|
4
|
+
canShow: boolean;
|
|
5
|
+
canEdit: boolean;
|
|
6
|
+
canDelete: boolean;
|
|
7
|
+
onShow: () => void;
|
|
8
|
+
onEdit: () => void;
|
|
9
|
+
onQuickEdit: () => void;
|
|
10
|
+
onDelete: () => void;
|
|
11
|
+
};
|
|
12
|
+
declare const RecordRowActions: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type RecordRowActions = ReturnType<typeof RecordRowActions>;
|
|
14
|
+
export default RecordRowActions;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { definedOptions } from '@svadmin/core/options';
|
|
3
|
+
|
|
2
4
|
import type { Action } from '@svadmin/core';
|
|
3
5
|
import { captureAdminContext, useCan } from '@svadmin/core';
|
|
4
6
|
import type { Snippet } from 'svelte';
|
|
@@ -34,18 +36,18 @@
|
|
|
34
36
|
);
|
|
35
37
|
const sourceReadAllowedByResource = $derived(!requireSourceRead || resource.canShow !== false);
|
|
36
38
|
const params = $derived(id === undefined ? undefined : { id });
|
|
37
|
-
const permission = useCan(() => ({
|
|
39
|
+
const permission = useCan(() => (definedOptions({
|
|
38
40
|
resource: resourceName,
|
|
39
41
|
action,
|
|
40
42
|
params,
|
|
41
43
|
queryOptions: { enabled: accessControlEnabled },
|
|
42
|
-
}));
|
|
43
|
-
const sourceReadPermission = useCan(() => ({
|
|
44
|
+
})));
|
|
45
|
+
const sourceReadPermission = useCan(() => (definedOptions({
|
|
44
46
|
resource: resourceName,
|
|
45
47
|
action: 'show',
|
|
46
48
|
params,
|
|
47
49
|
queryOptions: { enabled: accessControlEnabled && requireSourceRead },
|
|
48
|
-
}));
|
|
50
|
+
})));
|
|
49
51
|
const permissionPending = $derived(
|
|
50
52
|
accessControlEnabled
|
|
51
53
|
&& (permission.isLoading || (requireSourceRead && sourceReadPermission.isLoading)),
|
|
@@ -64,5 +66,5 @@
|
|
|
64
66
|
{:else if allowed}
|
|
65
67
|
{@render children()}
|
|
66
68
|
{:else}
|
|
67
|
-
<DataState state="forbidden" description
|
|
69
|
+
<DataState state="forbidden" {...definedOptions({ "description": denialReason })} />
|
|
68
70
|
{/if}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { captureAdminContext, queryKeyMatches } from '@svadmin/core';
|
|
2
|
+
import { captureAdminContext, queryKeyMatches, withValidatedTaskProvider } from '@svadmin/core';
|
|
3
3
|
import { useQueryClient } from '@tanstack/svelte-query';
|
|
4
4
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
5
5
|
|
|
@@ -34,23 +34,27 @@
|
|
|
34
34
|
|
|
35
35
|
async function handleRetry() {
|
|
36
36
|
if (!taskProvider?.retry || pending) return;
|
|
37
|
+
const scopedTaskId = taskId;
|
|
38
|
+
const scope = adminContext.queryKeyMatcher();
|
|
37
39
|
pending = true;
|
|
38
40
|
try {
|
|
39
|
-
|
|
41
|
+
const provider = withValidatedTaskProvider(taskProvider);
|
|
42
|
+
if (!provider.retry) return;
|
|
43
|
+
await provider.retry(scopedTaskId);
|
|
40
44
|
await Promise.all([
|
|
41
45
|
queryClient.invalidateQueries({
|
|
42
46
|
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
43
|
-
...
|
|
47
|
+
...scope,
|
|
44
48
|
kind: 'task',
|
|
45
49
|
action: 'list',
|
|
46
50
|
}),
|
|
47
51
|
}),
|
|
48
52
|
queryClient.invalidateQueries({
|
|
49
53
|
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
50
|
-
...
|
|
54
|
+
...scope,
|
|
51
55
|
kind: 'task',
|
|
52
56
|
action: 'one',
|
|
53
|
-
id:
|
|
57
|
+
id: scopedTaskId,
|
|
54
58
|
}),
|
|
55
59
|
}),
|
|
56
60
|
]);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { definedOptions } from '@svadmin/core/options';
|
|
3
|
+
|
|
2
4
|
import { Ellipsis } from '@lucide/svelte';
|
|
3
5
|
import { tick, type Component, type Snippet } from 'svelte';
|
|
4
6
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
@@ -143,7 +145,7 @@
|
|
|
143
145
|
{#each visibleActions as action (action)}
|
|
144
146
|
{#if action.href}
|
|
145
147
|
<Button
|
|
146
|
-
variant
|
|
148
|
+
{...definedOptions({ "variant": resolvedVariant(action) })}
|
|
147
149
|
size="sm"
|
|
148
150
|
href={action.href}
|
|
149
151
|
disabled={action.disabled}
|
|
@@ -158,7 +160,7 @@
|
|
|
158
160
|
</Button>
|
|
159
161
|
{:else}
|
|
160
162
|
<Button
|
|
161
|
-
variant
|
|
163
|
+
{...definedOptions({ "variant": resolvedVariant(action) })}
|
|
162
164
|
size="sm"
|
|
163
165
|
onclick={action.onclick}
|
|
164
166
|
disabled={action.disabled}
|
|
@@ -188,7 +190,7 @@
|
|
|
188
190
|
{#if action.href}
|
|
189
191
|
<Button
|
|
190
192
|
href={action.href}
|
|
191
|
-
variant
|
|
193
|
+
{...definedOptions({ "variant": resolvedVariant(action) })}
|
|
192
194
|
size="sm"
|
|
193
195
|
disabled={action.disabled}
|
|
194
196
|
onclick={(event) => handleOverflowAction(action, event)}
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
: '********';
|
|
49
49
|
case 'email': {
|
|
50
50
|
const parts = val.split('@');
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const [name, domain] = parts;
|
|
52
|
+
if (parts.length === 2 && name && domain) {
|
|
53
53
|
const maskedName = name.length > 2 ? `${name[0]}***${name.slice(-1)}` : `${name[0]}***`;
|
|
54
|
-
return `${maskedName}@${
|
|
54
|
+
return `${maskedName}@${domain}`;
|
|
55
55
|
}
|
|
56
56
|
return '***@***.***';
|
|
57
57
|
}
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
const i18n = useTranslation();
|
|
109
109
|
|
|
110
110
|
let activeKey = $derived.by(() => {
|
|
111
|
-
const tab = getParams()
|
|
111
|
+
const tab = getParams()['tab'];
|
|
112
112
|
if (!tab) return "profile";
|
|
113
113
|
const alias = tab === "api-keys" ? "api" : tab === "audit-logs" ? "audit" : tab;
|
|
114
114
|
if (sectionKeys.has(alias)) return alias;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { useNavigation
|
|
2
|
+
import { useNavigation } from '@svadmin/core';
|
|
3
|
+
import { useRecordDetail } from './record-detail.svelte';
|
|
3
4
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
4
5
|
import type { Snippet } from 'svelte';
|
|
5
6
|
import * as Card from './ui/card/index.js';
|
|
6
7
|
import { Skeleton } from './ui/skeleton/index.js';
|
|
7
8
|
import PageHeader from './PageHeader.svelte';
|
|
8
|
-
import
|
|
9
|
+
import FieldDisplay from './FieldDisplay.svelte';
|
|
9
10
|
import EditButton from './buttons/EditButton.svelte';
|
|
10
11
|
import DeleteButton from './buttons/DeleteButton.svelte';
|
|
11
12
|
import CloneButton from './buttons/CloneButton.svelte';
|
|
12
|
-
import
|
|
13
|
+
import { Button } from './ui/button/index.js';
|
|
14
|
+
import { RefreshCw } from '@lucide/svelte';
|
|
13
15
|
|
|
14
16
|
const i18n = useTranslation();
|
|
15
17
|
|
|
@@ -40,10 +42,10 @@
|
|
|
40
42
|
const navigation = useNavigation();
|
|
41
43
|
const isCompact = $derived(density === 'compact');
|
|
42
44
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const query =
|
|
45
|
+
const detail = useRecordDetail(() => ({ resourceName, id }));
|
|
46
|
+
const resource = $derived(detail.resource);
|
|
47
|
+
const showFields = $derived(detail.fields);
|
|
48
|
+
const query = detail.query;
|
|
47
49
|
|
|
48
50
|
const gridColumnClass = $derived.by(() => {
|
|
49
51
|
switch (columns) {
|
|
@@ -63,23 +65,27 @@
|
|
|
63
65
|
backLabel={i18n.t('common.backToList')}
|
|
64
66
|
>
|
|
65
67
|
{#snippet actions()}
|
|
66
|
-
{#if resource.canEdit !== false}
|
|
68
|
+
{#if detail.canRead && query.isSuccess && resource.canEdit !== false}
|
|
67
69
|
<EditButton resource={resourceName} recordItemId={id} hideText />
|
|
68
70
|
{/if}
|
|
69
|
-
{#if resource.canCreate !== false}
|
|
71
|
+
{#if detail.canRead && query.isSuccess && resource.canCreate !== false}
|
|
70
72
|
<CloneButton resource={resourceName} recordItemId={id} hideText />
|
|
71
73
|
{/if}
|
|
72
|
-
{#if resource.canDelete !== false}
|
|
74
|
+
{#if detail.canRead && query.isSuccess && resource.canDelete !== false}
|
|
73
75
|
<DeleteButton resource={resourceName} recordItemId={id} hideText onSuccess={() => navigation.list(resourceName)} />
|
|
74
76
|
{/if}
|
|
75
|
-
<
|
|
77
|
+
<Button type="button" variant="ghost" size="icon" title={i18n.t('common.refresh')}
|
|
78
|
+
aria-label={i18n.t('common.refresh')} aria-busy={query.isFetching}
|
|
79
|
+
disabled={!detail.canRead || query.isFetching} onclick={detail.refresh}>
|
|
80
|
+
<RefreshCw class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" />
|
|
81
|
+
</Button>
|
|
76
82
|
{#if headerActions}
|
|
77
83
|
{@render headerActions()}
|
|
78
84
|
{/if}
|
|
79
85
|
{/snippet}
|
|
80
86
|
</PageHeader>
|
|
81
87
|
|
|
82
|
-
{#if query.isLoading}
|
|
88
|
+
{#if detail.checkingPermission || query.isLoading}
|
|
83
89
|
<Card.Root class="svadmin-u-2cd02d11d1af svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
84
90
|
<Card.Content class="svadmin-u-8a539c7fe216">
|
|
85
91
|
{#each showFields.slice(0, 6) as _, i (i)}
|
|
@@ -90,26 +96,33 @@
|
|
|
90
96
|
{/each}
|
|
91
97
|
</Card.Content>
|
|
92
98
|
</Card.Root>
|
|
93
|
-
{:else if
|
|
99
|
+
{:else if !detail.canRead}
|
|
100
|
+
<p role="status" data-svadmin-access-denied>{i18n.t('common.accessDenied')}</p>
|
|
101
|
+
{:else if query.isError}
|
|
102
|
+
<div role="alert">
|
|
103
|
+
<p>{i18n.t('common.operationFailed')}</p>
|
|
104
|
+
<Button type="button" variant="ghost" size="icon" title={i18n.t('common.retry')}
|
|
105
|
+
aria-label={i18n.t('common.retry')} disabled={query.isFetching} onclick={detail.refresh}>
|
|
106
|
+
<RefreshCw class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" />
|
|
107
|
+
</Button>
|
|
108
|
+
</div>
|
|
109
|
+
{:else if query.isSuccess}
|
|
110
|
+
{@const record = query.data.data}
|
|
94
111
|
{#if layout === 'grid'}
|
|
95
112
|
<Card.Root class="svadmin-u-2cd02d11d1af svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
96
113
|
<Card.Content class="svadmin-u-8a539c7fe216">
|
|
97
114
|
<dl class="svadmin-u-f3c543ad5fe9 svadmin-u-fa6acbf81d74 svadmin-u-3533cbce9e93 svadmin-u-4d9da416df16 {gridColumnClass}">
|
|
98
115
|
{#each showFields as field, i (i)}
|
|
99
|
-
{@const value =
|
|
100
|
-
{@const DisplayComponent = getDisplayComponent(field.type)}
|
|
116
|
+
{@const value = record[field.key]}
|
|
101
117
|
<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed {bordered ? 'svadmin-u-65fdbade2025 svadmin-u-5ceb636bd9f3 svadmin-u-cdf6e054e5e0 svadmin-u-b4cf72cd1cd8 svadmin-u-eb6e8b881acd svadmin-u-1884518576f2' : 'svadmin-u-eb6e8b881acd svadmin-u-1884518576f2 svadmin-u-65fdbade2025 svadmin-u-945ecb9a9005'}">
|
|
102
118
|
<dt class="svadmin-u-359090c2d529 svadmin-u-2689f3958069 svadmin-u-bfa603190748 svadmin-u-65281709dacf">{field.label}</dt>
|
|
103
119
|
<dd class="svadmin-u-359090c2d529 svadmin-u-e2327d142859 svadmin-u-d4108abe6359 svadmin-u-2689f3958069 svadmin-u-170cee3ff4e4">
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
{:else}
|
|
111
|
-
{value != null ? String(value) : '—'}
|
|
112
|
-
{/if}
|
|
120
|
+
<FieldDisplay
|
|
121
|
+
type={field.type}
|
|
122
|
+
{value}
|
|
123
|
+
options={field.options}
|
|
124
|
+
resourceName={field.resource}
|
|
125
|
+
/>
|
|
113
126
|
</dd>
|
|
114
127
|
</div>
|
|
115
128
|
{/each}
|
|
@@ -120,20 +133,16 @@
|
|
|
120
133
|
<Card.Root class="svadmin-u-2cd02d11d1af svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
121
134
|
<Card.Content class="svadmin-u-8a539c7fe216 svadmin-u-fa6acbf81d74 svadmin-u-3533cbce9e93">
|
|
122
135
|
{#each showFields as field, i (i)}
|
|
123
|
-
{@const value =
|
|
124
|
-
{@const DisplayComponent = getDisplayComponent(field.type)}
|
|
136
|
+
{@const value = record[field.key]}
|
|
125
137
|
<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-020ba687fa12 {isCompact ? 'svadmin-u-f0faeb26d656 svadmin-u-03b4dd7f172b svadmin-u-cc06a6575385 svadmin-u-7209fd2a90e7' : 'svadmin-u-f0faeb26d656 svadmin-u-7a9aabfcd059 svadmin-u-1b2d54a3fd12 svadmin-u-4701b613ff95'} {i % 2 === 1 ? 'svadmin-u-8a25a995eb8e' : ''}">
|
|
126
138
|
<div class="svadmin-u-b03f3c253900 {isCompact ? 'svadmin-u-359090c2d529' : 'svadmin-u-359090c2d529 svadmin-u-e2327d142859'} svadmin-u-2689f3958069 svadmin-u-bfa603190748 svadmin-u-65281709dacf svadmin-u-84bdd56b0e81">{field.label}</div>
|
|
127
139
|
<div class="svadmin-u-6b3526212209 {isCompact ? 'svadmin-u-359090c2d529 svadmin-u-e2327d142859' : 'svadmin-u-fc7473ca09eb'} svadmin-u-d4108abe6359">
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{:else}
|
|
135
|
-
{value != null ? String(value) : '—'}
|
|
136
|
-
{/if}
|
|
140
|
+
<FieldDisplay
|
|
141
|
+
type={field.type}
|
|
142
|
+
{value}
|
|
143
|
+
options={field.options}
|
|
144
|
+
resourceName={field.resource}
|
|
145
|
+
/>
|
|
137
146
|
</div>
|
|
138
147
|
</div>
|
|
139
148
|
{/each}
|
|
@@ -147,9 +156,6 @@
|
|
|
147
156
|
<Card.Root class="svadmin-u-2cd02d11d1af svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
148
157
|
<Card.Content class="svadmin-u-845f53365c8d svadmin-u-ca6bf63030aa">
|
|
149
158
|
<p class="svadmin-u-bfa603190748">{i18n.t('common.noData')}</p>
|
|
150
|
-
{#if query.isError}
|
|
151
|
-
<p class="svadmin-u-fc7473ca09eb svadmin-u-811148b13d1e svadmin-u-50d0d216a2f8">{(query.error as Error)?.message ?? i18n.t('common.operationFailed')}</p>
|
|
152
|
-
{/if}
|
|
153
159
|
</Card.Content>
|
|
154
160
|
</Card.Root>
|
|
155
161
|
{/if}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { definedOptions } from '@svadmin/core/options';
|
|
3
|
+
|
|
2
4
|
import { captureAdminContext } from '@svadmin/core';
|
|
3
5
|
import type { AccessControlProvider, Action, Identity, MenuItem } from '@svadmin/core';
|
|
4
6
|
import { getPath } from '../router-state.svelte.js';
|
|
@@ -107,11 +109,11 @@
|
|
|
107
109
|
async function checkAccess(provider: AccessControlProvider | null, resource: string, action: Action): Promise<boolean> {
|
|
108
110
|
if (!provider) return true;
|
|
109
111
|
|
|
110
|
-
const result = await provider.can({
|
|
112
|
+
const result = await provider.can(definedOptions({
|
|
111
113
|
resource,
|
|
112
114
|
action,
|
|
113
115
|
meta: adminContext.getProviderMeta(resource),
|
|
114
|
-
});
|
|
116
|
+
}));
|
|
115
117
|
return Array.isArray(result) ? (result[0]?.can ?? false) : result.can;
|
|
116
118
|
}
|
|
117
119
|
|
|
@@ -132,11 +134,11 @@
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
function snapshotCustomMenuItems(menuItems: MenuItem[]): MenuItem[] {
|
|
135
|
-
return menuItems.map((menuItem) => ({
|
|
137
|
+
return menuItems.map((menuItem) => (definedOptions({
|
|
136
138
|
...menuItem,
|
|
137
139
|
meta: menuItem.meta ? { ...menuItem.meta } : undefined,
|
|
138
140
|
children: menuItem.children ? snapshotCustomMenuItems(menuItem.children) : undefined,
|
|
139
|
-
}));
|
|
141
|
+
})));
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
async function visibleCustomMenuItem(menuItem: MenuItem, provider: AccessControlProvider | null): Promise<MenuItem | null> {
|
|
@@ -188,12 +190,12 @@
|
|
|
188
190
|
const items: NavItem[] = [{ path: '/', label: homeLabel, Icon: LayoutDashboard }];
|
|
189
191
|
for (const { r, can } of results) {
|
|
190
192
|
if (can) {
|
|
191
|
-
items.push({
|
|
193
|
+
items.push(definedOptions({
|
|
192
194
|
path: `/${r.name}`,
|
|
193
195
|
label: r.label,
|
|
194
196
|
Icon: (typeof r.icon === 'string' ? iconMap[r.icon] : r.icon) ?? iconMap[r.name] ?? Settings,
|
|
195
197
|
group: r.group,
|
|
196
|
-
});
|
|
198
|
+
}));
|
|
197
199
|
}
|
|
198
200
|
}
|
|
199
201
|
navItems = items;
|
|
@@ -454,14 +456,14 @@
|
|
|
454
456
|
<div class="svadmin-u-0e17f2bd9074 svadmin-u-7fcf9124b5df svadmin-u-6b7d6e21ccbd">
|
|
455
457
|
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-7e9a2a250cc3 svadmin-u-5f22e64f2282 svadmin-u-7660b450905a svadmin-u-ceb69a6b0e5f svadmin-u-ace81495deee svadmin-u-34516836730d">
|
|
456
458
|
<Avatar
|
|
457
|
-
src={(identity as Record<string, unknown>)
|
|
459
|
+
src={(identity as Record<string, unknown>)['avatar'] as string | undefined}
|
|
458
460
|
alt={identity.name ?? 'User'}
|
|
459
461
|
fallback={identity.name?.charAt(0).toUpperCase() ?? 'U'}
|
|
460
462
|
size="sm"
|
|
461
463
|
/>
|
|
462
464
|
<div class="svadmin-u-36e579c0b41c svadmin-u-7e0b7cdf1a94">
|
|
463
465
|
<p class="svadmin-u-f283ea9bea0e svadmin-u-a14daebf7748 svadmin-u-2689f3958069 svadmin-u-a7a63217e098">{identity.name}</p>
|
|
464
|
-
<p class="svadmin-u-f283ea9bea0e svadmin-u-d058ca6de60f svadmin-u-5f1ff8fe8768">{((identity as Record<string, unknown>)
|
|
466
|
+
<p class="svadmin-u-f283ea9bea0e svadmin-u-d058ca6de60f svadmin-u-5f1ff8fe8768">{((identity as Record<string, unknown>)['role'] || (identity as Record<string, unknown>)['roleName']) ?? 'User'}</p>
|
|
465
467
|
</div>
|
|
466
468
|
<button
|
|
467
469
|
class="svadmin-u-d0a52b312f7d svadmin-u-cbbf90f9a828 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-421ac2be5045 svadmin-u-3e3534b4c5df svadmin-u-646e10356266 svadmin-u-0b48b877be2a svadmin-u-ceb69a6b0e5f"
|
|
@@ -35,10 +35,12 @@
|
|
|
35
35
|
if (!containerEl) return;
|
|
36
36
|
const rect = containerEl.getBoundingClientRect();
|
|
37
37
|
|
|
38
|
+
const pointer = 'touches' in ev ? ev.touches.item(0) : ev;
|
|
39
|
+
if (!pointer) return;
|
|
38
40
|
const clientPos =
|
|
39
41
|
direction === 'horizontal'
|
|
40
|
-
?
|
|
41
|
-
:
|
|
42
|
+
? pointer.clientX - rect.left
|
|
43
|
+
: pointer.clientY - rect.top;
|
|
42
44
|
const totalSize = direction === 'horizontal' ? rect.width : rect.height;
|
|
43
45
|
|
|
44
46
|
if (totalSize > 0) {
|