@svadmin/ui 0.34.10 → 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.
Files changed (71) hide show
  1. package/dist/app.css +746 -22
  2. package/dist/component-registry.svelte.d.ts +1 -0
  3. package/dist/components/AdminApp.svelte +94 -15
  4. package/dist/components/AdminApp.svelte.d.ts +32 -2
  5. package/dist/components/ApiSettings.svelte +191 -0
  6. package/dist/components/ApiSettings.svelte.d.ts +3 -0
  7. package/dist/components/AutoForm.svelte +47 -42
  8. package/dist/components/AutoTable.svelte +29 -24
  9. package/dist/components/ErrorPage.svelte +58 -0
  10. package/dist/components/ErrorPage.svelte.d.ts +8 -0
  11. package/dist/components/FieldRenderer.svelte +2 -2
  12. package/dist/components/ForgotPasswordPage.svelte +111 -68
  13. package/dist/components/Header.svelte +6 -7
  14. package/dist/components/IntegrationsSettings.svelte +70 -0
  15. package/dist/components/IntegrationsSettings.svelte.d.ts +4 -0
  16. package/dist/components/Layout.svelte +7 -7
  17. package/dist/components/LoginPage.svelte +207 -105
  18. package/dist/components/LoginPage.svelte.d.ts +3 -15
  19. package/dist/components/NotificationsSettings.svelte +115 -0
  20. package/dist/components/NotificationsSettings.svelte.d.ts +3 -0
  21. package/dist/components/PageHeader.svelte +9 -7
  22. package/dist/components/RegisterPage.svelte +103 -65
  23. package/dist/components/ResourceOperationsPage.svelte +151 -0
  24. package/dist/components/ResourceOperationsPage.svelte.d.ts +37 -0
  25. package/dist/components/SecuritySettings.svelte +179 -0
  26. package/dist/components/SecuritySettings.svelte.d.ts +4 -0
  27. package/dist/components/SettingsPage.svelte +120 -87
  28. package/dist/components/SettingsPage.svelte.d.ts +25 -1
  29. package/dist/components/ShowPage.svelte +9 -9
  30. package/dist/components/Sidebar.svelte +278 -189
  31. package/dist/components/SidebarItem.svelte +77 -18
  32. package/dist/components/TwoFactorAuthPage.svelte +247 -0
  33. package/dist/components/TwoFactorAuthPage.svelte.d.ts +3 -0
  34. package/dist/components/UpdatePasswordPage.svelte +86 -48
  35. package/dist/components/account/CompanyProfilePage.svelte +99 -0
  36. package/dist/components/account/CompanyProfilePage.svelte.d.ts +3 -0
  37. package/dist/components/account/GetStartedPage.svelte +123 -0
  38. package/dist/components/account/GetStartedPage.svelte.d.ts +4 -0
  39. package/dist/components/account/ImportMembersPage.svelte +143 -0
  40. package/dist/components/account/ImportMembersPage.svelte.d.ts +3 -0
  41. package/dist/components/account/MembersStarterPage.svelte +104 -0
  42. package/dist/components/account/MembersStarterPage.svelte.d.ts +3 -0
  43. package/dist/components/account/SecurityLogPage.svelte +135 -0
  44. package/dist/components/account/SecurityLogPage.svelte.d.ts +3 -0
  45. package/dist/components/account/SettingsEnterprisePage.svelte +131 -0
  46. package/dist/components/account/SettingsEnterprisePage.svelte.d.ts +3 -0
  47. package/dist/components/account/SettingsPlainPage.svelte +81 -0
  48. package/dist/components/account/SettingsPlainPage.svelte.d.ts +3 -0
  49. package/dist/components/account/SettingsSidebarPage.svelte +98 -0
  50. package/dist/components/account/SettingsSidebarPage.svelte.d.ts +3 -0
  51. package/dist/components/account/TeamMembersPage.svelte +98 -0
  52. package/dist/components/account/TeamMembersPage.svelte.d.ts +3 -0
  53. package/dist/components/network/TeamCrewTablePage.svelte +155 -0
  54. package/dist/components/network/TeamCrewTablePage.svelte.d.ts +3 -0
  55. package/dist/components/network/UserCardsNFTPage.svelte +134 -0
  56. package/dist/components/network/UserCardsNFTPage.svelte.d.ts +3 -0
  57. package/dist/components/profile/ActivityTimeline.svelte +77 -0
  58. package/dist/components/profile/ActivityTimeline.svelte.d.ts +16 -0
  59. package/dist/components/profile/ProfileCard.svelte +216 -0
  60. package/dist/components/profile/ProfileCard.svelte.d.ts +27 -0
  61. package/dist/components/profile/ProjectsGrid.svelte +91 -0
  62. package/dist/components/profile/ProjectsGrid.svelte.d.ts +17 -0
  63. package/dist/components/profile/PublicProfilePage.svelte +179 -0
  64. package/dist/components/profile/PublicProfilePage.svelte.d.ts +10 -0
  65. package/dist/components/profile/TeamsShowcase.svelte +98 -0
  66. package/dist/components/profile/TeamsShowcase.svelte.d.ts +19 -0
  67. package/dist/components/ui/pagination/pagination-ellipsis.svelte +1 -1
  68. package/dist/index.d.ts +23 -0
  69. package/dist/index.js +27 -0
  70. package/dist/router-state.svelte.js +17 -0
  71. package/package.json +2 -2
