@svadmin/ui 0.35.0 → 0.36.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 +412 -60
- package/dist/components/AutoForm.svelte +8 -8
- package/dist/components/AutoTable.svelte +2 -2
- package/dist/components/ErrorPage.svelte +1 -1
- package/dist/components/Layout.svelte +1 -1
- package/dist/components/PageHeader.svelte +3 -3
- package/dist/components/ResourceOperationsPage.svelte +12 -14
- package/dist/components/SettingsPage.svelte +41 -75
- package/dist/components/ShowPage.svelte +5 -5
- package/dist/components/Sidebar.svelte +233 -192
- package/dist/components/SidebarItem.svelte +53 -19
- package/dist/components/TwoFactorAuthPage.svelte +1 -1
- package/dist/components/account/CompanyProfilePage.svelte +1 -1
- package/dist/components/account/GetStartedPage.svelte +1 -1
- package/dist/components/account/ImportMembersPage.svelte +1 -1
- package/dist/components/account/MembersStarterPage.svelte +1 -1
- package/dist/components/account/SecurityLogPage.svelte +29 -2
- package/dist/components/account/SettingsEnterprisePage.svelte +1 -1
- package/dist/components/account/SettingsPlainPage.svelte +1 -1
- package/dist/components/account/SettingsSidebarPage.svelte +1 -1
- package/dist/components/account/TeamMembersPage.svelte +1 -1
- package/dist/components/network/TeamCrewTablePage.svelte +35 -2
- package/dist/components/network/UserCardsNFTPage.svelte +1 -1
- package/dist/components/profile/PublicProfilePage.svelte +1 -1
- package/dist/components/ui/pagination/pagination-ellipsis.svelte +1 -1
- package/package.json +2 -2
|
@@ -915,7 +915,7 @@
|
|
|
915
915
|
<div class="flex items-center gap-2">
|
|
916
916
|
<span>{t('common.total', { total: query.data?.total ?? 0 })}</span>
|
|
917
917
|
<select
|
|
918
|
-
class="h-8 w-[70px] px-
|
|
918
|
+
class="h-8 w-[70px] px-2 rounded-md border border-input bg-background text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
919
919
|
value={String(pagination.pageSize ?? 10)}
|
|
920
920
|
onchange={(e) => {
|
|
921
921
|
const size = Number((e.target as HTMLSelectElement).value);
|
|
@@ -930,7 +930,7 @@
|
|
|
930
930
|
<option value="100">100</option>
|
|
931
931
|
</select>
|
|
932
932
|
</div>
|
|
933
|
-
<PaginationUI.Root>
|
|
933
|
+
<PaginationUI.Root class="sm:w-auto sm:justify-end sm:mx-0">
|
|
934
934
|
<PaginationUI.Content>
|
|
935
935
|
<PaginationUI.Item>
|
|
936
936
|
<PaginationUI.Previous
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
);
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
|
-
<div class="flex min-h-[70vh] items-center justify-center p-4">
|
|
29
|
+
<div class="flex min-h-[70vh] items-center justify-center p-4" data-svadmin-content-page="error">
|
|
30
30
|
<Card.Card class="w-full max-w-md border-border/60">
|
|
31
31
|
<Card.CardContent class="space-y-6 px-6 py-10 text-center">
|
|
32
32
|
<div class="mx-auto inline-flex h-16 w-16 items-center justify-center rounded-lg bg-muted/50 text-muted-foreground">
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
}: Props = $props();
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
|
-
<div class="
|
|
22
|
+
<div class="border-b border-border/70 pb-5 {className}" data-svadmin-page-header>
|
|
23
23
|
{#if showBreadcrumbs}
|
|
24
24
|
<div class="mb-4">
|
|
25
25
|
<Breadcrumbs />
|
|
26
26
|
</div>
|
|
27
27
|
{/if}
|
|
28
|
-
<div class="flex flex-col gap-4
|
|
28
|
+
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
|
|
29
29
|
<div class="min-w-0">
|
|
30
30
|
<h1 class="truncate text-2xl font-semibold tracking-tight text-foreground">{title}</h1>
|
|
31
31
|
{#if description}
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
{/if}
|
|
34
34
|
</div>
|
|
35
35
|
{#if actions}
|
|
36
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
36
|
+
<div class="flex w-full flex-wrap items-center gap-2 lg:w-auto lg:justify-end">
|
|
37
37
|
{@render actions()}
|
|
38
38
|
</div>
|
|
39
39
|
{/if}
|
|
@@ -59,13 +59,12 @@
|
|
|
59
59
|
</script>
|
|
60
60
|
|
|
61
61
|
<div class="space-y-6" data-svadmin-resource-operations={resourceName}>
|
|
62
|
-
<section class="grid gap-4 xl:grid-cols-[0.82fr_1.18fr]">
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
<Badge class="mb-4 rounded-full bg-primary/10 text-primary hover:bg-primary/10">{eyebrow}</Badge>
|
|
62
|
+
<section class="grid gap-4 border-b border-border/60 pb-5 xl:grid-cols-[0.82fr_1.18fr]" data-svadmin-ops-hero>
|
|
63
|
+
<div class="min-w-0 py-1">
|
|
64
|
+
<Badge>{eyebrow}</Badge>
|
|
66
65
|
<div class="flex items-start gap-4">
|
|
67
66
|
{#if Icon}
|
|
68
|
-
<span class="flex h-
|
|
67
|
+
<span class="flex h-10 w-10 shrink-0 items-center justify-center text-primary">
|
|
69
68
|
<Icon class="h-6 w-6" />
|
|
70
69
|
</span>
|
|
71
70
|
{/if}
|
|
@@ -74,15 +73,14 @@
|
|
|
74
73
|
<p class="mt-2 max-w-2xl text-sm text-muted-foreground">{description}</p>
|
|
75
74
|
</div>
|
|
76
75
|
</div>
|
|
77
|
-
<Button class="mt-6
|
|
76
|
+
<Button class="mt-6" onclick={() => navigate(`/${resourceName}/create`)}>
|
|
78
77
|
{actionLabel}
|
|
79
78
|
</Button>
|
|
80
|
-
|
|
81
|
-
</Card.Root>
|
|
79
|
+
</div>
|
|
82
80
|
|
|
83
81
|
<div class="grid gap-4 md:grid-cols-3">
|
|
84
82
|
{#each metrics as metric (metric.label)}
|
|
85
|
-
<Card.Root
|
|
83
|
+
<Card.Root data-svadmin-metric-card>
|
|
86
84
|
<Card.Content class="p-5">
|
|
87
85
|
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">{metric.label}</p>
|
|
88
86
|
<p class="mt-2 text-3xl font-semibold">{metric.value}</p>
|
|
@@ -97,7 +95,7 @@
|
|
|
97
95
|
|
|
98
96
|
<div class="grid gap-4 xl:grid-cols-[0.76fr_1.24fr]">
|
|
99
97
|
<div class="space-y-4">
|
|
100
|
-
<Card.Root
|
|
98
|
+
<Card.Root data-svadmin-panel-card>
|
|
101
99
|
<Card.Header>
|
|
102
100
|
<Card.Title>{tableLabel}</Card.Title>
|
|
103
101
|
{#if tableDescription}
|
|
@@ -106,7 +104,7 @@
|
|
|
106
104
|
</Card.Header>
|
|
107
105
|
<Card.Content class="space-y-3">
|
|
108
106
|
{#each lanes as lane (lane.label)}
|
|
109
|
-
<div class="
|
|
107
|
+
<div class="py-3">
|
|
110
108
|
<div class="flex items-center justify-between gap-3">
|
|
111
109
|
<p class="font-medium capitalize">{lane.label}</p>
|
|
112
110
|
<Badge variant="outline">{lane.value}</Badge>
|
|
@@ -116,19 +114,19 @@
|
|
|
116
114
|
{/if}
|
|
117
115
|
</div>
|
|
118
116
|
{:else}
|
|
119
|
-
<p class="
|
|
117
|
+
<p class="py-3 text-sm text-muted-foreground">{emptyLanesText}</p>
|
|
120
118
|
{/each}
|
|
121
119
|
</Card.Content>
|
|
122
120
|
</Card.Root>
|
|
123
121
|
|
|
124
122
|
{#if highlights.length > 0}
|
|
125
|
-
<Card.Root
|
|
123
|
+
<Card.Root data-svadmin-panel-card>
|
|
126
124
|
<Card.Header>
|
|
127
125
|
<Card.Title>{highlightsLabel}</Card.Title>
|
|
128
126
|
</Card.Header>
|
|
129
127
|
<Card.Content class="space-y-3">
|
|
130
128
|
{#each highlights as item (item.title)}
|
|
131
|
-
<div class="
|
|
129
|
+
<div class="py-3">
|
|
132
130
|
<div class="flex items-center justify-between gap-3">
|
|
133
131
|
<p class="font-medium">{item.title}</p>
|
|
134
132
|
{#if item.badge}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { Component, Snippet } from "svelte";
|
|
3
3
|
import { t } from "@svadmin/core/i18n";
|
|
4
4
|
import { navigate } from "@svadmin/core/router";
|
|
5
|
-
import { User,
|
|
5
|
+
import { User, Info, FileSearch, Lock, Puzzle } from "@lucide/svelte";
|
|
6
6
|
import ProfilePage from "./ProfilePage.svelte";
|
|
7
7
|
import AppearanceSettings from "./AppearanceSettings.svelte";
|
|
8
8
|
import AboutSettings from "./AboutSettings.svelte";
|
|
@@ -60,25 +60,19 @@
|
|
|
60
60
|
{
|
|
61
61
|
group: "settings.general",
|
|
62
62
|
items: [
|
|
63
|
-
{ key: "
|
|
64
|
-
{ key: "appearance", path: "/settings/appearance", icon: Palette, label: "settings.appearance" },
|
|
65
|
-
{ key: "notifications", path: "/settings/notifications", icon: Bell, label: "settings.notifications" },
|
|
63
|
+
{ key: "account", path: "/settings/account", icon: User, label: "settings.accountPreferences" },
|
|
66
64
|
],
|
|
67
65
|
},
|
|
68
66
|
{
|
|
69
67
|
group: "settings.workspace",
|
|
70
68
|
items: [
|
|
71
|
-
{ key: "
|
|
72
|
-
|
|
73
|
-
{ key: "roles", path: "/settings/roles", icon: Shield, label: "settings.rolesAndPermissions" },
|
|
74
|
-
] : []),
|
|
75
|
-
{ key: "integrations", path: "/settings/integrations", icon: Puzzle, label: "settings.integrations" },
|
|
69
|
+
{ key: "access", path: "/settings/access", icon: Lock, label: "settings.accessSecurity" },
|
|
70
|
+
{ key: "developer", path: "/settings/developer", icon: Puzzle, label: "settings.integrationApi" },
|
|
76
71
|
],
|
|
77
72
|
},
|
|
78
73
|
{
|
|
79
74
|
group: "settings.developer",
|
|
80
75
|
items: [
|
|
81
|
-
{ key: "api", path: "/settings/api", icon: Key, label: "settings.api" },
|
|
82
76
|
...(authProvider ? [
|
|
83
77
|
{ key: "audit", path: "/settings/audit", icon: FileSearch, label: "settings.auditLogs" },
|
|
84
78
|
] : []),
|
|
@@ -92,87 +86,59 @@
|
|
|
92
86
|
|
|
93
87
|
import { getParams, getRoute } from "../router-state.svelte.js";
|
|
94
88
|
|
|
89
|
+
function normalizeKey(tab: string | undefined): string {
|
|
90
|
+
if (!tab) return "account";
|
|
91
|
+
if (["profile", "appearance", "notifications"].includes(tab)) return "account";
|
|
92
|
+
if (["security", "roles"].includes(tab)) return "access";
|
|
93
|
+
if (["integrations", "api"].includes(tab)) return "developer";
|
|
94
|
+
return tab;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function pathForKey(key: string): string {
|
|
98
|
+
return `/settings/${key}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
let activeKey = $derived.by(() => {
|
|
96
102
|
const tab = getParams().tab;
|
|
97
|
-
|
|
98
|
-
if (sectionKeys.has(
|
|
99
|
-
return content ?
|
|
103
|
+
const normalized = normalizeKey(tab);
|
|
104
|
+
if (sectionKeys.has(normalized)) return normalized;
|
|
105
|
+
return content ? normalized : "account";
|
|
100
106
|
});
|
|
101
107
|
|
|
102
|
-
// Default redirect to profile
|
|
103
108
|
$effect(() => {
|
|
104
109
|
if (getRoute() === "/settings") {
|
|
105
|
-
navigate("/settings/
|
|
110
|
+
navigate("/settings/account");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const tab = getParams().tab;
|
|
115
|
+
const normalized = normalizeKey(tab);
|
|
116
|
+
if (tab && tab !== normalized && sectionKeys.has(normalized)) {
|
|
117
|
+
navigate(pathForKey(normalized));
|
|
106
118
|
}
|
|
107
119
|
});
|
|
108
120
|
</script>
|
|
109
121
|
|
|
110
|
-
<div class="
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{#each resolvedSections as section, _i (_i)}
|
|
116
|
-
{#each section.items as item, _j (_j)}
|
|
117
|
-
{@const active = activeKey === item.key}
|
|
118
|
-
<button
|
|
119
|
-
class="flex items-center gap-2 whitespace-nowrap rounded-xl px-3 py-2 text-sm font-medium transition-colors
|
|
120
|
-
{active ? 'bg-primary/10 text-primary ring-1 ring-primary/15' : 'text-muted-foreground hover:bg-background/70 hover:text-foreground'}"
|
|
121
|
-
onclick={() => navigate(item.path)}
|
|
122
|
-
>
|
|
123
|
-
<item.icon class="h-4 w-4" />
|
|
124
|
-
{t(item.label)}
|
|
125
|
-
</button>
|
|
126
|
-
{/each}
|
|
127
|
-
{/each}
|
|
128
|
-
</div>
|
|
129
|
-
|
|
130
|
-
<!-- Desktop: vertical nav with groups -->
|
|
131
|
-
<div class="hidden space-y-6 px-4 py-6 lg:block">
|
|
132
|
-
<div class="rounded-2xl border border-border/60 bg-background/70 p-4">
|
|
133
|
-
<h2 class="text-lg font-semibold tracking-tight text-foreground">{t("settings.title")}</h2>
|
|
134
|
-
<p class="text-xs text-muted-foreground mt-1">{t("settings.settingsDescription")}</p>
|
|
135
|
-
</div>
|
|
136
|
-
{#each resolvedSections as section, _i (_i)}
|
|
137
|
-
<div>
|
|
138
|
-
<h3 class="mb-2 px-3 text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground/60">
|
|
139
|
-
{t(section.group)}
|
|
140
|
-
</h3>
|
|
141
|
-
<div class="space-y-1">
|
|
142
|
-
{#each section.items as item, _j (_j)}
|
|
143
|
-
{@const active = activeKey === item.key}
|
|
144
|
-
<button
|
|
145
|
-
class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm transition-all
|
|
146
|
-
{active
|
|
147
|
-
? 'bg-primary/10 text-primary font-medium ring-1 ring-primary/15 shadow-sm shadow-primary/10'
|
|
148
|
-
: 'text-muted-foreground hover:bg-background/75 hover:text-foreground'}"
|
|
149
|
-
onclick={() => navigate(item.path)}
|
|
150
|
-
>
|
|
151
|
-
<item.icon class="h-4 w-4 shrink-0" />
|
|
152
|
-
{t(item.label)}
|
|
153
|
-
</button>
|
|
154
|
-
{/each}
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
{/each}
|
|
122
|
+
<div class="min-h-full max-w-6xl">
|
|
123
|
+
<div class="mb-6 border-b border-border/70 pb-4">
|
|
124
|
+
<div class="min-w-0">
|
|
125
|
+
<h2 class="text-2xl font-semibold tracking-tight text-foreground">{t("settings.title")}</h2>
|
|
126
|
+
<p class="mt-1 text-sm text-muted-foreground">{t("settings.settingsDescription")}</p>
|
|
158
127
|
</div>
|
|
159
|
-
</
|
|
128
|
+
</div>
|
|
160
129
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
{#if activeKey === "profile"}
|
|
130
|
+
<div class="{activeKey === 'access' || activeKey === 'audit' || activeKey === 'developer' ? 'max-w-6xl' : 'max-w-3xl'} space-y-6">
|
|
131
|
+
{#if activeKey === "account"}
|
|
164
132
|
{#if profile}{@render profile()}{:else}<ProfilePage />{/if}
|
|
165
|
-
{:else if activeKey === "appearance"}
|
|
166
133
|
{#if appearance}{@render appearance()}{:else}<AppearanceSettings />{/if}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
{:else if activeKey === "security"}
|
|
134
|
+
{#if notifications}{@render notifications()}{:else}<NotificationsSettings />{/if}
|
|
135
|
+
{:else if activeKey === "access"}
|
|
170
136
|
{#if security}{@render security()}{:else}<SecuritySettings />{/if}
|
|
171
|
-
|
|
137
|
+
{#if authProvider}
|
|
138
|
+
{#if roles}{@render roles()}{:else}<RolesSettings />{/if}
|
|
139
|
+
{/if}
|
|
140
|
+
{:else if activeKey === "developer"}
|
|
172
141
|
{#if integrations}{@render integrations()}{:else}<IntegrationsSettings />{/if}
|
|
173
|
-
{:else if activeKey === "notifications"}
|
|
174
|
-
{#if notifications}{@render notifications()}{:else}<NotificationsSettings />{/if}
|
|
175
|
-
{:else if activeKey === "api"}
|
|
176
142
|
{#if api}{@render api()}{:else}<ApiSettings />{/if}
|
|
177
143
|
{:else if activeKey === "audit"}
|
|
178
144
|
{#if audit}{@render audit()}{:else}<AuditLogViewer />{/if}
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
</PageHeader>
|
|
39
39
|
|
|
40
40
|
{#if query.isLoading}
|
|
41
|
-
<Card.Root class="overflow-hidden
|
|
41
|
+
<Card.Root class="overflow-hidden border-border/60 bg-card shadow-none" data-svadmin-show-card>
|
|
42
42
|
<Card.Content class="space-y-3 p-4 sm:p-5">
|
|
43
43
|
{#each showFields.slice(0, 6) as _, i (i)}
|
|
44
|
-
<div class="flex flex-col
|
|
44
|
+
<div class="flex flex-col border-b border-border/50 px-1 py-4 sm:flex-row sm:py-4">
|
|
45
45
|
<Skeleton class="h-4 w-1/2 sm:w-1/4" />
|
|
46
46
|
<Skeleton class="h-4 w-3/4 sm:w-2/5 mt-1 sm:mt-0 sm:ml-auto" />
|
|
47
47
|
</div>
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
</Card.Content>
|
|
50
50
|
</Card.Root>
|
|
51
51
|
{:else if query.data?.data}
|
|
52
|
-
<Card.Root class="overflow-hidden
|
|
52
|
+
<Card.Root class="overflow-hidden border-border/60 bg-card shadow-none" data-svadmin-show-card>
|
|
53
53
|
<Card.Content class="space-y-3 p-4 sm:p-5">
|
|
54
54
|
{#each showFields as field, i (i)}
|
|
55
55
|
{@const value = ((query.data as { data: Record<string, unknown> }).data as Record<string, unknown>)[field.key]}
|
|
56
56
|
{@const DisplayComponent = getDisplayComponent(field.type)}
|
|
57
|
-
<div class="flex flex-col
|
|
57
|
+
<div class="flex flex-col border-b border-border/50 px-1 py-4 transition-colors hover:bg-muted/20 sm:flex-row sm:items-start sm:py-4 {i % 2 === 1 ? 'bg-muted/10' : ''}">
|
|
58
58
|
<div class="mb-1 text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground sm:mb-0 sm:w-1/3">{field.label}</div>
|
|
59
59
|
<div class="text-sm text-foreground sm:w-2/3">
|
|
60
60
|
{#if DisplayComponent && value != null}
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
</Card.Content>
|
|
73
73
|
</Card.Root>
|
|
74
74
|
{:else}
|
|
75
|
-
<Card.Root class="overflow-hidden
|
|
75
|
+
<Card.Root class="overflow-hidden border-border/60 bg-card shadow-none" data-svadmin-show-card>
|
|
76
76
|
<Card.Content class="p-8 text-center">
|
|
77
77
|
<p class="text-muted-foreground">{t('common.noData')}</p>
|
|
78
78
|
{#if query.isError}
|