@svadmin/ui 0.36.0 → 0.38.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/app.css +76 -104
- package/dist/components/AdminApp.svelte +18 -14
- package/dist/components/AutoForm.svelte +32 -47
- package/dist/components/AutoTable.svelte +26 -31
- package/dist/components/CanAccess.svelte +3 -2
- package/dist/components/CanAccess.svelte.d.ts +1 -0
- package/dist/components/CreateInferencer.svelte +13 -0
- package/dist/components/CreateInferencer.svelte.d.ts +7 -0
- package/dist/components/EditInferencer.svelte +21 -0
- package/dist/components/EditInferencer.svelte.d.ts +9 -0
- package/dist/components/Header.svelte +6 -6
- package/dist/components/Layout.svelte +21 -11
- package/dist/components/ListInferencer.svelte +14 -0
- package/dist/components/ListInferencer.svelte.d.ts +7 -0
- package/dist/components/PageHeader.svelte +7 -9
- package/dist/components/ResourceInferencer.svelte +28 -0
- package/dist/components/ResourceInferencer.svelte.d.ts +7 -0
- package/dist/components/ResourceOperationsPage.svelte +168 -21
- package/dist/components/ResourceOperationsPage.svelte.d.ts +2 -0
- package/dist/components/SettingsPage.svelte +76 -41
- package/dist/components/ShowInferencer.svelte +21 -0
- package/dist/components/ShowInferencer.svelte.d.ts +9 -0
- package/dist/components/ShowPage.svelte +11 -11
- package/dist/components/ShowPage.svelte.d.ts +1 -0
- package/dist/components/Sidebar.svelte +214 -300
- package/dist/components/Sidebar.svelte.d.ts +0 -1
- package/dist/components/SidebarItem.svelte +141 -108
- package/dist/components/account/TeamMembersPage.svelte +9 -3
- package/dist/components/buttons/CloneButton.svelte +10 -2
- package/dist/components/buttons/CloneButton.svelte.d.ts +2 -4
- package/dist/components/buttons/CreateButton.svelte +9 -2
- package/dist/components/buttons/CreateButton.svelte.d.ts +2 -4
- package/dist/components/buttons/DeleteButton.svelte +10 -2
- package/dist/components/buttons/DeleteButton.svelte.d.ts +2 -4
- package/dist/components/buttons/EditButton.svelte +10 -2
- package/dist/components/buttons/EditButton.svelte.d.ts +2 -4
- package/dist/components/buttons/ExportButton.svelte +9 -2
- package/dist/components/buttons/ExportButton.svelte.d.ts +2 -4
- package/dist/components/buttons/ImportButton.svelte +9 -2
- package/dist/components/buttons/ImportButton.svelte.d.ts +2 -4
- package/dist/components/buttons/ShowButton.svelte +10 -2
- package/dist/components/buttons/ShowButton.svelte.d.ts +2 -4
- package/dist/components/buttons/access-control.d.ts +7 -0
- package/dist/components/buttons/access-control.js +5 -0
- package/dist/components/ui/sheet/sheet.svelte +3 -3
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/router-state.svelte.js +15 -0
- package/package.json +2 -2
|
@@ -6,7 +6,6 @@ type $$ComponentProps = {
|
|
|
6
6
|
onToggle: () => void;
|
|
7
7
|
onLogout: () => void;
|
|
8
8
|
menu?: MenuItem[];
|
|
9
|
-
/** 'hash' for SPA hash routing, 'path' for SvelteKit/filesystem routing, 'auto' to detect */
|
|
10
9
|
routeMode?: 'hash' | 'path' | 'auto';
|
|
11
10
|
};
|
|
12
11
|
declare const Sidebar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import type { MenuItem } from '@svadmin/core';
|
|
3
3
|
import * as Collapsible from './ui/collapsible/index.js';
|
|
4
4
|
import * as Tooltip from './ui/tooltip/index.js';
|
|
5
|
+
import { Badge } from './ui/badge/index.js';
|
|
5
6
|
import SidebarItem from './SidebarItem.svelte';
|
|
6
7
|
import {
|
|
7
8
|
LayoutDashboard, FileText, Users, Settings, Home,
|
|
8
9
|
ChevronDown, Folder, ExternalLink, Repeat, ClipboardCheck, SlidersHorizontal, AlertTriangle,
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
Mail, Map as MapIcon, MessageSquare, Send, ShoppingBag, Star, Briefcase, Building2, Calendar, Bell, Clock, Tag, Trash2, Palette, Layout,
|
|
11
|
+
Download, ListTodo, TrendingUp, Sparkles, Images, Bot, Key, KeyRound, CreditCard, BookOpen, Wrench, Shield,
|
|
12
|
+
Circle,
|
|
11
13
|
} from '@lucide/svelte';
|
|
12
14
|
|
|
13
15
|
import { formatLink } from '@svadmin/core/router';
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
const iconMap: Record<string, typeof LayoutDashboard> = {
|
|
23
25
|
dashboard: LayoutDashboard,
|
|
24
26
|
posts: FileText,
|
|
27
|
+
file: FileText,
|
|
25
28
|
users: Users,
|
|
26
29
|
settings: Settings,
|
|
27
30
|
home: Home,
|
|
@@ -30,21 +33,38 @@
|
|
|
30
33
|
'clipboard-check': ClipboardCheck,
|
|
31
34
|
'sliders-horizontal': SlidersHorizontal,
|
|
32
35
|
'alert-triangle': AlertTriangle,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'
|
|
36
|
-
|
|
37
|
-
'
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
mail: Mail,
|
|
37
|
+
map: MapIcon,
|
|
38
|
+
'message-square': MessageSquare,
|
|
39
|
+
send: Send,
|
|
40
|
+
'shopping-bag': ShoppingBag,
|
|
41
|
+
star: Star,
|
|
42
|
+
briefcase: Briefcase,
|
|
43
|
+
building: Building2,
|
|
44
|
+
calendar: Calendar,
|
|
45
|
+
bell: Bell,
|
|
46
|
+
clock: Clock,
|
|
47
|
+
tag: Tag,
|
|
48
|
+
trash: Trash2,
|
|
49
|
+
palette: Palette,
|
|
50
|
+
layout: Layout,
|
|
51
|
+
download: Download,
|
|
52
|
+
'list-todo': ListTodo,
|
|
53
|
+
'trending-up': TrendingUp,
|
|
54
|
+
sparkles: Sparkles,
|
|
55
|
+
images: Images,
|
|
56
|
+
bot: Bot,
|
|
57
|
+
key: Key,
|
|
41
58
|
'key-round': KeyRound,
|
|
42
|
-
'
|
|
59
|
+
'credit-card': CreditCard,
|
|
60
|
+
'book-open': BookOpen,
|
|
61
|
+
wrench: Wrench,
|
|
62
|
+
shield: Shield,
|
|
43
63
|
};
|
|
44
64
|
|
|
45
|
-
function getIcon(name?:
|
|
46
|
-
if (!name) return depth === 0 ? Settings :
|
|
47
|
-
if (typeof name !== 'string') return name
|
|
65
|
+
function getIcon(name?: unknown): typeof LayoutDashboard {
|
|
66
|
+
if (!name) return depth === 0 ? Settings : Circle;
|
|
67
|
+
if (typeof name !== 'string') return name as typeof LayoutDashboard;
|
|
48
68
|
return iconMap[name] ?? Settings;
|
|
49
69
|
}
|
|
50
70
|
|
|
@@ -54,95 +74,91 @@
|
|
|
54
74
|
|
|
55
75
|
function isActive(itemHref: string | undefined): boolean {
|
|
56
76
|
if (!itemHref) return false;
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
const baseHref = itemHref.replace(/^#/, '').split(/[?#]/)[0] || '/';
|
|
78
|
+
const baseCurrentPath = currentPath.replace(/^#/, '').split(/[?#]/)[0] || '/';
|
|
79
|
+
if (baseHref === '/') return baseCurrentPath === '/';
|
|
80
|
+
return baseCurrentPath.startsWith(baseHref) && (baseCurrentPath.length === baseHref.length || baseCurrentPath[baseHref.length] === '/');
|
|
59
81
|
}
|
|
60
82
|
|
|
61
|
-
/** Check if any child (recursively) is active — used to auto-open parent groups */
|
|
62
83
|
function hasActiveChild(menuItem: MenuItem): boolean {
|
|
63
84
|
if (isActive(menuItem.href)) return true;
|
|
64
85
|
return menuItem.children?.some((c: MenuItem) => hasActiveChild(c)) ?? false;
|
|
65
86
|
}
|
|
66
87
|
|
|
67
|
-
function getItemBadge(menuItem: MenuItem): string | undefined {
|
|
68
|
-
const meta = menuItem.meta as (MenuItem['meta'] & { badge?: unknown }) | undefined;
|
|
69
|
-
return typeof meta?.badge === 'string' ? meta.badge : undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function aggregateBadges(menuItems: MenuItem[] | undefined): string | undefined {
|
|
73
|
-
const total = menuItems?.reduce((sum, menuItem) => {
|
|
74
|
-
const ownBadge = Number(getItemBadge(menuItem));
|
|
75
|
-
const childBadge = Number(aggregateBadges(menuItem.children));
|
|
76
|
-
return sum + (Number.isFinite(ownBadge) ? ownBadge : 0) + (Number.isFinite(childBadge) ? childBadge : 0);
|
|
77
|
-
}, 0) ?? 0;
|
|
78
|
-
|
|
79
|
-
return total > 0 ? String(total) : undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
88
|
const hasChildren = $derived(item.children && item.children.length > 0);
|
|
83
89
|
const active = $derived(isActive(item.href));
|
|
84
90
|
const childActive = $derived(hasActiveChild(item));
|
|
85
91
|
const isExternal = $derived(item.target === '_blank' || item.href?.startsWith('http'));
|
|
86
|
-
const badge = $derived(getItemBadge(item));
|
|
87
|
-
const childrenBadge = $derived(aggregateBadges(item.children));
|
|
88
92
|
|
|
89
93
|
const finalHref = $derived(isExternal ? item.href : (item.href ? formatLink(item.href) : undefined));
|
|
90
|
-
// Auto-open the active branch once, then respect manual collapse/expand.
|
|
91
|
-
let isOpen = $state(false);
|
|
92
|
-
let touchedOpen = $state(false);
|
|
93
|
-
|
|
94
|
-
function setOpen(nextOpen: boolean) {
|
|
95
|
-
touchedOpen = true;
|
|
96
|
-
isOpen = nextOpen;
|
|
97
|
-
}
|
|
98
94
|
|
|
99
|
-
$
|
|
100
|
-
|
|
101
|
-
});
|
|
95
|
+
let isOpen = $state(false);
|
|
96
|
+
$effect(() => { if (childActive) isOpen = true; });
|
|
102
97
|
|
|
103
98
|
const Icon = $derived(getIcon(item.icon));
|
|
104
99
|
const label = $derived(getLabel(item));
|
|
100
|
+
const soon = $derived(Boolean((item.meta as { soon?: boolean } | undefined)?.soon));
|
|
101
|
+
|
|
102
|
+
const isTopLevel = $derived(depth === 0);
|
|
103
|
+
const indentPx = $derived(isTopLevel ? 10 : 10 + depth * 16);
|
|
105
104
|
</script>
|
|
106
105
|
|
|
107
106
|
{#if item.meta?.hidden}
|
|
108
|
-
<!-- hidden
|
|
107
|
+
<!-- hidden -->
|
|
109
108
|
{:else if hasChildren && !collapsed}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
109
|
+
{#if isTopLevel}
|
|
110
|
+
<div class="mt-3 first:mt-0">
|
|
111
|
+
<Collapsible.Root bind:open={isOpen}>
|
|
112
|
+
<Collapsible.Trigger
|
|
113
|
+
class="sidebar-menu-item flex w-full items-center justify-between rounded-md py-[7px] text-[13px] font-medium transition-colors duration-150
|
|
114
|
+
{childActive
|
|
115
|
+
? 'text-sidebar-foreground'
|
|
116
|
+
: 'text-sidebar-foreground/70 hover:text-sidebar-foreground'}"
|
|
117
|
+
style="padding-left: {indentPx}px; padding-right: 10px"
|
|
118
|
+
>
|
|
119
|
+
<span class="flex items-center gap-2.5">
|
|
120
|
+
<Icon class="h-[18px] w-[18px] flex-shrink-0 {childActive ? 'text-primary' : 'text-sidebar-foreground/50'}" />
|
|
121
|
+
<span>{label}</span>
|
|
122
|
+
</span>
|
|
123
|
+
<ChevronDown class="h-3.5 w-3.5 text-sidebar-foreground/40 transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
|
|
124
|
+
</Collapsible.Trigger>
|
|
125
|
+
<Collapsible.Content>
|
|
126
|
+
<div class="relative mt-[2px]">
|
|
127
|
+
<div class="absolute left-[19px] top-0 bottom-0 w-px bg-border/60"></div>
|
|
128
|
+
<div class="space-y-[1px]">
|
|
129
|
+
{#each item.children as child, _i (_i)}
|
|
130
|
+
<SidebarItem item={child} {currentPath} {collapsed} depth={depth + 1} />
|
|
131
|
+
{/each}
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</Collapsible.Content>
|
|
135
|
+
</Collapsible.Root>
|
|
136
|
+
</div>
|
|
137
|
+
{:else}
|
|
138
|
+
<Collapsible.Root bind:open={isOpen}>
|
|
139
|
+
<Collapsible.Trigger
|
|
140
|
+
class="sidebar-menu-item flex w-full items-center justify-between rounded-md py-[6px] text-[13px] font-normal transition-colors duration-150
|
|
141
|
+
{childActive
|
|
142
|
+
? 'text-primary font-medium'
|
|
143
|
+
: 'text-sidebar-foreground/65 hover:text-sidebar-foreground'}"
|
|
144
|
+
style="padding-left: {indentPx}px; padding-right: 10px"
|
|
137
145
|
>
|
|
138
|
-
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
<span class="flex items-center gap-2.5">
|
|
147
|
+
<Circle class="h-[5px] w-[5px] flex-shrink-0 {childActive ? 'fill-primary text-primary' : 'fill-sidebar-foreground/30 text-sidebar-foreground/30'}" />
|
|
148
|
+
<span>{label}</span>
|
|
149
|
+
</span>
|
|
150
|
+
<ChevronDown class="h-3 w-3 text-sidebar-foreground/35 transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
|
|
151
|
+
</Collapsible.Trigger>
|
|
152
|
+
<Collapsible.Content>
|
|
153
|
+
<div class="space-y-[1px] mt-[1px]">
|
|
154
|
+
{#each item.children as child, _i (_i)}
|
|
155
|
+
<SidebarItem item={child} {currentPath} {collapsed} depth={depth + 1} />
|
|
156
|
+
{/each}
|
|
157
|
+
</div>
|
|
158
|
+
</Collapsible.Content>
|
|
159
|
+
</Collapsible.Root>
|
|
160
|
+
{/if}
|
|
144
161
|
{:else if collapsed}
|
|
145
|
-
<!-- Collapsed sidebar: icon-only with tooltip -->
|
|
146
162
|
<Tooltip.Root>
|
|
147
163
|
<Tooltip.Trigger>
|
|
148
164
|
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
@@ -151,14 +167,12 @@
|
|
|
151
167
|
href={finalHref}
|
|
152
168
|
target={isExternal ? '_blank' : undefined}
|
|
153
169
|
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
154
|
-
class="
|
|
170
|
+
class="sidebar-menu-item flex items-center justify-center rounded-md px-2 py-[7px] transition-colors duration-150
|
|
155
171
|
{active
|
|
156
|
-
? 'text-primary'
|
|
157
|
-
: 'text-sidebar-foreground/
|
|
158
|
-
data-sidebar={depth === 0 ? "menu-button" : "menu-sub-button"}
|
|
159
|
-
data-active={active ? "true" : "false"}
|
|
172
|
+
? 'sidebar-menu-item-active bg-sidebar-accent text-primary'
|
|
173
|
+
: 'text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground'}"
|
|
160
174
|
>
|
|
161
|
-
<Icon class="h-
|
|
175
|
+
<Icon class="h-[18px] w-[18px] flex-shrink-0" />
|
|
162
176
|
</a>
|
|
163
177
|
{/snippet}
|
|
164
178
|
</Tooltip.Trigger>
|
|
@@ -167,26 +181,45 @@
|
|
|
167
181
|
</Tooltip.Content>
|
|
168
182
|
</Tooltip.Root>
|
|
169
183
|
{:else}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
{#if isTopLevel}
|
|
185
|
+
<a
|
|
186
|
+
href={finalHref}
|
|
187
|
+
target={isExternal ? '_blank' : undefined}
|
|
188
|
+
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
189
|
+
class="sidebar-menu-item flex items-center gap-2.5 rounded-md py-[7px] text-[13px] font-medium transition-colors duration-150
|
|
190
|
+
{active
|
|
191
|
+
? 'sidebar-menu-item-active text-primary bg-sidebar-accent'
|
|
192
|
+
: 'text-sidebar-foreground/70 hover:text-sidebar-foreground hover:bg-sidebar-accent/50'}"
|
|
193
|
+
style="padding-left: {indentPx}px; padding-right: 10px"
|
|
194
|
+
>
|
|
195
|
+
<Icon class="h-[18px] w-[18px] flex-shrink-0 {active ? 'text-primary' : 'text-sidebar-foreground/50'}" />
|
|
196
|
+
<span class="flex-1">{label}</span>
|
|
197
|
+
{#if soon}
|
|
198
|
+
<Badge variant="secondary" class="h-[18px] px-1.5 text-[10px] font-medium">Soon</Badge>
|
|
199
|
+
{/if}
|
|
200
|
+
{#if isExternal}
|
|
201
|
+
<ExternalLink class="h-3 w-3 opacity-40" />
|
|
202
|
+
{/if}
|
|
203
|
+
</a>
|
|
204
|
+
{:else}
|
|
205
|
+
<a
|
|
206
|
+
href={finalHref}
|
|
207
|
+
target={isExternal ? '_blank' : undefined}
|
|
208
|
+
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
209
|
+
class="sidebar-menu-item flex items-center gap-2.5 rounded-md py-[6px] text-[13px] font-normal transition-colors duration-150
|
|
210
|
+
{active
|
|
211
|
+
? 'sidebar-menu-item-active text-primary font-medium'
|
|
212
|
+
: 'text-sidebar-foreground/65 hover:text-sidebar-foreground'}"
|
|
213
|
+
style="padding-left: {indentPx}px; padding-right: 10px"
|
|
214
|
+
>
|
|
215
|
+
<Circle class="h-[5px] w-[5px] flex-shrink-0 {active ? 'fill-primary text-primary' : 'fill-sidebar-foreground/30 text-sidebar-foreground/30'}" />
|
|
216
|
+
<span class="flex-1">{label}</span>
|
|
217
|
+
{#if soon}
|
|
218
|
+
<Badge variant="secondary" class="h-[18px] px-1.5 text-[10px] font-medium">Soon</Badge>
|
|
219
|
+
{/if}
|
|
220
|
+
{#if isExternal}
|
|
221
|
+
<ExternalLink class="h-3 w-3 opacity-40" />
|
|
222
|
+
{/if}
|
|
223
|
+
</a>
|
|
224
|
+
{/if}
|
|
192
225
|
{/if}
|
|
@@ -46,13 +46,19 @@
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const initials = (name: string) => name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
|
49
|
+
|
|
50
|
+
function statusLabel(status: TeamMember['status']) {
|
|
51
|
+
if (status === 'active') return t('account.active');
|
|
52
|
+
if (status === 'invited') return t('account.invited');
|
|
53
|
+
return t('account.inactive');
|
|
54
|
+
}
|
|
49
55
|
</script>
|
|
50
56
|
|
|
51
57
|
<div class="space-y-6" data-svadmin-content-page="account">
|
|
52
58
|
<div class="flex items-start justify-between gap-4">
|
|
53
59
|
<div>
|
|
54
|
-
<h2 class="text-xl font-semibold text-foreground">{t('account.
|
|
55
|
-
<p class="mt-1 text-sm text-muted-foreground">{t('account.
|
|
60
|
+
<h2 class="text-xl font-semibold text-foreground">{t('account.teamMembers')}</h2>
|
|
61
|
+
<p class="mt-1 text-sm text-muted-foreground">{t('account.teamMembersDescription')}</p>
|
|
56
62
|
</div>
|
|
57
63
|
<Button size="sm">
|
|
58
64
|
<UserPlus class="h-4 w-4 mr-1" />{t('account.inviteMember')}
|
|
@@ -82,7 +88,7 @@
|
|
|
82
88
|
<p class="text-xs text-muted-foreground truncate">{member.email}</p>
|
|
83
89
|
<div class="mt-2 flex flex-wrap gap-1.5">
|
|
84
90
|
<span class="rounded-full px-2 py-0.5 text-[10px] font-semibold {roleColors[member.role]}">{member.role}</span>
|
|
85
|
-
<span class="rounded-full px-2 py-0.5 text-[10px] font-semibold {statusColors[member.status]}">{member.status}</span>
|
|
91
|
+
<span class="rounded-full px-2 py-0.5 text-[10px] font-semibold {statusColors[member.status]}">{statusLabel(member.status)}</span>
|
|
86
92
|
</div>
|
|
87
93
|
<div class="mt-2 flex items-center justify-between">
|
|
88
94
|
<span class="text-xs text-muted-foreground">{member.department}</span>
|
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
import { useNavigation, useCan, t } from '@svadmin/core';
|
|
3
3
|
import { Button } from '../ui/button/index.js';
|
|
4
4
|
import { Copy } from '@lucide/svelte';
|
|
5
|
+
import type { ButtonAccessControl } from './access-control';
|
|
6
|
+
import { withRecordId } from './access-control';
|
|
5
7
|
|
|
6
8
|
let { resource, recordItemId, hideText = false, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
|
|
7
9
|
resource: string;
|
|
8
10
|
recordItemId: string | number;
|
|
9
11
|
hideText?: boolean;
|
|
10
|
-
accessControl?:
|
|
12
|
+
accessControl?: ButtonAccessControl;
|
|
11
13
|
class?: string;
|
|
12
14
|
}>();
|
|
13
15
|
|
|
14
16
|
const nav = useNavigation();
|
|
15
|
-
const can = useCan(() => ({
|
|
17
|
+
const can = useCan(() => ({
|
|
18
|
+
resource,
|
|
19
|
+
action: 'create',
|
|
20
|
+
params: withRecordId(accessControl?.params, recordItemId),
|
|
21
|
+
meta: accessControl?.meta,
|
|
22
|
+
queryOptions: { enabled: accessControl?.enabled ?? true }
|
|
23
|
+
}));
|
|
16
24
|
const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
|
|
17
25
|
</script>
|
|
18
26
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import type { ButtonAccessControl } from './access-control';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
resource: string;
|
|
3
4
|
recordItemId: string | number;
|
|
4
5
|
hideText?: boolean;
|
|
5
|
-
accessControl?:
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
hideIfUnauthorized?: boolean;
|
|
8
|
-
};
|
|
6
|
+
accessControl?: ButtonAccessControl;
|
|
9
7
|
class?: string;
|
|
10
8
|
};
|
|
11
9
|
declare const CloneButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
import { useNavigation, useCan, t } from '@svadmin/core';
|
|
3
3
|
import { Button } from '../ui/button/index.js';
|
|
4
4
|
import { Plus } from '@lucide/svelte';
|
|
5
|
+
import type { ButtonAccessControl } from './access-control';
|
|
5
6
|
|
|
6
7
|
let { resource, hideText = false, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
|
|
7
8
|
resource: string;
|
|
8
9
|
hideText?: boolean;
|
|
9
|
-
accessControl?:
|
|
10
|
+
accessControl?: ButtonAccessControl;
|
|
10
11
|
class?: string;
|
|
11
12
|
}>();
|
|
12
13
|
|
|
13
14
|
const nav = useNavigation();
|
|
14
|
-
const can = useCan(() => ({
|
|
15
|
+
const can = useCan(() => ({
|
|
16
|
+
resource,
|
|
17
|
+
action: 'create',
|
|
18
|
+
params: accessControl?.params,
|
|
19
|
+
meta: accessControl?.meta,
|
|
20
|
+
queryOptions: { enabled: accessControl?.enabled ?? true }
|
|
21
|
+
}));
|
|
15
22
|
const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
|
|
16
23
|
</script>
|
|
17
24
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import type { ButtonAccessControl } from './access-control';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
resource: string;
|
|
3
4
|
hideText?: boolean;
|
|
4
|
-
accessControl?:
|
|
5
|
-
enabled?: boolean;
|
|
6
|
-
hideIfUnauthorized?: boolean;
|
|
7
|
-
};
|
|
5
|
+
accessControl?: ButtonAccessControl;
|
|
8
6
|
class?: string;
|
|
9
7
|
};
|
|
10
8
|
declare const CreateButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { useDelete, useCan, t } from '@svadmin/core';
|
|
3
3
|
import { Button } from '../ui/button/index.js';
|
|
4
4
|
import { Trash2 } from '@lucide/svelte';
|
|
5
|
+
import type { ButtonAccessControl } from './access-control';
|
|
6
|
+
import { withRecordId } from './access-control';
|
|
5
7
|
|
|
6
8
|
let {
|
|
7
9
|
resource, recordItemId, hideText = false,
|
|
@@ -10,14 +12,20 @@
|
|
|
10
12
|
resource: string;
|
|
11
13
|
recordItemId: string | number;
|
|
12
14
|
hideText?: boolean;
|
|
13
|
-
accessControl?:
|
|
15
|
+
accessControl?: ButtonAccessControl;
|
|
14
16
|
onSuccess?: () => void;
|
|
15
17
|
undoable?: boolean;
|
|
16
18
|
class?: string;
|
|
17
19
|
}>();
|
|
18
20
|
|
|
19
21
|
const deleteMut = useDelete({ get resource() { return resource; }, get mutationMode() { return undoable ? 'undoable' as const : 'pessimistic' as const; } });
|
|
20
|
-
const can = useCan(() => ({
|
|
22
|
+
const can = useCan(() => ({
|
|
23
|
+
resource,
|
|
24
|
+
action: 'delete',
|
|
25
|
+
params: withRecordId(accessControl?.params, recordItemId),
|
|
26
|
+
meta: accessControl?.meta,
|
|
27
|
+
queryOptions: { enabled: accessControl?.enabled ?? true }
|
|
28
|
+
}));
|
|
21
29
|
const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
|
|
22
30
|
let confirming = $state(false);
|
|
23
31
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import type { ButtonAccessControl } from './access-control';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
resource: string;
|
|
3
4
|
recordItemId: string | number;
|
|
4
5
|
hideText?: boolean;
|
|
5
|
-
accessControl?:
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
hideIfUnauthorized?: boolean;
|
|
8
|
-
};
|
|
6
|
+
accessControl?: ButtonAccessControl;
|
|
9
7
|
onSuccess?: () => void;
|
|
10
8
|
undoable?: boolean;
|
|
11
9
|
class?: string;
|
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
import { useNavigation, useCan, t } from '@svadmin/core';
|
|
3
3
|
import { Button } from '../ui/button/index.js';
|
|
4
4
|
import { Pencil } from '@lucide/svelte';
|
|
5
|
+
import type { ButtonAccessControl } from './access-control';
|
|
6
|
+
import { withRecordId } from './access-control';
|
|
5
7
|
|
|
6
8
|
let { resource, recordItemId, hideText = false, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
|
|
7
9
|
resource: string;
|
|
8
10
|
recordItemId: string | number;
|
|
9
11
|
hideText?: boolean;
|
|
10
|
-
accessControl?:
|
|
12
|
+
accessControl?: ButtonAccessControl;
|
|
11
13
|
class?: string;
|
|
12
14
|
}>();
|
|
13
15
|
|
|
14
16
|
const nav = useNavigation();
|
|
15
|
-
const can = useCan(() => ({
|
|
17
|
+
const can = useCan(() => ({
|
|
18
|
+
resource,
|
|
19
|
+
action: 'edit',
|
|
20
|
+
params: withRecordId(accessControl?.params, recordItemId),
|
|
21
|
+
meta: accessControl?.meta,
|
|
22
|
+
queryOptions: { enabled: accessControl?.enabled ?? true }
|
|
23
|
+
}));
|
|
16
24
|
const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
|
|
17
25
|
</script>
|
|
18
26
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import type { ButtonAccessControl } from './access-control';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
resource: string;
|
|
3
4
|
recordItemId: string | number;
|
|
4
5
|
hideText?: boolean;
|
|
5
|
-
accessControl?:
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
hideIfUnauthorized?: boolean;
|
|
8
|
-
};
|
|
6
|
+
accessControl?: ButtonAccessControl;
|
|
9
7
|
class?: string;
|
|
10
8
|
};
|
|
11
9
|
declare const EditButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
import { useExport, useCan, t } from '@svadmin/core';
|
|
3
3
|
import { Button } from '../ui/button/index.js';
|
|
4
4
|
import { Download } from '@lucide/svelte';
|
|
5
|
+
import type { ButtonAccessControl } from './access-control';
|
|
5
6
|
|
|
6
7
|
let { resource, hideText = false, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
|
|
7
8
|
resource: string;
|
|
8
9
|
hideText?: boolean;
|
|
9
|
-
accessControl?:
|
|
10
|
+
accessControl?: ButtonAccessControl;
|
|
10
11
|
class?: string;
|
|
11
12
|
}>();
|
|
12
13
|
|
|
13
14
|
const { triggerExport, isLoading } = useExport({ get resource() { return resource; } });
|
|
14
|
-
const can = useCan(() => ({
|
|
15
|
+
const can = useCan(() => ({
|
|
16
|
+
resource,
|
|
17
|
+
action: 'export',
|
|
18
|
+
params: accessControl?.params,
|
|
19
|
+
meta: accessControl?.meta,
|
|
20
|
+
queryOptions: { enabled: accessControl?.enabled ?? true }
|
|
21
|
+
}));
|
|
15
22
|
const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
|
|
16
23
|
</script>
|
|
17
24
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import type { ButtonAccessControl } from './access-control';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
resource: string;
|
|
3
4
|
hideText?: boolean;
|
|
4
|
-
accessControl?:
|
|
5
|
-
enabled?: boolean;
|
|
6
|
-
hideIfUnauthorized?: boolean;
|
|
7
|
-
};
|
|
5
|
+
accessControl?: ButtonAccessControl;
|
|
8
6
|
class?: string;
|
|
9
7
|
};
|
|
10
8
|
declare const ExportButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
import { useImport, useCan, t } from '@svadmin/core';
|
|
3
3
|
import { Button } from '../ui/button/index.js';
|
|
4
4
|
import { Upload } from '@lucide/svelte';
|
|
5
|
+
import type { ButtonAccessControl } from './access-control';
|
|
5
6
|
|
|
6
7
|
let { resource, hideText = false, onFinish, accessControl = { enabled: true, hideIfUnauthorized: true }, class: className = '' } = $props<{
|
|
7
8
|
resource: string;
|
|
8
9
|
hideText?: boolean;
|
|
9
10
|
onFinish?: (result: { succeeded: unknown[]; errored: { request: unknown; error: unknown }[] }) => void;
|
|
10
|
-
accessControl?:
|
|
11
|
+
accessControl?: ButtonAccessControl;
|
|
11
12
|
class?: string;
|
|
12
13
|
}>();
|
|
13
14
|
|
|
@@ -31,7 +32,13 @@
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
const can = useCan(() => ({
|
|
35
|
+
const can = useCan(() => ({
|
|
36
|
+
resource,
|
|
37
|
+
action: 'import',
|
|
38
|
+
params: accessControl?.params,
|
|
39
|
+
meta: accessControl?.meta,
|
|
40
|
+
queryOptions: { enabled: accessControl?.enabled ?? true }
|
|
41
|
+
}));
|
|
35
42
|
const hidden = $derived(accessControl?.hideIfUnauthorized && !can.allowed);
|
|
36
43
|
</script>
|
|
37
44
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ButtonAccessControl } from './access-control';
|
|
1
2
|
type $$ComponentProps = {
|
|
2
3
|
resource: string;
|
|
3
4
|
hideText?: boolean;
|
|
@@ -8,10 +9,7 @@ type $$ComponentProps = {
|
|
|
8
9
|
error: unknown;
|
|
9
10
|
}[];
|
|
10
11
|
}) => void;
|
|
11
|
-
accessControl?:
|
|
12
|
-
enabled?: boolean;
|
|
13
|
-
hideIfUnauthorized?: boolean;
|
|
14
|
-
};
|
|
12
|
+
accessControl?: ButtonAccessControl;
|
|
15
13
|
class?: string;
|
|
16
14
|
};
|
|
17
15
|
declare const ImportButton: import("svelte").Component<$$ComponentProps, {}, "">;
|