@svadmin/ui 0.57.0 → 0.59.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/components/AICommandBar.svelte +34 -10
- package/dist/components/AdminApp.svelte +16 -5
- package/dist/components/AutoForm.svelte +4 -4
- package/dist/components/AutoTable.svelte +884 -217
- package/dist/components/AutoTable.svelte.d.ts +1 -1
- package/dist/components/CommandPalette.svelte +40 -17
- package/dist/components/ConfirmDialog.svelte +4 -3
- package/dist/components/ConfirmDialog.svelte.d.ts +1 -0
- package/dist/components/CopilotPanel.svelte +3 -3
- package/dist/components/DraggableHeader.svelte +15 -36
- package/dist/components/DraggableHeader.svelte.d.ts +13 -4
- package/dist/components/EditPage.svelte +4 -4
- package/dist/components/EmptyState.svelte +4 -2
- package/dist/components/Layout.svelte +8 -1
- package/dist/components/ListPage.svelte +4 -12
- package/dist/components/ListPage.svelte.d.ts +1 -1
- package/dist/components/RecordDetailDrawer.svelte +121 -0
- package/dist/components/RecordDetailDrawer.svelte.d.ts +9 -0
- package/dist/components/ResourceAccessGuard.svelte +68 -0
- package/dist/components/ResourceAccessGuard.svelte.d.ts +12 -0
- package/dist/components/ResourceOperationsPage.svelte +8 -3
- package/dist/components/ShowPage.svelte +5 -6
- package/dist/components/StatsCard.svelte +5 -3
- package/dist/components/TaskQueueDrawer.svelte +10 -1
- package/dist/components/TenantSwitcher.svelte +1 -0
- package/dist/components/Toast.svelte +35 -1
- package/dist/components/UndoableNotification.svelte +12 -6
- package/dist/components/UndoableNotification.svelte.d.ts +2 -0
- package/dist/components/buttons/CloneButton.svelte +9 -2
- package/dist/components/buttons/CreateButton.svelte +9 -2
- package/dist/components/buttons/DeleteButton.svelte +9 -2
- package/dist/components/buttons/EditButton.svelte +9 -2
- package/dist/components/buttons/ListButton.svelte +1 -1
- package/dist/components/buttons/ShowButton.svelte +9 -2
- package/dist/components/saved-list-views.d.ts +35 -0
- package/dist/components/saved-list-views.js +168 -0
- package/dist/components/ui/input/input.svelte +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +2 -2
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { captureAdminContext, toggleTheme } from '@svadmin/core';
|
|
2
|
+
import { captureAdminContext, toggleTheme, useNavigation } from '@svadmin/core';
|
|
3
3
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
4
4
|
import { Command } from './ui/command/index.js';
|
|
5
5
|
import * as Dialog from './ui/dialog/index.js';
|
|
6
6
|
import { Search, LayoutDashboard, Plus, Sun, FileText, Sparkles, Loader2 } from '@lucide/svelte';
|
|
7
7
|
import MarkdownRenderer from './MarkdownRenderer.svelte';
|
|
8
|
+
import CanAccess from './CanAccess.svelte';
|
|
8
9
|
|
|
9
10
|
const i18n = useTranslation();
|
|
10
11
|
|
|
11
12
|
let { open = $bindable(false) } = $props<{ open?: boolean }>();
|
|
12
13
|
const adminContext = captureAdminContext();
|
|
14
|
+
const navigation = useNavigation();
|
|
13
15
|
let searchValue = $state('');
|
|
14
16
|
|
|
15
17
|
const resources = $derived(adminContext.resources);
|
|
@@ -109,6 +111,7 @@
|
|
|
109
111
|
|
|
110
112
|
<Dialog.Dialog bind:open onOpenChange={(v: boolean) => !v && close()}>
|
|
111
113
|
<Dialog.DialogContent class="overflow-hidden p-0 sm:max-w-[600px] border-none shadow-2xl">
|
|
114
|
+
<Dialog.DialogTitle class="sr-only">{i18n.t('common.actions')}</Dialog.DialogTitle>
|
|
112
115
|
<Command.Root
|
|
113
116
|
shouldFilter={!aiMode}
|
|
114
117
|
class="flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground [&_[data-command-group-heading]]:text-muted-foreground [&_[data-command-group-heading]]:px-2 [&_[data-command-group-heading]]:py-1.5 [&_[data-command-group-heading]]:text-xs [&_[data-command-group-heading]]:font-semibold [&_[data-command-group-heading]]:uppercase [&_[data-command-group-heading]]:tracking-wider"
|
|
@@ -127,7 +130,8 @@
|
|
|
127
130
|
/>
|
|
128
131
|
{#if searchValue.trim() && !aiMode}
|
|
129
132
|
<button
|
|
130
|
-
|
|
133
|
+
type="button"
|
|
134
|
+
class="absolute right-3 flex items-center gap-1 text-[10px] font-medium text-warning bg-warning/10 hover:bg-warning/20 px-2 py-1 rounded transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
131
135
|
onclick={askAI}
|
|
132
136
|
>
|
|
133
137
|
<Sparkles class="h-3 w-3" />
|
|
@@ -149,10 +153,19 @@
|
|
|
149
153
|
{i18n.t('common.home')}
|
|
150
154
|
</Command.Item>
|
|
151
155
|
{#each resources as r, _i (_i)}
|
|
152
|
-
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
{#if adminContext.accessControlProvider}
|
|
157
|
+
<CanAccess resource={r.name} action="list">
|
|
158
|
+
<Command.Item value={r.name} onSelect={() => act(() => navigation.list(r.name))} class={itemClass}>
|
|
159
|
+
<FileText class="h-4 w-4 text-muted-foreground" />
|
|
160
|
+
{r.label}
|
|
161
|
+
</Command.Item>
|
|
162
|
+
</CanAccess>
|
|
163
|
+
{:else}
|
|
164
|
+
<Command.Item value={r.name} onSelect={() => act(() => navigation.list(r.name))} class={itemClass}>
|
|
165
|
+
<FileText class="h-4 w-4 text-muted-foreground" />
|
|
166
|
+
{r.label}
|
|
167
|
+
</Command.Item>
|
|
168
|
+
{/if}
|
|
156
169
|
{/each}
|
|
157
170
|
</Command.Group>
|
|
158
171
|
|
|
@@ -161,10 +174,21 @@
|
|
|
161
174
|
<!-- Actions -->
|
|
162
175
|
<Command.Group heading={i18n.t('common.actions')}>
|
|
163
176
|
{#each resources as r, _i (_i)}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
177
|
+
{#if r.canCreate !== false}
|
|
178
|
+
{#if adminContext.accessControlProvider}
|
|
179
|
+
<CanAccess resource={r.name} action="create">
|
|
180
|
+
<Command.Item value={`create-${r.name}`} onSelect={() => act(() => navigation.create(r.name))} class={itemClass}>
|
|
181
|
+
<Plus class="h-4 w-4 text-muted-foreground" />
|
|
182
|
+
{i18n.t('common.create')} {r.label}
|
|
183
|
+
</Command.Item>
|
|
184
|
+
</CanAccess>
|
|
185
|
+
{:else}
|
|
186
|
+
<Command.Item value={`create-${r.name}`} onSelect={() => act(() => navigation.create(r.name))} class={itemClass}>
|
|
187
|
+
<Plus class="h-4 w-4 text-muted-foreground" />
|
|
188
|
+
{i18n.t('common.create')} {r.label}
|
|
189
|
+
</Command.Item>
|
|
190
|
+
{/if}
|
|
191
|
+
{/if}
|
|
168
192
|
{/each}
|
|
169
193
|
<Command.Item value="toggle-theme" onSelect={() => act(() => toggleTheme())} class={itemClass}>
|
|
170
194
|
<Sun class="h-4 w-4 text-muted-foreground" />
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
import ErrorPage from './ErrorPage.svelte';
|
|
74
74
|
import SystemPageShell from './SystemPageShell.svelte';
|
|
75
75
|
import LazyPage from './LazyPage.svelte';
|
|
76
|
+
import ResourceAccessGuard from './ResourceAccessGuard.svelte';
|
|
76
77
|
import { Button } from './ui/button/index.js';
|
|
77
78
|
import { Input } from './ui/input/index.js';
|
|
78
79
|
import { Badge } from './ui/badge/index.js';
|
|
@@ -609,27 +610,37 @@
|
|
|
609
610
|
{:else if (renderedRoute === '/:resource' || renderedRoute === '/:parent/:parentId/:resource') && renderedHasRouteResource}
|
|
610
611
|
{#key renderedParams.resource}
|
|
611
612
|
{@const Comp = renderedResourcePages?.list ?? mergedComponents.AutoTable}
|
|
612
|
-
<
|
|
613
|
+
<ResourceAccessGuard resourceName={renderedParams.resource} action="list">
|
|
614
|
+
<Comp resourceName={renderedParams.resource} />
|
|
615
|
+
</ResourceAccessGuard>
|
|
613
616
|
{/key}
|
|
614
617
|
{:else if (renderedRoute === '/:resource/create' || renderedRoute === '/:parent/:parentId/:resource/create') && renderedHasRouteResource}
|
|
615
618
|
{#key renderedParams.resource}
|
|
616
619
|
{@const Comp = renderedResourcePages?.create ?? mergedComponents.AutoForm}
|
|
617
|
-
<
|
|
620
|
+
<ResourceAccessGuard resourceName={renderedParams.resource} action="create">
|
|
621
|
+
<Comp resourceName={renderedParams.resource} mode="create" />
|
|
622
|
+
</ResourceAccessGuard>
|
|
618
623
|
{/key}
|
|
619
624
|
{:else if (renderedRoute === '/:resource/edit/:id' || renderedRoute === '/:resource/:id/edit' || renderedRoute === '/:parent/:parentId/:resource/edit/:id') && renderedHasRouteResource}
|
|
620
625
|
{#key `${renderedParams.resource}-${renderedParams.id}`}
|
|
621
626
|
{@const Comp = renderedResourcePages?.edit ?? mergedComponents.AutoForm}
|
|
622
|
-
<
|
|
627
|
+
<ResourceAccessGuard resourceName={renderedParams.resource} action="edit" id={renderedParams.id}>
|
|
628
|
+
<Comp resourceName={renderedParams.resource} mode="edit" id={renderedParams.id} />
|
|
629
|
+
</ResourceAccessGuard>
|
|
623
630
|
{/key}
|
|
624
631
|
{:else if (renderedRoute === '/:resource/show/:id' || renderedRoute === '/:resource/:id' || renderedRoute === '/:parent/:parentId/:resource/show/:id') && renderedHasRouteResource}
|
|
625
632
|
{#key `${renderedParams.resource}-${renderedParams.id}`}
|
|
626
633
|
{@const Comp = renderedResourcePages?.show ?? mergedComponents.ShowPage}
|
|
627
|
-
<
|
|
634
|
+
<ResourceAccessGuard resourceName={renderedParams.resource} action="show" id={renderedParams.id}>
|
|
635
|
+
<Comp resourceName={renderedParams.resource} id={renderedParams.id} />
|
|
636
|
+
</ResourceAccessGuard>
|
|
628
637
|
{/key}
|
|
629
638
|
{:else if (renderedRoute === '/:resource/clone/:id' || renderedRoute === '/:parent/:parentId/:resource/clone/:id') && renderedHasRouteResource}
|
|
630
639
|
{#key `${renderedParams.resource}-clone-${renderedParams.id}`}
|
|
631
640
|
{@const Comp = renderedResourcePages?.clone ?? mergedComponents.AutoForm}
|
|
632
|
-
<
|
|
641
|
+
<ResourceAccessGuard resourceName={renderedParams.resource} action="create" id={renderedParams.id} requireSourceRead>
|
|
642
|
+
<Comp resourceName={renderedParams.resource} mode="clone" id={renderedParams.id} />
|
|
643
|
+
</ResourceAccessGuard>
|
|
633
644
|
{/key}
|
|
634
645
|
{:else}
|
|
635
646
|
{@const ErrorComp = mergedComponents.ErrorPage || ErrorPage}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { useForm, getResource, deriveValidator, useNavigation } from '@svadmin/core';
|
|
3
3
|
import { slide } from 'svelte/transition';
|
|
4
4
|
import type { FieldDefinition } from '@svadmin/core';
|
|
5
5
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
onSuccess,
|
|
46
46
|
onNavigationGuardReady,
|
|
47
47
|
}: Props = $props();
|
|
48
|
-
const
|
|
48
|
+
const navigation = useNavigation();
|
|
49
49
|
const isReadonly = $derived(mode === 'show');
|
|
50
50
|
const isCompact = $derived(density === 'compact');
|
|
51
51
|
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
<div class="flex items-center gap-4">
|
|
197
197
|
<TooltipButton
|
|
198
198
|
tooltip={i18n.t('common.back')}
|
|
199
|
-
onclick={() => guardNavigate(() =>
|
|
199
|
+
onclick={() => guardNavigate(() => navigation.list(resourceName))}
|
|
200
200
|
>
|
|
201
201
|
<ArrowLeft class="h-5 w-5" />
|
|
202
202
|
</TooltipButton>
|
|
@@ -311,7 +311,7 @@
|
|
|
311
311
|
type="button"
|
|
312
312
|
variant="outline"
|
|
313
313
|
size={isCompact ? 'sm' : 'default'}
|
|
314
|
-
onclick={() => guardNavigate(() =>
|
|
314
|
+
onclick={() => guardNavigate(() => navigation.list(resourceName))}
|
|
315
315
|
>
|
|
316
316
|
{i18n.t('common.cancel')}
|
|
317
317
|
</Button>
|