@@ -1,118 +1,151 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
3
- import { navigate } from '@svadmin/core/router';
4
- import { User, Palette, Info, Shield, FileSearch } from '@lucide/svelte';
5
- import ProfilePage from './ProfilePage.svelte';
6
- import AppearanceSettings from './AppearanceSettings.svelte';
7
- import AboutSettings from './AboutSettings.svelte';
8
- import RolesSettings from './RolesSettings.svelte';
9
- import AuditLogViewer from './AuditLogViewer.svelte';
10
- import { getAuthProvider } from '@svadmin/core';
2
+ import type { Component, Snippet } from "svelte";
3
+ import { t } from "@svadmin/core/i18n";
4
+ import { navigate } from "@svadmin/core/router";
5
+ import { User, Info, FileSearch, Lock, Puzzle } from "@lucide/svelte";
6
+ import ProfilePage from "./ProfilePage.svelte";
7
+ import AppearanceSettings from "./AppearanceSettings.svelte";
8
+ import AboutSettings from "./AboutSettings.svelte";
9
+ import RolesSettings from "./RolesSettings.svelte";
10
+ import AuditLogViewer from "./AuditLogViewer.svelte";
11
+ import SecuritySettings from "./SecuritySettings.svelte";
12
+ import IntegrationsSettings from "./IntegrationsSettings.svelte";
13
+ import NotificationsSettings from "./NotificationsSettings.svelte";
14
+ import ApiSettings from "./ApiSettings.svelte";
15
+ import { getAuthProvider } from "@svadmin/core";
11
16
 
12
17
  const authProvider = getAuthProvider({ optional: true });
13
18
 
14
- const sections = [
19
+ interface SettingsItem {
20
+ key: string;
21
+ path: string;
22
+ icon: Component;
23
+ label: string;
24
+ }
25
+
26
+ interface SettingsSection {
27
+ group: string;
28
+ items: SettingsItem[];
29
+ }
30
+
31
+ interface Props {
32
+ customSections?: SettingsSection[];
33
+ profile?: Snippet;
34
+ appearance?: Snippet;
35
+ roles?: Snippet;
36
+ security?: Snippet;
37
+ integrations?: Snippet;
38
+ notifications?: Snippet;
39
+ api?: Snippet;
40
+ audit?: Snippet;
41
+ about?: Snippet;
42
+ content?: Snippet<[string]>;
43
+ }
44
+
45
+ let {
46
+ customSections,
47
+ profile,
48
+ appearance,
49
+ roles,
50
+ security,
51
+ integrations,
52
+ notifications,
53
+ api,
54
+ audit,
55
+ about,
56
+ content,
57
+ }: Props = $props();
58
+
59
+ const defaultSections = [
15
60
  {
16
- group: 'settings.general',
61
+ group: "settings.general",
17
62
  items: [
18
- { key: 'profile', path: '/settings/profile', icon: User, label: 'settings.profile' },
19
- { key: 'appearance', path: '/settings/appearance', icon: Palette, label: 'settings.appearance' },
63
+ { key: "account", path: "/settings/account", icon: User, label: "settings.accountPreferences" },
20
64
  ],
21
65
  },
22
66
  {
23
- group: 'settings.system',
67
+ group: "settings.workspace",
68
+ items: [
69
+ { key: "access", path: "/settings/access", icon: Lock, label: "settings.accessSecurity" },
70
+ { key: "developer", path: "/settings/developer", icon: Puzzle, label: "settings.integrationApi" },
71
+ ],
72
+ },
73
+ {
74
+ group: "settings.developer",
24
75
  items: [
25
76
  ...(authProvider ? [
26
- { key: 'roles', path: '/settings/roles', icon: Shield, label: 'settings.rolesAndPermissions' },
27
- { key: 'audit', path: '/settings/audit', icon: FileSearch, label: 'settings.auditLogs' },
77
+ { key: "audit", path: "/settings/audit", icon: FileSearch, label: "settings.auditLogs" },
28
78
  ] : []),
29
- { key: 'about', path: '/settings/about', icon: Info, label: 'settings.about' },
79
+ { key: "about", path: "/settings/about", icon: Info, label: "settings.about" },
30
80
  ],
31
81
  },
32
82
  ];
33
83
 
34
- import { getParams, getRoute } from '../router-state.svelte.js';
84
+ const resolvedSections = $derived(customSections ?? defaultSections);
85
+ const sectionKeys = $derived(new Set(resolvedSections.flatMap((section) => section.items.map((item) => item.key))));
86
+
87
+ import { getParams, getRoute } from "../router-state.svelte.js";
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
+ }
35
100
 
