@svadmin/ui 0.37.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 +42 -88
- package/dist/components/AdminApp.svelte +18 -14
- 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 +4 -4
- package/dist/components/Layout.svelte +17 -7
- package/dist/components/ListInferencer.svelte +14 -0
- package/dist/components/ListInferencer.svelte.d.ts +7 -0
- 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 +3 -2
- package/dist/components/ShowInferencer.svelte +21 -0
- package/dist/components/ShowInferencer.svelte.d.ts +9 -0
- package/dist/components/ShowPage.svelte +2 -2
- package/dist/components/ShowPage.svelte.d.ts +1 -0
- package/dist/components/Sidebar.svelte +101 -104
- package/dist/components/Sidebar.svelte.d.ts +0 -1
- package/dist/components/SidebarItem.svelte +114 -52
- 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
|
@@ -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
|
-
Mail, Send, ShoppingBag, Briefcase, Building2, Calendar, Bell, Clock, Trash2, Palette, Layout,
|
|
10
|
+
Mail, Map as MapIcon, MessageSquare, Send, ShoppingBag, Star, Briefcase, Building2, Calendar, Bell, Clock, Tag, Trash2, Palette, Layout,
|
|
10
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';
|
|
@@ -32,13 +34,17 @@
|
|
|
32
34
|
'sliders-horizontal': SlidersHorizontal,
|
|
33
35
|
'alert-triangle': AlertTriangle,
|
|
34
36
|
mail: Mail,
|
|
37
|
+
map: MapIcon,
|
|
38
|
+
'message-square': MessageSquare,
|
|
35
39
|
send: Send,
|
|
36
40
|
'shopping-bag': ShoppingBag,
|
|
41
|
+
star: Star,
|
|
37
42
|
briefcase: Briefcase,
|
|
38
43
|
building: Building2,
|
|
39
44
|
calendar: Calendar,
|
|
40
45
|
bell: Bell,
|
|
41
46
|
clock: Clock,
|
|
47
|
+
tag: Tag,
|
|
42
48
|
trash: Trash2,
|
|
43
49
|
palette: Palette,
|
|
44
50
|
layout: Layout,
|
|
@@ -57,7 +63,7 @@
|
|
|
57
63
|
};
|
|
58
64
|
|
|
59
65
|
function getIcon(name?: unknown): typeof LayoutDashboard {
|
|
60
|
-
if (!name) return depth === 0 ? Settings :
|
|
66
|
+
if (!name) return depth === 0 ? Settings : Circle;
|
|
61
67
|
if (typeof name !== 'string') return name as typeof LayoutDashboard;
|
|
62
68
|
return iconMap[name] ?? Settings;
|
|
63
69
|
}
|
|
@@ -68,11 +74,12 @@
|
|
|
68
74
|
|
|
69
75
|
function isActive(itemHref: string | undefined): boolean {
|
|
70
76
|
if (!itemHref) return false;
|
|
71
|
-
|
|
72
|
-
|
|
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] === '/');
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
/** Check if any child (recursively) is active — used to auto-open parent groups */
|
|
76
83
|
function hasActiveChild(menuItem: MenuItem): boolean {
|
|
77
84
|
if (isActive(menuItem.href)) return true;
|
|
78
85
|
return menuItem.children?.some((c: MenuItem) => hasActiveChild(c)) ?? false;
|
|
@@ -85,42 +92,73 @@
|
|
|
85
92
|
|
|
86
93
|
const finalHref = $derived(isExternal ? item.href : (item.href ? formatLink(item.href) : undefined));
|
|
87
94
|
|
|
88
|
-
// Track open state — auto-open if a child is active
|
|
89
95
|
let isOpen = $state(false);
|
|
90
96
|
$effect(() => { if (childActive) isOpen = true; });
|
|
91
97
|
|
|
92
98
|
const Icon = $derived(getIcon(item.icon));
|
|
93
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);
|
|
94
104
|
</script>
|
|
95
105
|
|
|
96
106
|
{#if item.meta?.hidden}
|
|
97
|
-
<!-- hidden
|
|
107
|
+
<!-- hidden -->
|
|
98
108
|
{:else if hasChildren && !collapsed}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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"
|
|
145
|
+
>
|
|
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}
|
|
122
161
|
{:else if collapsed}
|
|
123
|
-
<!-- Collapsed sidebar: icon-only with tooltip -->
|
|
124
162
|
<Tooltip.Root>
|
|
125
163
|
<Tooltip.Trigger>
|
|
126
164
|
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
@@ -129,12 +167,12 @@
|
|
|
129
167
|
href={finalHref}
|
|
130
168
|
target={isExternal ? '_blank' : undefined}
|
|
131
169
|
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
132
|
-
class="flex items-center justify-center rounded-
|
|
170
|
+
class="sidebar-menu-item flex items-center justify-center rounded-md px-2 py-[7px] transition-colors duration-150
|
|
133
171
|
{active
|
|
134
|
-
? '
|
|
135
|
-
: 'text-sidebar-foreground/
|
|
172
|
+
? 'sidebar-menu-item-active bg-sidebar-accent text-primary'
|
|
173
|
+
: 'text-sidebar-foreground/50 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground'}"
|
|
136
174
|
>
|
|
137
|
-
<Icon class="h-
|
|
175
|
+
<Icon class="h-[18px] w-[18px] flex-shrink-0" />
|
|
138
176
|
</a>
|
|
139
177
|
{/snippet}
|
|
140
178
|
</Tooltip.Trigger>
|
|
@@ -143,21 +181,45 @@
|
|
|
143
181
|
</Tooltip.Content>
|
|
144
182
|
</Tooltip.Root>
|
|
145
183
|
{:else}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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}
|
|
163
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, {}, "">;
|
|
@@ -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 { Eye } 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: 'show',
|
|
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 ShowButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ButtonAccessControl {
|
|
2
|
+
enabled?: boolean;
|
|
3
|
+
hideIfUnauthorized?: boolean;
|
|
4
|
+
params?: Record<string, unknown>;
|
|
5
|
+
meta?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export declare function withRecordId(params: Record<string, unknown> | undefined, recordItemId: string | number | undefined): Record<string, unknown> | undefined;
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
bind:this={ref}
|
|
55
55
|
data-slot="sheet"
|
|
56
56
|
class={cn(
|
|
57
|
-
"fixed z-50 flex flex-col gap-4 bg-background p-6 shadow-lg
|
|
57
|
+
"fixed z-50 flex translate-x-0 flex-col gap-4 bg-background p-6 shadow-lg",
|
|
58
58
|
side === "right"
|
|
59
|
-
? "inset-y-0 right-0 h-full w-3/4 max-w-sm border-l
|
|
60
|
-
: "inset-y-0 left-0 h-full w-3/4 max-w-sm border-r
|
|
59
|
+
? "inset-y-0 right-0 h-full w-3/4 max-w-sm border-l"
|
|
60
|
+
: "inset-y-0 left-0 h-full w-3/4 max-w-sm border-r",
|
|
61
61
|
className
|
|
62
62
|
)}
|
|
63
63
|
{...restProps}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ export { default as UpdatePasswordPage } from './components/UpdatePasswordPage.s
|
|
|
28
28
|
export { default as ProfilePage } from './components/ProfilePage.svelte';
|
|
29
29
|
export { default as ConfigErrorScreen } from './components/ConfigErrorScreen.svelte';
|
|
30
30
|
export { default as InferencerPanel } from './components/InferencerPanel.svelte';
|
|
31
|
+
export { default as ListInferencer } from './components/ListInferencer.svelte';
|
|
32
|
+
export { default as CreateInferencer } from './components/CreateInferencer.svelte';
|
|
33
|
+
export { default as EditInferencer } from './components/EditInferencer.svelte';
|
|
34
|
+
export { default as ShowInferencer } from './components/ShowInferencer.svelte';
|
|
35
|
+
export { default as ResourceInferencer } from './components/ResourceInferencer.svelte';
|
|
31
36
|
export { default as LiveIndicator } from './components/LiveIndicator.svelte';
|
|
32
37
|
export { default as ListPage } from './components/ListPage.svelte';
|
|
33
38
|
export { default as CreatePage } from './components/CreatePage.svelte';
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,11 @@ export { default as UpdatePasswordPage } from './components/UpdatePasswordPage.s
|
|
|
31
31
|
export { default as ProfilePage } from './components/ProfilePage.svelte';
|
|
32
32
|
export { default as ConfigErrorScreen } from './components/ConfigErrorScreen.svelte';
|
|
33
33
|
export { default as InferencerPanel } from './components/InferencerPanel.svelte';
|
|
34
|
+
export { default as ListInferencer } from './components/ListInferencer.svelte';
|
|
35
|
+
export { default as CreateInferencer } from './components/CreateInferencer.svelte';
|
|
36
|
+
export { default as EditInferencer } from './components/EditInferencer.svelte';
|
|
37
|
+
export { default as ShowInferencer } from './components/ShowInferencer.svelte';
|
|
38
|
+
export { default as ResourceInferencer } from './components/ResourceInferencer.svelte';
|
|
34
39
|
export { default as LiveIndicator } from './components/LiveIndicator.svelte';
|
|
35
40
|
export { default as ListPage } from './components/ListPage.svelte';
|
|
36
41
|
export { default as CreatePage } from './components/CreatePage.svelte';
|