@svadmin/ui 0.34.9 → 0.35.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 +372 -0
- package/dist/component-registry.svelte.d.ts +1 -0
- package/dist/component-registry.svelte.js +1 -0
- package/dist/components/AICommandBar.svelte +3 -3
- package/dist/components/AboutSettings.svelte +2 -2
- package/dist/components/AdminApp.svelte +96 -18
- package/dist/components/AdminApp.svelte.d.ts +32 -2
- package/dist/components/AnomalyBadge.svelte +1 -1
- package/dist/components/ApiSettings.svelte +191 -0
- package/dist/components/ApiSettings.svelte.d.ts +3 -0
- package/dist/components/AppearanceSettings.svelte +5 -6
- package/dist/components/AuditLogDrawer.svelte +1 -1
- package/dist/components/AuditLogViewer.svelte +1 -1
- package/dist/components/AutoForm.svelte +50 -44
- package/dist/components/AutoTable.svelte +44 -39
- package/dist/components/Breadcrumbs.svelte +1 -0
- package/dist/components/ChatDialog.svelte +5 -4
- package/dist/components/ComboboxField.svelte +3 -3
- package/dist/components/CommandPalette.svelte +3 -3
- package/dist/components/CopilotPanel.svelte +2 -2
- package/dist/components/CreatePage.svelte +0 -1
- package/dist/components/DevTools.svelte +4 -3
- package/dist/components/DraggableHeader.svelte +2 -1
- package/dist/components/EditPage.svelte +1 -1
- package/dist/components/EmptyState.svelte +1 -1
- package/dist/components/ErrorPage.svelte +58 -0
- package/dist/components/ErrorPage.svelte.d.ts +8 -0
- package/dist/components/FieldRenderer.svelte +9 -8
- package/dist/components/ForgotPasswordPage.svelte +111 -68
- package/dist/components/Header.svelte +7 -7
- package/dist/components/InferencerPanel.svelte +4 -3
- package/dist/components/InfiniteList.svelte +3 -4
- package/dist/components/IntegrationsSettings.svelte +70 -0
- package/dist/components/IntegrationsSettings.svelte.d.ts +4 -0
- package/dist/components/KeyboardShortcuts.svelte +3 -3
- package/dist/components/Layout.svelte +13 -13
- package/dist/components/LoginPage.svelte +207 -105
- package/dist/components/LoginPage.svelte.d.ts +3 -15
- package/dist/components/MarkdownRenderer.svelte +3 -5
- package/dist/components/ModalForm.svelte +1 -3
- package/dist/components/NotificationsSettings.svelte +115 -0
- package/dist/components/NotificationsSettings.svelte.d.ts +3 -0
- package/dist/components/PageHeader.svelte +9 -7
- package/dist/components/PageSkeleton.svelte +4 -4
- package/dist/components/PasswordInput.svelte +1 -2
- package/dist/components/PermissionMatrix.svelte +5 -5
- package/dist/components/ProfilePage.svelte +1 -1
- package/dist/components/RegisterPage.svelte +103 -65
- package/dist/components/ResourceOperationsPage.svelte +153 -0
- package/dist/components/ResourceOperationsPage.svelte.d.ts +37 -0
- package/dist/components/RolesSettings.svelte +1 -2
- package/dist/components/SecuritySettings.svelte +179 -0
- package/dist/components/SecuritySettings.svelte.d.ts +4 -0
- package/dist/components/SettingsPage.svelte +121 -55
- package/dist/components/SettingsPage.svelte.d.ts +25 -1
- package/dist/components/ShowPage.svelte +13 -15
- package/dist/components/Sidebar.svelte +107 -58
- package/dist/components/SidebarItem.svelte +38 -14
- package/dist/components/StepsForm.svelte +1 -1
- package/dist/components/TaskList.svelte +1 -1
- package/dist/components/TaskQueueDrawer.svelte +2 -1
- package/dist/components/TenantSwitcher.svelte +1 -2
- package/dist/components/Toast.svelte +1 -0
- package/dist/components/TooltipButton.svelte +1 -0
- package/dist/components/TwoFactorAuthPage.svelte +247 -0
- package/dist/components/TwoFactorAuthPage.svelte.d.ts +3 -0
- package/dist/components/UpdatePasswordPage.svelte +86 -48
- package/dist/components/VirtualTable.svelte +1 -1
- package/dist/components/VoiceInput.svelte +1 -0
- package/dist/components/account/CompanyProfilePage.svelte +99 -0
- package/dist/components/account/CompanyProfilePage.svelte.d.ts +3 -0
- package/dist/components/account/GetStartedPage.svelte +123 -0
- package/dist/components/account/GetStartedPage.svelte.d.ts +4 -0
- package/dist/components/account/ImportMembersPage.svelte +143 -0
- package/dist/components/account/ImportMembersPage.svelte.d.ts +3 -0
- package/dist/components/account/MembersStarterPage.svelte +104 -0
- package/dist/components/account/MembersStarterPage.svelte.d.ts +3 -0
- package/dist/components/account/SecurityLogPage.svelte +108 -0
- package/dist/components/account/SecurityLogPage.svelte.d.ts +3 -0
- package/dist/components/account/SettingsEnterprisePage.svelte +131 -0
- package/dist/components/account/SettingsEnterprisePage.svelte.d.ts +3 -0
- package/dist/components/account/SettingsPlainPage.svelte +81 -0
- package/dist/components/account/SettingsPlainPage.svelte.d.ts +3 -0
- package/dist/components/account/SettingsSidebarPage.svelte +98 -0
- package/dist/components/account/SettingsSidebarPage.svelte.d.ts +3 -0
- package/dist/components/account/TeamMembersPage.svelte +98 -0
- package/dist/components/account/TeamMembersPage.svelte.d.ts +3 -0
- package/dist/components/buttons/ImportButton.svelte +1 -1
- package/dist/components/buttons/ListButton.svelte +1 -1
- package/dist/components/charts/BarChart.svelte +1 -1
- package/dist/components/charts/LineChart.svelte +3 -3
- package/dist/components/charts/PieChart.svelte +2 -2
- package/dist/components/fieldComponentMap.js +2 -1
- package/dist/components/fields/ArrayField.svelte +1 -1
- package/dist/components/fields/MarkdownField.svelte +1 -0
- package/dist/components/fields/MultiSelectField.svelte +1 -1
- package/dist/components/fields/TagField.svelte +1 -1
- package/dist/components/network/TeamCrewTablePage.svelte +122 -0
- package/dist/components/network/TeamCrewTablePage.svelte.d.ts +3 -0
- package/dist/components/network/UserCardsNFTPage.svelte +134 -0
- package/dist/components/network/UserCardsNFTPage.svelte.d.ts +3 -0
- package/dist/components/profile/ActivityTimeline.svelte +77 -0
- package/dist/components/profile/ActivityTimeline.svelte.d.ts +16 -0
- package/dist/components/profile/ProfileCard.svelte +216 -0
- package/dist/components/profile/ProfileCard.svelte.d.ts +27 -0
- package/dist/components/profile/ProjectsGrid.svelte +91 -0
- package/dist/components/profile/ProjectsGrid.svelte.d.ts +17 -0
- package/dist/components/profile/PublicProfilePage.svelte +179 -0
- package/dist/components/profile/PublicProfilePage.svelte.d.ts +10 -0
- package/dist/components/profile/TeamsShowcase.svelte +98 -0
- package/dist/components/profile/TeamsShowcase.svelte.d.ts +19 -0
- package/dist/components/ui/alert/index.js +1 -0
- package/dist/components/ui/badge/index.js +1 -0
- package/dist/components/ui/button/button.svelte +1 -0
- package/dist/components/ui/button/index.js +1 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +1 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +1 -1
- package/dist/components/ui/sheet/sheet.svelte +1 -0
- package/dist/components/ui/tabs/tabs.svelte +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +27 -0
- package/dist/router-state.svelte.js +17 -0
- package/package.json +13 -13
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
/* eslint-disable svelte/prefer-svelte-reactivity */
|
|
2
3
|
import { getResources, canAccessAsync } from '@svadmin/core';
|
|
3
4
|
import type { Identity, MenuItem } from '@svadmin/core';
|
|
4
5
|
import { navigate } from '@svadmin/core/router';
|
|
@@ -11,13 +12,14 @@
|
|
|
11
12
|
import SidebarItem from './SidebarItem.svelte';
|
|
12
13
|
|
|
13
14
|
import { ScrollArea } from './ui/scroll-area/index.js';
|
|
14
|
-
import * as Collapsible from './ui/collapsible/index.js';
|
|
15
15
|
import { Avatar } from './ui/avatar/index.js';
|
|
16
16
|
import {
|
|
17
17
|
LayoutDashboard, FileText, Users, Settings, Home,
|
|
18
|
-
ChevronLeft, ChevronRight,
|
|
18
|
+
ChevronLeft, ChevronRight, LogOut, Sun, Moon, Palette,
|
|
19
19
|
Image as ImageIcon, Layout, Folder, Type, Video,
|
|
20
|
-
Download, ListTodo, TrendingUp, Sparkles, Images, Bot, Key, KeyRound, CreditCard, BookOpen, Wrench
|
|
20
|
+
Download, ListTodo, TrendingUp, Sparkles, Images, Bot, Key, KeyRound, CreditCard, BookOpen, Wrench,
|
|
21
|
+
Repeat, ClipboardCheck, SlidersHorizontal, AlertTriangle, Bell, CalendarDays, Package, Shield,
|
|
22
|
+
Barcode, FolderTree, Building2, Warehouse
|
|
21
23
|
} from '@lucide/svelte';
|
|
22
24
|
|
|
23
25
|
let { collapsed, identity, title, onToggle, onLogout, menu, routeMode = 'auto' }: {
|
|
@@ -37,7 +39,7 @@
|
|
|
37
39
|
: routeMode
|
|
38
40
|
);
|
|
39
41
|
|
|
40
|
-
const
|
|
42
|
+
const _resources = getResources();
|
|
41
43
|
|
|
42
44
|
const iconMap: Record<string, typeof LayoutDashboard> = {
|
|
43
45
|
'dashboard': LayoutDashboard,
|
|
@@ -62,6 +64,18 @@
|
|
|
62
64
|
'credit-card': CreditCard,
|
|
63
65
|
'book-open': BookOpen,
|
|
64
66
|
'wrench': Wrench,
|
|
67
|
+
'repeat': Repeat,
|
|
68
|
+
'clipboard-check': ClipboardCheck,
|
|
69
|
+
'sliders-horizontal': SlidersHorizontal,
|
|
70
|
+
'alert-triangle': AlertTriangle,
|
|
71
|
+
'bell': Bell,
|
|
72
|
+
'calendar': CalendarDays,
|
|
73
|
+
'package': Package,
|
|
74
|
+
'shield': Shield,
|
|
75
|
+
'barcode': Barcode,
|
|
76
|
+
'folder-tree': FolderTree,
|
|
77
|
+
'building-2': Building2,
|
|
78
|
+
'warehouse': Warehouse,
|
|
65
79
|
};
|
|
66
80
|
|
|
67
81
|
interface NavItem {
|
|
@@ -69,6 +83,7 @@
|
|
|
69
83
|
label: string;
|
|
70
84
|
Icon: typeof LayoutDashboard;
|
|
71
85
|
group?: string;
|
|
86
|
+
badge?: string;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
interface NavGroup {
|
|
@@ -80,7 +95,7 @@
|
|
|
80
95
|
let navItems = $state.raw<NavItem[]>([]);
|
|
81
96
|
|
|
82
97
|
$effect(() => {
|
|
83
|
-
const
|
|
98
|
+
const _localeVal = getLocale();
|
|
84
99
|
const currentResources = getResources();
|
|
85
100
|
let cancelled = false;
|
|
86
101
|
|
|
@@ -101,6 +116,7 @@
|
|
|
101
116
|
label: r.label,
|
|
102
117
|
Icon: (typeof r.icon === 'string' ? iconMap[r.icon] : r.icon) ?? iconMap[r.name] ?? Settings,
|
|
103
118
|
group: r.group,
|
|
119
|
+
badge: typeof r.meta?.badge === 'string' ? r.meta.badge : undefined,
|
|
104
120
|
});
|
|
105
121
|
}
|
|
106
122
|
}
|
|
@@ -132,7 +148,7 @@
|
|
|
132
148
|
|
|
133
149
|
/** Prefetch resource data on hover for instant navigation */
|
|
134
150
|
const prefetchedResources = new Set<string>();
|
|
135
|
-
function
|
|
151
|
+
function _prefetchResource(resourceName: string) {
|
|
136
152
|
if (resourceName === '/' || prefetchedResources.has(resourceName)) return;
|
|
137
153
|
prefetchedResources.add(resourceName);
|
|
138
154
|
// Trigger a background fetch by navigating the browser cache
|
|
@@ -148,16 +164,14 @@
|
|
|
148
164
|
const key = item.group ?? null;
|
|
149
165
|
if (!groupMap.has(key)) {
|
|
150
166
|
groupMap.set(key, []);
|
|
151
|
-
groups.push({ name: key, items: groupMap.get(key)
|
|
167
|
+
groups.push({ name: key, items: groupMap.get(key) ?? [] });
|
|
152
168
|
}
|
|
153
|
-
groupMap.get(key)
|
|
169
|
+
(groupMap.get(key) ?? []).push(item);
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
return groups;
|
|
157
173
|
});
|
|
158
174
|
|
|
159
|
-
// Track which groups are open
|
|
160
|
-
let openGroups = $state<Set<string>>(new Set());
|
|
161
175
|
let colorPickerOpen = $state(false);
|
|
162
176
|
let colorPickerRef = $state<HTMLDivElement | null>(null);
|
|
163
177
|
let colorPickerOpenedAt = 0;
|
|
@@ -203,77 +217,89 @@
|
|
|
203
217
|
|
|
204
218
|
<aside
|
|
205
219
|
aria-label="Sidebar navigation"
|
|
206
|
-
class="fixed inset-y-0 left-0 z-30 flex flex-col backdrop-blur-xl transition-all duration-300"
|
|
207
|
-
|
|
208
|
-
class:w-
|
|
209
|
-
|
|
220
|
+
class="fixed inset-y-0 left-0 z-30 flex flex-col border-r border-border/70 bg-sidebar/95 shadow-sm shadow-slate-900/[0.03] backdrop-blur-xl transition-all duration-300"
|
|
221
|
+
class:w-72={!collapsed}
|
|
222
|
+
class:w-20={collapsed}
|
|
223
|
+
data-sidebar="sidebar"
|
|
210
224
|
>
|
|
211
225
|
<!-- Logo -->
|
|
212
|
-
<div class="flex h-16 items-center justify-between px-4">
|
|
226
|
+
<div class="flex h-16 items-center justify-between border-b border-border/60 px-4">
|
|
213
227
|
{#if !collapsed}
|
|
214
228
|
<div class="flex items-center gap-3">
|
|
215
|
-
<div class="w-
|
|
216
|
-
|
|
229
|
+
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary text-sm font-bold text-primary-foreground shadow-sm shadow-primary/25">
|
|
230
|
+
{title.slice(0, 1).toUpperCase()}
|
|
231
|
+
</div>
|
|
232
|
+
<div class="min-w-0">
|
|
233
|
+
<span class="block truncate text-[0.95rem] font-semibold tracking-tight text-sidebar-foreground">{title}</span>
|
|
234
|
+
<span class="block truncate text-[11px] font-medium text-sidebar-foreground/50">{getLocale() === 'zh-CN' ? '管理控制台' : 'Admin Console'}</span>
|
|
235
|
+
</div>
|
|
217
236
|
</div>
|
|
218
237
|
{:else}
|
|
219
|
-
<div class="
|
|
238
|
+
<div class="mx-auto flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary text-sm font-bold text-primary-foreground shadow-sm shadow-primary/25">
|
|
239
|
+
{title.slice(0, 1).toUpperCase()}
|
|
240
|
+
</div>
|
|
220
241
|
{/if}
|
|
221
242
|
{#if !collapsed}
|
|
222
|
-
<TooltipButton tooltip={t('common.toggleSidebar')} variant="ghost" size="icon-sm" onclick={onToggle}>
|
|
243
|
+
<TooltipButton tooltip={t('common.toggleSidebar')} variant="ghost" size="icon-sm" onclick={onToggle} class="rounded-xl text-sidebar-foreground/55 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground">
|
|
223
244
|
<ChevronLeft class="h-4 w-4" />
|
|
224
245
|
</TooltipButton>
|
|
225
246
|
{:else}
|
|
226
|
-
<TooltipButton tooltip={t('common.toggleSidebar')} variant="ghost" size="icon-sm" onclick={onToggle} class="mx-auto
|
|
247
|
+
<TooltipButton tooltip={t('common.toggleSidebar')} variant="ghost" size="icon-sm" onclick={onToggle} class="mx-auto mb-2 mt-2 block rounded-xl text-sidebar-foreground/55 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground">
|
|
227
248
|
<ChevronRight class="h-4 w-4" />
|
|
228
249
|
</TooltipButton>
|
|
229
250
|
{/if}
|
|
230
251
|
</div>
|
|
231
252
|
|
|
232
|
-
<
|
|
233
|
-
<
|
|
253
|
+
<div class="flex min-h-0 flex-1">
|
|
254
|
+
<ScrollArea class="min-w-0 flex-1">
|
|
255
|
+
<nav aria-label="Main menu" class="space-y-5 px-3 py-5">
|
|
234
256
|
{#if menu && menu.length > 0}
|
|
235
257
|
<!-- Custom multi-level menu -->
|
|
236
|
-
{#each menu as item}
|
|
258
|
+
{#each menu as item, _i (_i)}
|
|
237
259
|
<SidebarItem {item} currentPath={path} {collapsed} depth={0} />
|
|
238
260
|
{/each}
|
|
239
261
|
{:else}
|
|
240
262
|
<!-- Auto-generated menu from resources (fallback) -->
|
|
241
|
-
{#each navGroups as group}
|
|
263
|
+
{#each navGroups as group, _i (_i)}
|
|
242
264
|
{#if group.name && !collapsed}
|
|
243
|
-
<!-- Grouped section
|
|
244
|
-
<
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}}>
|
|
249
|
-
<Collapsible.Trigger
|
|
250
|
-
class="flex w-full items-center justify-between px-3 py-1.5 text-sm font-semibold tracking-wider text-sidebar-foreground/60 hover:text-sidebar-foreground transition-colors"
|
|
265
|
+
<!-- Grouped section: expanded by default in desktop light sidebar mode -->
|
|
266
|
+
<div>
|
|
267
|
+
<div
|
|
268
|
+
class="flex w-full items-center justify-between px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-sidebar-foreground/45"
|
|
269
|
+
data-sidebar="group-label"
|
|
251
270
|
>
|
|
252
271
|
<span>{group.name}</span>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
<div class="mt-1 space-y-1">
|
|
257
|
-
{#each group.items as item}
|
|
272
|
+
</div>
|
|
273
|
+
<div class="mt-1 space-y-1" data-sidebar="menu-sub">
|
|
274
|
+
{#each group.items as item, _i (_i)}
|
|
258
275
|
{@const active = isActive(item.path)}
|
|
259
276
|
<a
|
|
260
277
|
href={effectiveRouteMode === 'hash' ? `#${item.path}` : item.path}
|
|
261
278
|
onclick={(e) => { e.preventDefault(); navigate(item.path); }}
|
|
262
|
-
class="flex items-center gap-3 rounded-xl px-3 {pyClassGroupItem} text-sm font-medium transition-all duration-
|
|
279
|
+
class="group relative flex items-center gap-3 rounded-xl px-3 {pyClassGroupItem} text-sm font-medium transition-all duration-200
|
|
263
280
|
{active
|
|
264
|
-
? 'bg-
|
|
265
|
-
: 'text-sidebar-foreground/
|
|
281
|
+
? 'bg-primary text-primary-foreground shadow-sm shadow-primary/20'
|
|
282
|
+
: 'text-sidebar-foreground/68 hover:bg-background/85 hover:text-sidebar-foreground'}"
|
|
283
|
+
data-sidebar="menu-sub-button"
|
|
284
|
+
data-active={active ? "true" : "false"}
|
|
266
285
|
>
|
|
267
|
-
|
|
268
|
-
|
|
286
|
+
{#if active && !collapsed}
|
|
287
|
+
<div class="absolute left-0 top-1/2 -translate-y-1/2 h-5 w-1 rounded-r-full bg-primary"></div>
|
|
288
|
+
{/if}
|
|
289
|
+
<span data-sidebar="menu-icon" class="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg {active ? 'bg-primary-foreground/15' : 'bg-muted/60 text-sidebar-foreground/55 group-hover:bg-primary/10 group-hover:text-primary'}">
|
|
290
|
+
<item.Icon class="h-4 w-4 flex-shrink-0" />
|
|
291
|
+
</span>
|
|
292
|
+
<span class="min-w-0 flex-1 truncate">{item.label}</span>
|
|
293
|
+
{#if item.badge}
|
|
294
|
+
<span class="rounded-full {active ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-primary/10 text-primary'} px-2 py-0.5 text-[10px] font-semibold">{item.badge}</span>
|
|
295
|
+
{/if}
|
|
269
296
|
</a>
|
|
270
297
|
{/each}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
</Collapsible.Root>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
274
300
|
{:else}
|
|
275
301
|
<!-- Ungrouped items (flat) -->
|
|
276
|
-
{#each group.items as item}
|
|
302
|
+
{#each group.items as item, _i (_i)}
|
|
277
303
|
{@const active = isActive(item.path)}
|
|
278
304
|
{#if collapsed}
|
|
279
305
|
<Tooltip.Root>
|
|
@@ -283,10 +309,12 @@
|
|
|
283
309
|
{...props}
|
|
284
310
|
href={effectiveRouteMode === 'hash' ? `#${item.path}` : item.path}
|
|
285
311
|
onclick={(e) => { e.preventDefault(); navigate(item.path); }}
|
|
286
|
-
class="flex items-center justify-center gap-3 rounded-xl px-3 {pyClass} text-sm font-medium transition-all duration-
|
|
312
|
+
class="flex items-center justify-center gap-3 rounded-xl px-3 {pyClass} text-sm font-medium transition-all duration-200
|
|
287
313
|
{active
|
|
288
|
-
? 'bg-
|
|
289
|
-
: 'text-sidebar-foreground/60 hover:bg-sidebar-accent/
|
|
314
|
+
? 'bg-primary/10 text-primary shadow-sm shadow-primary/10 ring-1 ring-primary/15'
|
|
315
|
+
: 'text-sidebar-foreground/60 hover:bg-sidebar-accent/65 hover:text-sidebar-foreground'}"
|
|
316
|
+
data-sidebar="menu-button"
|
|
317
|
+
data-active={active ? "true" : "false"}
|
|
290
318
|
>
|
|
291
319
|
<item.Icon class="h-4 w-4 flex-shrink-0" />
|
|
292
320
|
</a>
|
|
@@ -300,13 +328,23 @@
|
|
|
300
328
|
<a
|
|
301
329
|
href={effectiveRouteMode === 'hash' ? `#${item.path}` : item.path}
|
|
302
330
|
onclick={(e) => { e.preventDefault(); navigate(item.path); }}
|
|
303
|
-
class="flex items-center gap-3 rounded-xl px-3 {pyClass} text-sm font-medium transition-all duration-
|
|
331
|
+
class="group relative flex items-center gap-3 rounded-xl px-3 {pyClass} text-sm font-medium transition-all duration-200
|
|
304
332
|
{active
|
|
305
|
-
? 'bg-
|
|
306
|
-
: 'text-sidebar-foreground/
|
|
333
|
+
? 'bg-primary text-primary-foreground shadow-sm shadow-primary/20'
|
|
334
|
+
: 'text-sidebar-foreground/68 hover:bg-background/85 hover:text-sidebar-foreground'}"
|
|
335
|
+
data-sidebar="menu-button"
|
|
336
|
+
data-active={active ? "true" : "false"}
|
|
307
337
|
>
|
|
308
|
-
|
|
309
|
-
|
|
338
|
+
{#if active && !collapsed}
|
|
339
|
+
<div class="absolute left-0 top-1/2 -translate-y-1/2 h-5 w-1 rounded-r-full bg-primary"></div>
|
|
340
|
+
{/if}
|
|
341
|
+
<span data-sidebar="menu-icon" class="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg {active ? 'bg-primary-foreground/15' : 'bg-muted/60 text-sidebar-foreground/55 group-hover:bg-primary/10 group-hover:text-primary'}">
|
|
342
|
+
<item.Icon class="h-4 w-4 flex-shrink-0" />
|
|
343
|
+
</span>
|
|
344
|
+
<span class="min-w-0 flex-1 truncate">{item.label}</span>
|
|
345
|
+
{#if item.badge}
|
|
346
|
+
<span class="rounded-full {active ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-primary/10 text-primary'} px-2 py-0.5 text-[10px] font-semibold">{item.badge}</span>
|
|
347
|
+
{/if}
|
|
310
348
|
</a>
|
|
311
349
|
{/if}
|
|
312
350
|
{/each}
|
|
@@ -315,16 +353,17 @@
|
|
|
315
353
|
{/if}
|
|
316
354
|
</nav>
|
|
317
355
|
</ScrollArea>
|
|
356
|
+
</div>
|
|
318
357
|
|
|
319
358
|
<!-- Footer -->
|
|
320
|
-
<div>
|
|
359
|
+
<div class="border-t border-border/60 bg-sidebar/95">
|
|
321
360
|
<!-- Color theme picker -->
|
|
322
361
|
{#if !collapsed}
|
|
323
362
|
<div class="px-3 pt-3 pb-1">
|
|
324
363
|
<div class="relative" bind:this={colorPickerRef}>
|
|
325
364
|
<Button
|
|
326
365
|
variant="ghost"
|
|
327
|
-
class="flex w-full items-center gap-2 rounded-
|
|
366
|
+
class="flex h-auto w-full items-center justify-start gap-2 rounded-xl px-2 py-1.5 text-sm text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-foreground"
|
|
328
367
|
onclick={() => { if (!colorPickerOpen) colorPickerOpenedAt = Date.now(); colorPickerOpen = !colorPickerOpen; }}
|
|
329
368
|
>
|
|
330
369
|
<Palette class="h-4 w-4" />
|
|
@@ -336,7 +375,7 @@
|
|
|
336
375
|
</Button>
|
|
337
376
|
{#if colorPickerOpen}
|
|
338
377
|
<div class="absolute bottom-full left-0 mb-1 z-50 w-40 rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95">
|
|
339
|
-
{#each getColorThemes() as ct}
|
|
378
|
+
{#each getColorThemes() as ct, _i (_i)}
|
|
340
379
|
<button
|
|
341
380
|
class="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground transition-colors"
|
|
342
381
|
onclick={() => { setColorTheme(ct.id as typeof ct.id & import('@svadmin/core').ColorTheme); colorPickerOpen = false; }}
|
|
@@ -357,7 +396,7 @@
|
|
|
357
396
|
<!-- User info -->
|
|
358
397
|
{#if !collapsed && identity}
|
|
359
398
|
<div class="px-3 pb-3 pt-1">
|
|
360
|
-
<div class="flex items-center gap-3 rounded-
|
|
399
|
+
<div class="flex items-center gap-3 rounded-2xl border border-border/50 bg-background/65 p-2 shadow-sm shadow-slate-900/[0.02] transition-colors hover:bg-sidebar-accent/50">
|
|
361
400
|
<Avatar
|
|
362
401
|
src={(identity as Record<string, unknown>).avatar as string | undefined}
|
|
363
402
|
alt={identity.name ?? 'User'}
|
|
@@ -414,6 +453,16 @@
|
|
|
414
453
|
</Tooltip.Trigger>
|
|
415
454
|
<Tooltip.Content side="right">{t('common.toggleTheme')}</Tooltip.Content>
|
|
416
455
|
</Tooltip.Root>
|
|
456
|
+
<Tooltip.Root>
|
|
457
|
+
<Tooltip.Trigger>
|
|
458
|
+
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
459
|
+
<Button {...props} variant="ghost" size="icon" onclick={() => navigate('/settings')} class="w-full text-sidebar-foreground/60 hover:text-sidebar-foreground">
|
|
460
|
+
<Settings class="h-4 w-4" />
|
|
461
|
+
</Button>
|
|
462
|
+
{/snippet}
|
|
463
|
+
</Tooltip.Trigger>
|
|
464
|
+
<Tooltip.Content side="right">{t('settings.title')}</Tooltip.Content>
|
|
465
|
+
</Tooltip.Root>
|
|
417
466
|
<Tooltip.Root>
|
|
418
467
|
<Tooltip.Trigger>
|
|
419
468
|
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { MenuItem } from '@svadmin/core';
|
|
3
|
-
import { t } from '@svadmin/core/i18n';
|
|
4
3
|
import * as Collapsible from './ui/collapsible/index.js';
|
|
5
4
|
import * as Tooltip from './ui/tooltip/index.js';
|
|
6
5
|
import SidebarItem from './SidebarItem.svelte';
|
|
7
6
|
import {
|
|
8
7
|
LayoutDashboard, FileText, Users, Settings, Home,
|
|
9
|
-
ChevronDown, Folder, ExternalLink,
|
|
8
|
+
ChevronDown, Folder, ExternalLink, Repeat, ClipboardCheck, SlidersHorizontal, AlertTriangle,
|
|
9
|
+
Barcode, FolderTree, Building2, Warehouse, BriefcaseBusiness, Handshake, UserRoundCheck,
|
|
10
|
+
CalendarCheck, KeyRound, MapPinned,
|
|
10
11
|
} from '@lucide/svelte';
|
|
11
12
|
|
|
12
13
|
import { formatLink } from '@svadmin/core/router';
|
|
@@ -25,9 +26,23 @@
|
|
|
25
26
|
settings: Settings,
|
|
26
27
|
home: Home,
|
|
27
28
|
folder: Folder,
|
|
29
|
+
repeat: Repeat,
|
|
30
|
+
'clipboard-check': ClipboardCheck,
|
|
31
|
+
'sliders-horizontal': SlidersHorizontal,
|
|
32
|
+
'alert-triangle': AlertTriangle,
|
|
33
|
+
'barcode': Barcode,
|
|
34
|
+
'folder-tree': FolderTree,
|
|
35
|
+
'building-2': Building2,
|
|
36
|
+
'warehouse': Warehouse,
|
|
37
|
+
'briefcase-business': BriefcaseBusiness,
|
|
38
|
+
handshake: Handshake,
|
|
39
|
+
'user-round-check': UserRoundCheck,
|
|
40
|
+
'calendar-check': CalendarCheck,
|
|
41
|
+
'key-round': KeyRound,
|
|
42
|
+
'map-pinned': MapPinned,
|
|
28
43
|
};
|
|
29
44
|
|
|
30
|
-
function getIcon(name?: string |
|
|
45
|
+
function getIcon(name?: string | typeof LayoutDashboard): typeof LayoutDashboard {
|
|
31
46
|
if (!name) return depth === 0 ? Settings : Folder;
|
|
32
47
|
if (typeof name !== 'string') return name; // if it's already a component
|
|
33
48
|
return iconMap[name] ?? Settings;
|
|
@@ -70,11 +85,13 @@
|
|
|
70
85
|
<!-- Parent node with children: render as collapsible group -->
|
|
71
86
|
<Collapsible.Root bind:open={isOpen}>
|
|
72
87
|
<Collapsible.Trigger
|
|
73
|
-
class="flex w-full items-center justify-between rounded-
|
|
88
|
+
class="flex w-full items-center justify-between rounded-xl px-3 py-2 text-sm font-medium transition-all duration-200
|
|
74
89
|
{childActive
|
|
75
|
-
? 'text-
|
|
76
|
-
: 'text-sidebar-foreground/
|
|
90
|
+
? 'bg-primary/10 text-primary ring-1 ring-primary/15'
|
|
91
|
+
: 'text-sidebar-foreground/68 hover:bg-sidebar-accent/65 hover:text-sidebar-foreground'}"
|
|
77
92
|
style="padding-left: {12 + depth * 12}px"
|
|
93
|
+
data-sidebar={depth === 0 ? "menu-button" : "menu-sub-button"}
|
|
94
|
+
data-active={childActive ? "true" : "false"}
|
|
78
95
|
>
|
|
79
96
|
<span class="flex items-center gap-3">
|
|
80
97
|
<Icon class="h-4 w-4 flex-shrink-0" />
|
|
@@ -83,8 +100,8 @@
|
|
|
83
100
|
<ChevronDown class="h-3.5 w-3.5 transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
|
|
84
101
|
</Collapsible.Trigger>
|
|
85
102
|
<Collapsible.Content>
|
|
86
|
-
<div class="mt-0.5 space-y-0.5" style="padding-left: {depth > 0 ? 0 : 0}px">
|
|
87
|
-
{#each item.children as child}
|
|
103
|
+
<div class="mt-0.5 space-y-0.5" style="padding-left: {depth > 0 ? 0 : 0}px" data-sidebar="menu-sub">
|
|
104
|
+
{#each item.children as child, _i (_i)}
|
|
88
105
|
<SidebarItem item={child} {currentPath} {collapsed} depth={depth + 1} />
|
|
89
106
|
{/each}
|
|
90
107
|
</div>
|
|
@@ -100,10 +117,12 @@
|
|
|
100
117
|
href={finalHref}
|
|
101
118
|
target={isExternal ? '_blank' : undefined}
|
|
102
119
|
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
103
|
-
class="flex items-center justify-center rounded-
|
|
120
|
+
class="flex items-center justify-center rounded-xl px-3 py-2.5 text-sm font-medium transition-all duration-200
|
|
104
121
|
{active
|
|
105
|
-
? 'bg-
|
|
106
|
-
: 'text-sidebar-foreground/
|
|
122
|
+
? 'bg-primary/10 text-primary shadow-sm shadow-primary/10 ring-1 ring-primary/15'
|
|
123
|
+
: 'text-sidebar-foreground/68 hover:bg-sidebar-accent/65 hover:text-sidebar-foreground'}"
|
|
124
|
+
data-sidebar={depth === 0 ? "menu-button" : "menu-sub-button"}
|
|
125
|
+
data-active={active ? "true" : "false"}
|
|
107
126
|
>
|
|
108
127
|
<Icon class="h-4 w-4 flex-shrink-0" />
|
|
109
128
|
</a>
|
|
@@ -119,12 +138,17 @@
|
|
|
119
138
|
href={finalHref}
|
|
120
139
|
target={isExternal ? '_blank' : undefined}
|
|
121
140
|
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
122
|
-
class="flex items-center gap-3 rounded-
|
|
141
|
+
class="relative flex items-center gap-3 rounded-xl px-3 py-2 text-sm font-medium transition-all duration-200
|
|
123
142
|
{active
|
|
124
|
-
? 'bg-
|
|
125
|
-
: 'text-sidebar-foreground/
|
|
143
|
+
? 'bg-primary/10 text-primary font-semibold shadow-sm shadow-primary/10 ring-1 ring-primary/15'
|
|
144
|
+
: 'text-sidebar-foreground/68 hover:bg-sidebar-accent/65 hover:text-sidebar-foreground'}"
|
|
126
145
|
style="padding-left: {12 + depth * 12}px"
|
|
146
|
+
data-sidebar={depth === 0 ? "menu-button" : "menu-sub-button"}
|
|
147
|
+
data-active={active ? "true" : "false"}
|
|
127
148
|
>
|
|
149
|
+
{#if active && !collapsed}
|
|
150
|
+
<div class="absolute left-0 top-1/2 -translate-y-1/2 h-5 w-1 rounded-r-full bg-primary"></div>
|
|
151
|
+
{/if}
|
|
128
152
|
<Icon class="h-4 w-4 flex-shrink-0" />
|
|
129
153
|
<span class="flex-1">{label}</span>
|
|
130
154
|
{#if isExternal}
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
</Table.Row>
|
|
111
111
|
</Table.Header>
|
|
112
112
|
<Table.Body>
|
|
113
|
-
{#each resolvedTasks as task}
|
|
113
|
+
{#each resolvedTasks as task, _i (_i)}
|
|
114
114
|
<Table.Row class="cursor-pointer hover:bg-muted/40" onclick={() => onSelect?.(task)}>
|
|
115
115
|
<Table.Cell class="min-w-[240px]">
|
|
116
116
|
<div class="space-y-1">
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
initialTab?: TaskTab;
|
|
37
37
|
} = $props();
|
|
38
38
|
|
|
39
|
+
// eslint-disable-next-line svelte/prefer-writable-derived
|
|
39
40
|
let activeTab = $state<TaskTab>('tasks');
|
|
40
41
|
let selectedTaskId = $state<string | null>(null);
|
|
41
42
|
let searchQuery = $state('');
|
|
@@ -283,7 +284,7 @@
|
|
|
283
284
|
</div>
|
|
284
285
|
<Select bind:value={statusFilter} aria-label={t('task.statusFilterLabel')}>
|
|
285
286
|
<option value="all">{t('task.statusAll')}</option>
|
|
286
|
-
{#each availableStatuses as status}
|
|
287
|
+
{#each availableStatuses as status, _i (_i)}
|
|
287
288
|
<option value={status}>{status}</option>
|
|
288
289
|
{/each}
|
|
289
290
|
</Select>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { ChevronsUpDown, Building2, Check } from '@lucide/svelte';
|
|
4
|
-
import { Button } from './ui/button/index.js';
|
|
5
4
|
import * as DropdownMenu from './ui/dropdown-menu/index.js';
|
|
6
5
|
import type { Tenant } from '../types.js';
|
|
7
6
|
|
|
@@ -61,7 +60,7 @@
|
|
|
61
60
|
<DropdownMenu.Separator />
|
|
62
61
|
{/if}
|
|
63
62
|
<div class="px-2 py-1.5 text-xs text-muted-foreground">Workspaces</div>
|
|
64
|
-
{#each tenants as tenant}
|
|
63
|
+
{#each tenants as tenant, _i (_i)}
|
|
65
64
|
<DropdownMenu.Item
|
|
66
65
|
class="flex items-center gap-3 cursor-pointer"
|
|
67
66
|
onclick={() => selectTenant(tenant.id)}
|