36
101
  let activeKey = $derived.by(() => {
37
102
  const tab = getParams().tab;
38
- if (tab === 'appearance') return 'appearance';
39
- if (tab === 'roles') return 'roles';
40
- if (tab === 'audit') return 'audit';
41
- if (tab === 'about') return 'about';
42
- return 'profile';
103
+ const normalized = normalizeKey(tab);
104
+ if (sectionKeys.has(normalized)) return normalized;
105
+ return content ? normalized : "account";
43
106
  });
44
107
 
45
- // Default redirect to profile
46
108
  $effect(() => {
47
- if (getRoute() === '/settings') {
48
- navigate('/settings/profile');
109
+ if (getRoute() === "/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));
49
118
  }
50
119
  });
51
120
  </script>
52
121
 
53
- <div class="flex flex-col lg:flex-row gap-0 min-h-full">
54
- <!-- Left sidebar navigation -->
55
- <nav class="w-full lg:w-56 shrink-0 bg-muted/10 shadow-[inset_0_-1px_0_rgba(0,0,0,0.05)] lg:shadow-[inset_-1px_0_0_rgba(0,0,0,0.05)] dark:shadow-[inset_-1px_0_0_rgba(255,255,255,0.05)]">
56
- <!-- Mobile: horizontal scroll tabs -->
57
- <div class="flex lg:hidden overflow-x-auto px-4 py-2 gap-1">
58
- {#each sections as section, _i (_i)}
59
- {#each section.items as item, _i (_i)}
60
- {@const active = activeKey === item.key}
61
- <button
62
- class="flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md whitespace-nowrap transition-colors
63
- {active ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-background/50'}"
64
- onclick={() => navigate(item.path)}
65
- >
66
- <item.icon class="h-4 w-4" />
67
- {t(item.label)}
68
- </button>
69
- {/each}
70
- {/each}
71
- </div>
72
-
73
- <!-- Desktop: vertical nav with groups -->
74
- <div class="hidden lg:block py-6 px-3 space-y-6">
75
- <div class="px-3">
76
- <h2 class="text-lg font-semibold text-foreground">{t('settings.title')}</h2>
77
- <p class="text-xs text-muted-foreground mt-1">{t('settings.settingsDescription')}</p>
78
- </div>
79
- {#each sections as section, _i (_i)}
80
- <div>
81
- <h3 class="px-3 mb-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">
82
- {t(section.group)}
83
- </h3>
84
- <div class="space-y-0.5">
85
- {#each section.items as item, _i (_i)}
86
- {@const active = activeKey === item.key}
87
- <button
88
- class="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm transition-all
89
- {active
90
- ? 'bg-primary/10 text-primary font-medium'
91
- : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'}"
92
- onclick={() => navigate(item.path)}
93
- >
94
- <item.icon class="h-4 w-4 shrink-0" />
95
- {t(item.label)}
96
- </button>
97
- {/each}
98
- </div>
99
- </div>
100
- {/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>
101
127
  </div>
102
- </nav>
128
+ </div>
103
129
 
104
- <!-- Right content area -->
105
- <div class="flex-1 p-6 lg:p-8 {activeKey === 'roles' || activeKey === 'audit' ? 'max-w-6xl' : 'max-w-3xl'}">
106
- {#if activeKey === 'profile'}
107
- <ProfilePage />
108
- {:else if activeKey === 'appearance'}
109
- <AppearanceSettings />
110
- {:else if activeKey === 'roles'}
111
- <RolesSettings />
112
- {:else if activeKey === 'audit'}
113
- <AuditLogViewer />
114
- {:else if activeKey === 'about'}
115
- <AboutSettings />
130
+ <div class="{activeKey === 'access' || activeKey === 'audit' || activeKey === 'developer' ? 'max-w-6xl' : 'max-w-3xl'} space-y-6">
131
+ {#if activeKey === "account"}
132
+ {#if profile}{@render profile()}{:else}<ProfilePage />{/if}
133
+ {#if appearance}{@render appearance()}{:else}<AppearanceSettings />{/if}
134
+ {#if notifications}{@render notifications()}{:else}<NotificationsSettings />{/if}
135
+ {:else if activeKey === "access"}
136
+ {#if security}{@render security()}{:else}<SecuritySettings />{/if}
137
+ {#if authProvider}
138
+ {#if roles}{@render roles()}{:else}<RolesSettings />{/if}
139
+ {/if}
140
+ {:else if activeKey === "developer"}
141
+ {#if integrations}{@render integrations()}{:else}<IntegrationsSettings />{/if}
142
+ {#if api}{@render api()}{:else}<ApiSettings />{/if}
143
+ {:else if activeKey === "audit"}
144
+ {#if audit}{@render audit()}{:else}<AuditLogViewer />{/if}
145
+ {:else if activeKey === "about"}
146
+ {#if about}{@render about()}{:else}<AboutSettings />{/if}
147
+ {:else if content}
148
+ {@render content(activeKey)}
116
149
  {/if}
117
150
  </div>
118
151
  </div>
@@ -1,3 +1,27 @@
1
- declare const SettingsPage: import("svelte").Component<Record<string, never>, {}, "">;
1
+ import type { Component, Snippet } from "svelte";
2
+ interface SettingsItem {
3
+ key: string;
4
+ path: string;
5
+ icon: Component;
6
+ label: string;
7
+ }
8
+ interface SettingsSection {
9
+ group: string;
10
+ items: SettingsItem[];
11
+ }
12
+ interface Props {
13
+ customSections?: SettingsSection[];
14
+ profile?: Snippet;
15
+ appearance?: Snippet;
16
+ roles?: Snippet;
17
+ security?: Snippet;
18
+ integrations?: Snippet;
19
+ notifications?: Snippet;
20
+ api?: Snippet;
21
+ audit?: Snippet;
22
+ about?: Snippet;
23
+ content?: Snippet<[string]>;
24
+ }
25
+ declare const SettingsPage: Component<Props, {}, "">;
2
26
  type SettingsPage = ReturnType<typeof SettingsPage>;
3
27
  export default SettingsPage;
@@ -38,10 +38,10 @@
38
38
  </PageHeader>
39
39
 
40
40
  {#if query.isLoading}
41
- <Card.Root class="overflow-hidden border-border/40 shadow-sm">
42
- <Card.Content class="p-0">
41
+ <Card.Root class="overflow-hidden border-border/60 bg-card shadow-none" data-svadmin-show-card>
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 sm:flex-row px-4 sm:px-6 py-3 sm:py-4 {i % 2 === 1 ? 'bg-muted/20' : ''}">
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,14 +49,14 @@
49
49
  </Card.Content>
50
50
  </Card.Root>
51
51
  {:else if query.data?.data}
52
- <Card.Root class="overflow-hidden border-border/40 shadow-sm">
53
- <Card.Content class="p-0">
52
+ <Card.Root class="overflow-hidden border-border/60 bg-card shadow-none" data-svadmin-show-card>
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 sm:flex-row px-4 sm:px-6 py-3 sm:py-4 {i % 2 === 1 ? 'bg-muted/20' : ''}">
58
- <div class="sm:w-1/3 text-xs sm:text-sm font-medium text-muted-foreground mb-1 sm:mb-0">{field.label}</div>
59
- <div class="sm:w-2/3 text-sm text-foreground">
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
+ <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
+ <div class="text-sm text-foreground sm:w-2/3">
60
60
  {#if DisplayComponent && value != null}
61
61
  <DisplayComponent
62
62
  {value}
@@ -72,7 +72,7 @@
72
72
  </Card.Content>
73
73
  </Card.Root>
74
74
  {:else}
75
- <Card.Root class="overflow-hidden border-border/40 shadow-sm">
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}