@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
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
import { Button } from './ui/button/index.js';
|
|
6
6
|
import { Input } from './ui/input/index.js';
|
|
7
7
|
import { Label } from './ui/label/index.js';
|
|
8
|
-
import * as Card from './ui/card/index.js';
|
|
9
8
|
import * as Alert from './ui/alert/index.js';
|
|
10
|
-
import {
|
|
9
|
+
import { User, ArrowLeft, CheckCircle, Loader2, AlertCircle, Shield } from '@lucide/svelte';
|
|
11
10
|
|
|
12
11
|
let { title = 'Admin' } = $props<{
|
|
13
12
|
title?: string;
|
|
@@ -34,79 +33,123 @@
|
|
|
34
33
|
}
|
|
35
34
|
</script>
|
|
36
35
|
|
|
37
|
-
<div class="min-h-screen flex
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
<div class="min-h-screen flex flex-col lg:flex-row">
|
|
37
|
+
<!-- Left brand panel -->
|
|
38
|
+
<div class="hidden lg:flex lg:w-[480px] xl:w-[520px] relative overflow-hidden bg-gradient-to-br from-primary/90 via-primary to-primary/80 shrink-0">
|
|
39
|
+
<div class="absolute inset-0">
|
|
40
|
+
<div class="absolute top-[12%] left-[8%] w-28 h-28 rounded-3xl border border-white/10 bg-white/5 rotate-12 backdrop-blur-sm"></div>
|
|
41
|
+
<div class="absolute top-[55%] left-[55%] w-40 h-40 rounded-full border border-white/10 bg-white/5 -rotate-6 backdrop-blur-sm"></div>
|
|
42
|
+
<div class="absolute bottom-[15%] left-[15%] w-24 h-24 rounded-2xl border border-white/10 bg-white/5 -rotate-12 backdrop-blur-sm"></div>
|
|
43
|
+
<div class="absolute top-[30%] right-[10%] w-16 h-16 rounded-xl border border-white/10 bg-white/5 rotate-45 backdrop-blur-sm"></div>
|
|
44
|
+
<div class="absolute inset-0 opacity-[0.03]" style="background-image: radial-gradient(circle, white 1px, transparent 1px); background-size: 24px 24px;"></div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="relative z-10 flex flex-col justify-between p-10 xl:p-12 w-full">
|
|
48
|
+
<div class="flex items-center gap-3">
|
|
49
|
+
<div class="flex h-9 w-9 items-center justify-center rounded-lg bg-white/15 backdrop-blur-sm">
|
|
50
|
+
<Shield class="h-5 w-5 text-white" />
|
|
51
|
+
</div>
|
|
52
|
+
<span class="text-lg font-semibold text-white tracking-tight">{title}</span>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="space-y-4">
|
|
56
|
+
<h2 class="text-3xl xl:text-4xl font-bold text-white leading-tight">
|
|
57
|
+
{t('auth.forgotPassword')}
|
|
58
|
+
</h2>
|
|
59
|
+
<p class="text-base text-white/70 leading-relaxed max-w-[320px]">
|
|
60
|
+
{t('auth.forgotPasswordDescription')}
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="flex items-center gap-3">
|
|
65
|
+
<div class="h-1 w-8 rounded-full bg-white/30"></div>
|
|
66
|
+
<div class="h-1 w-4 rounded-full bg-white/15"></div>
|
|
67
|
+
<div class="h-1 w-2 rounded-full bg-white/10"></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<!-- Right form panel -->
|
|
73
|
+
<div class="flex-1 flex items-center justify-center bg-background p-6 sm:p-8 lg:p-12">
|
|
74
|
+
<div class="w-full max-w-[400px]">
|
|
75
|
+
<div class="flex items-center gap-3 mb-8 lg:hidden">
|
|
76
|
+
<div class="flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
77
|
+
<Shield class="h-5 w-5" />
|
|
78
|
+
</div>
|
|
79
|
+
<span class="text-lg font-semibold tracking-tight">{title}</span>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="space-y-1 mb-8">
|
|
83
|
+
<h1 class="text-2xl font-semibold tracking-tight">
|
|
42
84
|
{#if sent}
|
|
43
|
-
|
|
85
|
+
{t('auth.resetLinkSent')}
|
|
44
86
|
{:else}
|
|
45
|
-
|
|
87
|
+
{t('auth.forgotPassword')}
|
|
46
88
|
{/if}
|
|
47
|
-
</
|
|
48
|
-
<
|
|
49
|
-
{sent
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
<ArrowLeft class="h-4 w-4 mr-2" />
|
|
63
|
-
{t('auth.backToLogin')}
|
|
64
|
-
</Button>
|
|
89
|
+
</h1>
|
|
90
|
+
<p class="text-sm text-muted-foreground">
|
|
91
|
+
{#if sent}
|
|
92
|
+
{t('auth.resetLinkSent')}
|
|
93
|
+
{:else}
|
|
94
|
+
{t('auth.forgotPasswordDescription')}
|
|
95
|
+
{/if}
|
|
96
|
+
</p>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
{#if sent}
|
|
100
|
+
<div class="space-y-4">
|
|
101
|
+
<div class="flex items-center gap-3 rounded-lg border bg-muted/50 p-4">
|
|
102
|
+
<CheckCircle class="h-5 w-5 text-primary shrink-0" />
|
|
103
|
+
<p class="text-sm text-muted-foreground">{t('auth.resetLinkSent')}</p>
|
|
65
104
|
</div>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
105
|
+
<Button variant="outline" class="w-full" onclick={() => navigate('/login')}>
|
|
106
|
+
<ArrowLeft class="h-4 w-4 mr-2" />
|
|
107
|
+
{t('auth.backToLogin')}
|
|
108
|
+
</Button>
|
|
109
|
+
</div>
|
|
110
|
+
{:else}
|
|
111
|
+
<form onsubmit={handleSubmit} class="space-y-5">
|
|
112
|
+
{#if error}
|
|
113
|
+
<Alert.Root variant="destructive">
|
|
114
|
+
<AlertCircle class="h-4 w-4" />
|
|
115
|
+
<Alert.Description>{error}</Alert.Description>
|
|
116
|
+
</Alert.Root>
|
|
117
|
+
{/if}
|
|
74
118
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
</div>
|
|
119
|
+
<div class="space-y-2">
|
|
120
|
+
<Label for="forgot-identifier">{t('auth.usernameOrEmail')}</Label>
|
|
121
|
+
<div class="relative">
|
|
122
|
+
<User class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none z-[1]" />
|
|
123
|
+
<Input
|
|
124
|
+
id="forgot-identifier"
|
|
125
|
+
type="text"
|
|
126
|
+
placeholder={t('auth.identifierPlaceholder')}
|
|
127
|
+
bind:value={identifier}
|
|
128
|
+
class="pl-9"
|
|
129
|
+
autocomplete="username"
|
|
130
|
+
/>
|
|
88
131
|
</div>
|
|
89
|
-
|
|
90
|
-
<Button type="submit" class="w-full" disabled={forgot.isLoading}>
|
|
91
|
-
{#if forgot.isLoading}
|
|
92
|
-
<Loader2 class="h-4 w-4 animate-spin mr-2" />
|
|
93
|
-
{/if}
|
|
94
|
-
{t('auth.sendResetLink')}
|
|
95
|
-
</Button>
|
|
96
|
-
</form>
|
|
97
|
-
|
|
98
|
-
<div class="flex items-center justify-center mt-5 pt-5 border-t">
|
|
99
|
-
<Button variant="link" class="text-sm h-auto p-0 font-medium inline-flex items-center gap-1" onclick={() => navigate('/login')}>
|
|
100
|
-
<ArrowLeft class="h-3 w-3" />
|
|
101
|
-
{t('auth.backToLogin')}
|
|
102
|
-
</Button>
|
|
103
132
|
</div>
|
|
104
|
-
{/if}
|
|
105
|
-
</Card.CardContent>
|
|
106
|
-
</Card.Card>
|
|
107
133
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
<Button type="submit" class="w-full h-10" disabled={forgot.isLoading}>
|
|
135
|
+
{#if forgot.isLoading}
|
|
136
|
+
<Loader2 class="h-4 w-4 animate-spin mr-2" />
|
|
137
|
+
{/if}
|
|
138
|
+
{t('auth.sendResetLink')}
|
|
139
|
+
</Button>
|
|
140
|
+
</form>
|
|
141
|
+
|
|
142
|
+
<div class="flex items-center justify-center mt-8 pt-6 border-t">
|
|
143
|
+
<Button variant="link" class="text-sm h-auto p-0 font-medium inline-flex items-center gap-1" onclick={() => navigate('/login')}>
|
|
144
|
+
<ArrowLeft class="h-3 w-3" />
|
|
145
|
+
{t('auth.backToLogin')}
|
|
146
|
+
</Button>
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
149
|
+
|
|
150
|
+
<p class="text-xs text-muted-foreground/60 mt-8 text-center">
|
|
151
|
+
Powered by {title}
|
|
152
|
+
</p>
|
|
153
|
+
</div>
|
|
111
154
|
</div>
|
|
112
155
|
</div>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
3
|
import { Moon, Sun, Search, MonitorUp } from '@lucide/svelte';
|
|
3
4
|
import { Button } from './ui/button';
|
|
4
5
|
import TooltipButton from './TooltipButton.svelte';
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
const CustomNotificationPanel = registry.NotificationPanel;
|
|
35
36
|
</script>
|
|
36
37
|
|
|
37
|
-
<header class="sticky top-0 z-30 flex h-
|
|
38
|
+
<header class="sticky top-0 z-30 flex h-16 w-full shrink-0 items-center justify-between border-b border-border/70 bg-card/85 px-4 shadow-sm shadow-slate-900/[0.02] backdrop-blur-xl md:px-6">
|
|
38
39
|
<div class="flex items-center gap-4">
|
|
39
40
|
{#if children}
|
|
40
41
|
{@render children()}
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
{/if}
|
|
54
55
|
|
|
55
56
|
{#if siteUrl}
|
|
56
|
-
<TooltipButton tooltip={t('common.goToSite')
|
|
57
|
+
<TooltipButton tooltip={t('common.goToSite')} href={siteUrl} target="_blank" rel="noopener noreferrer" class="flex items-center justify-center rounded-xl border border-border/60 bg-background/80">
|
|
57
58
|
<MonitorUp class="h-4 w-4 text-muted-foreground transition-colors hover:text-foreground" />
|
|
58
59
|
</TooltipButton>
|
|
59
60
|
{/if}
|
|
@@ -63,10 +64,10 @@
|
|
|
63
64
|
{/if}
|
|
64
65
|
|
|
65
66
|
{#if showSearch && onSearchClick}
|
|
66
|
-
<Button variant="outline" size="sm" onclick={onSearchClick} class="gap-2 text-muted-foreground
|
|
67
|
+
<Button variant="outline" size="sm" onclick={onSearchClick} class="h-10 gap-2 rounded-xl border-border/70 bg-background/85 px-3 text-muted-foreground shadow-sm shadow-slate-900/[0.02] hover:bg-muted/45 sm:min-w-56 sm:justify-start">
|
|
67
68
|
<Search class="h-3.5 w-3.5" />
|
|
68
|
-
<span class="hidden sm:inline
|
|
69
|
-
<kbd class="hidden
|
|
69
|
+
<span class="hidden text-xs sm:inline">{t('common.search')}</span>
|
|
70
|
+
<kbd class="ml-auto hidden items-center gap-0.5 rounded-md border bg-muted/60 px-1.5 py-0.5 font-mono text-[10px] font-medium text-muted-foreground sm:inline-flex">
|
|
70
71
|
<span class="text-xs">⌘</span>K
|
|
71
72
|
</kbd>
|
|
72
73
|
</Button>
|
|
@@ -76,7 +77,7 @@
|
|
|
76
77
|
{#if CustomThemeToggle}
|
|
77
78
|
<CustomThemeToggle />
|
|
78
79
|
{:else}
|
|
79
|
-
<TooltipButton tooltip={t('common.toggleTheme')} onclick={() => toggleTheme()} class="rounded-
|
|
80
|
+
<TooltipButton tooltip={t('common.toggleTheme')} onclick={() => toggleTheme()} class="rounded-xl border border-border/60 bg-background/80">
|
|
80
81
|
{#if getResolvedTheme() === 'dark'}
|
|
81
82
|
<Moon class="h-4 w-4 transition-all" />
|
|
82
83
|
{:else}
|
|
@@ -91,4 +92,3 @@
|
|
|
91
92
|
{/if}
|
|
92
93
|
</div>
|
|
93
94
|
</header>
|
|
94
|
-
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
|
|
2
3
|
import { getDataProvider, getResources, inferResource } from '@svadmin/core';
|
|
3
4
|
import type { InferResult } from '@svadmin/core';
|
|
4
5
|
import { Button } from './ui/button/index.js';
|
|
@@ -99,7 +100,7 @@
|
|
|
99
100
|
bind:value={selectedResource}
|
|
100
101
|
placeholder="— Select a resource —"
|
|
101
102
|
>
|
|
102
|
-
{#each resources as res}
|
|
103
|
+
{#each resources as res, _i (_i)}
|
|
103
104
|
<option value={res.name}>{res.label} ({res.name})</option>
|
|
104
105
|
{/each}
|
|
105
106
|
</Select>
|
|
@@ -131,7 +132,7 @@
|
|
|
131
132
|
|
|
132
133
|
{#if loading}
|
|
133
134
|
<div class="space-y-2">
|
|
134
|
-
{#each Array(4) as _}
|
|
135
|
+
{#each Array(4) as _, _i (_i)}
|
|
135
136
|
<Skeleton class="h-8 w-full" />
|
|
136
137
|
{/each}
|
|
137
138
|
</div>
|
|
@@ -150,7 +151,7 @@
|
|
|
150
151
|
</Table.Row>
|
|
151
152
|
</Table.Header>
|
|
152
153
|
<Table.Body>
|
|
153
|
-
{#each inferResult.fields as field}
|
|
154
|
+
{#each inferResult.fields as field, _i (_i)}
|
|
154
155
|
<Table.Row>
|
|
155
156
|
<Table.Cell class="font-mono text-xs">{field.key}</Table.Cell>
|
|
156
157
|
<Table.Cell>
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { fade
|
|
2
|
+
import { fade } from 'svelte/transition';
|
|
3
3
|
import { useInfiniteList } from '@svadmin/core';
|
|
4
4
|
import type { BaseRecord, Sort, Filter } from '@svadmin/core';
|
|
5
5
|
import type { Snippet } from 'svelte';
|
|
6
6
|
import { t } from '@svadmin/core/i18n';
|
|
7
7
|
import { Skeleton } from './ui/skeleton/index.js';
|
|
8
|
-
import { Button } from './ui/button/index.js';
|
|
9
8
|
import { Loader2 } from '@lucide/svelte';
|
|
10
9
|
import { intersect } from '../actions.js';
|
|
11
10
|
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
{@render loadingSkeleton()}
|
|
60
59
|
{:else}
|
|
61
60
|
<div class="space-y-3">
|
|
62
|
-
{#each Array(5) as _}
|
|
61
|
+
{#each Array(5) as _, _i (_i)}
|
|
63
62
|
<Skeleton class="h-16 w-full rounded-lg" />
|
|
64
63
|
{/each}
|
|
65
64
|
</div>
|
|
@@ -71,7 +70,7 @@
|
|
|
71
70
|
<p class="text-center text-sm text-muted-foreground py-8">{t('empty.title')}</p>
|
|
72
71
|
{/if}
|
|
73
72
|
{:else}
|
|
74
|
-
{#each allItems as item, index}
|
|
73
|
+
{#each allItems as item, index (index)}
|
|
75
74
|
<div in:fade={{ duration: 200, delay: index < 20 ? index * 30 : 0 }}>
|
|
76
75
|
{@render children({ item, index })}
|
|
77
76
|
</div>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { t } from '@svadmin/core/i18n';
|
|
3
|
+
import { AlertCircle, Cloud, FolderGit, MessageCircle, Plug } from '@lucide/svelte';
|
|
4
|
+
import type { Component } from 'svelte';
|
|
5
|
+
import * as Card from './ui/card/index.js';
|
|
6
|
+
import { Switch } from './ui/switch/index.js';
|
|
7
|
+
|
|
8
|
+
interface Integration {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
connected: boolean;
|
|
13
|
+
Icon: Component;
|
|
14
|
+
tone: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let integrations = $state<Integration[]>([
|
|
18
|
+
{ id: 'source-control', name: 'Source Control', description: 'Sync repository metadata, deployments, and commit activity.', connected: true, Icon: FolderGit, tone: 'bg-zinc-900 text-white' },
|
|
19
|
+
{ id: 'chatops', name: 'ChatOps', description: 'Send operational alerts and audit summaries to workspace channels.', connected: false, Icon: MessageCircle, tone: 'bg-violet-700 text-white' },
|
|
20
|
+
{ id: 'identity-cloud', name: 'Identity Cloud', description: 'Configure SSO and workspace identity handoff.', connected: true, Icon: Cloud, tone: 'bg-blue-600 text-white' },
|
|
21
|
+
{ id: 'webhooks', name: 'Webhooks', description: 'Register outbound webhooks for internal tools and monitors.', connected: false, Icon: Plug, tone: 'bg-emerald-700 text-white' },
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
function toggleConnection(id: string, connected: boolean) {
|
|
25
|
+
integrations = integrations.map((item) => (item.id === id ? { ...item, connected } : item));
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<div class="space-y-6">
|
|
30
|
+
<div>
|
|
31
|
+
<h2 class="text-xl font-semibold text-foreground">{t('settings.integrations')}</h2>
|
|
32
|
+
<p class="mt-1 text-sm text-muted-foreground">{t('settings.integrationsDescription')}</p>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="grid gap-4">
|
|
36
|
+
{#each integrations as integration (integration.id)}
|
|
37
|
+
<Card.Card>
|
|
38
|
+
<Card.CardContent class="p-5">
|
|
39
|
+
<div class="flex flex-col justify-between gap-4 sm:flex-row sm:items-center">
|
|
40
|
+
<div class="flex items-start gap-4">
|
|
41
|
+
<div class="shrink-0 rounded-lg p-3 {integration.tone}">
|
|
42
|
+
<integration.Icon class="h-5 w-5" />
|
|
43
|
+
</div>
|
|
44
|
+
<div class="space-y-1">
|
|
45
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
46
|
+
<span class="font-semibold text-foreground">{integration.name}</span>
|
|
47
|
+
<span class="rounded-full px-2 py-0.5 text-[10px] font-semibold {integration.connected ? 'bg-green-500/10 text-green-600' : 'bg-muted text-muted-foreground'}">
|
|
48
|
+
{integration.connected ? t('integrations.statusConnected') : t('integrations.statusDisconnected')}
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
<p class="text-sm text-muted-foreground">{integration.description}</p>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="flex items-center gap-3">
|
|
55
|
+
<span class="text-xs text-muted-foreground">{integration.connected ? t('integrations.enabled') : t('integrations.disabled')}</span>
|
|
56
|
+
<Switch checked={integration.connected} onCheckedChange={(checked) => toggleConnection(integration.id, checked)} />
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</Card.CardContent>
|
|
60
|
+
</Card.Card>
|
|
61
|
+
{/each}
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<Card.Card class="border-dashed bg-muted/20">
|
|
65
|
+
<Card.CardContent class="flex items-center gap-3 p-5 text-sm text-muted-foreground">
|
|
66
|
+
<AlertCircle class="h-4 w-4 shrink-0" />
|
|
67
|
+
<span>{t('integrations.addMoreHint')}</span>
|
|
68
|
+
</Card.CardContent>
|
|
69
|
+
</Card.Card>
|
|
70
|
+
</div>
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
<Dialog.Title>{t('common.keyboardShortcuts') || 'Keyboard Shortcuts'}</Dialog.Title>
|
|
40
40
|
</Dialog.Header>
|
|
41
41
|
<div class="space-y-4 py-2">
|
|
42
|
-
{#each shortcuts as group}
|
|
42
|
+
{#each shortcuts as group, _i (_i)}
|
|
43
43
|
<div>
|
|
44
44
|
<h4 class="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2">{group.group}</h4>
|
|
45
45
|
<div class="space-y-1.5">
|
|
46
|
-
{#each group.items as shortcut}
|
|
46
|
+
{#each group.items as shortcut, _i (_i)}
|
|
47
47
|
<div class="flex items-center justify-between py-1">
|
|
48
48
|
<span class="text-sm text-foreground">{shortcut.label}</span>
|
|
49
49
|
<div class="flex items-center gap-1">
|
|
50
|
-
{#each shortcut.keys as key}
|
|
50
|
+
{#each shortcut.keys as key, _i (_i)}
|
|
51
51
|
<kbd class="inline-flex h-5 min-w-5 items-center justify-center rounded border bg-muted px-1.5 text-[10px] font-medium font-mono text-muted-foreground">{key}</kbd>
|
|
52
52
|
{/each}
|
|
53
53
|
</div>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
/* eslint-disable svelte/no-useless-children-snippet */
|
|
2
3
|
import type { Snippet } from 'svelte';
|
|
3
4
|
import { fade, fly } from 'svelte/transition';
|
|
4
5
|
import Sidebar from './Sidebar.svelte';
|
|
@@ -13,7 +14,6 @@
|
|
|
13
14
|
import { navigate } from '@svadmin/core/router';
|
|
14
15
|
import { getPath } from '../router-state.svelte.js';
|
|
15
16
|
import { Skeleton } from './ui/skeleton/index.js';
|
|
16
|
-
import { Button } from './ui/button/index.js';
|
|
17
17
|
import * as Sheet from './ui/sheet/index.js';
|
|
18
18
|
import { Menu } from '@lucide/svelte';
|
|
19
19
|
import { getComponentRegistry } from '../component-registry.svelte.js';
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
|
|
25
25
|
let { children, title = 'Admin', menu, siteUrl, routeMode = 'auto' }: { children: Snippet; title?: string; menu?: MenuItem[]; siteUrl?: string; routeMode?: 'hash' | 'path' | 'auto' } = $props();
|
|
26
26
|
|
|
27
|
-
let
|
|
27
|
+
let _hasAuth = true;
|
|
28
28
|
let auth: ReturnType<typeof getAuthProvider> | null = null;
|
|
29
29
|
try {
|
|
30
30
|
auth = getAuthProvider();
|
|
31
31
|
} catch {
|
|
32
|
-
|
|
32
|
+
_hasAuth = false;
|
|
33
33
|
}
|
|
34
34
|
let loading = $state(true);
|
|
35
35
|
let identity = $state<Identity | null>(null);
|
|
@@ -104,26 +104,26 @@
|
|
|
104
104
|
ontouchend={handleTouchEnd}
|
|
105
105
|
/>
|
|
106
106
|
{#if loading}
|
|
107
|
-
<div class="flex h-screen" in:fade={{ duration: 150 }}>
|
|
108
|
-
<div class="hidden
|
|
107
|
+
<div class="flex h-screen bg-muted/30" in:fade={{ duration: 150 }}>
|
|
108
|
+
<div class="hidden w-64 space-y-4 border-r border-border/70 bg-card p-4 md:block">
|
|
109
109
|
<Skeleton class="h-8 w-32" />
|
|
110
110
|
<div class="space-y-2 mt-6">
|
|
111
|
-
{#each Array(5) as _}
|
|
111
|
+
{#each Array(5) as _, _i (_i)}
|
|
112
112
|
<Skeleton class="h-9 w-full rounded-lg" />
|
|
113
113
|
{/each}
|
|
114
114
|
</div>
|
|
115
115
|
</div>
|
|
116
|
-
<div class="flex-1
|
|
116
|
+
<div class="flex-1 space-y-6 p-8">
|
|
117
117
|
<Skeleton class="h-8 w-48" />
|
|
118
118
|
<div class="space-y-3">
|
|
119
|
-
{#each Array(4) as _}
|
|
119
|
+
{#each Array(4) as _, _i (_i)}
|
|
120
120
|
<Skeleton class="h-12 w-full" />
|
|
121
121
|
{/each}
|
|
122
122
|
</div>
|
|
123
123
|
</div>
|
|
124
124
|
</div>
|
|
125
125
|
{:else}
|
|
126
|
-
<div class="flex h-screen bg-background" in:fade={{ duration: 200, delay: 50 }}>
|
|
126
|
+
<div class="flex h-screen bg-[radial-gradient(circle_at_top_left,hsl(var(--primary)/0.08),transparent_32rem),linear-gradient(180deg,hsl(var(--muted)/0.48),hsl(var(--background))_42rem)]" in:fade={{ duration: 200, delay: 50 }}>
|
|
127
127
|
<!-- Desktop sidebar -->
|
|
128
128
|
<div class="hidden md:block">
|
|
129
129
|
<Sidebar {collapsed} {identity} {title} {menu} {routeMode} onToggle={() => collapsed = !collapsed} onLogout={handleLogout} />
|
|
@@ -138,8 +138,8 @@
|
|
|
138
138
|
|
|
139
139
|
<div
|
|
140
140
|
class="flex-1 flex flex-col overflow-hidden transition-all duration-300"
|
|
141
|
-
class:md:ml-
|
|
142
|
-
class:md:ml-
|
|
141
|
+
class:md:ml-72={!collapsed}
|
|
142
|
+
class:md:ml-20={collapsed}
|
|
143
143
|
>
|
|
144
144
|
<!-- Header with mobile hamburger -->
|
|
145
145
|
<Header
|
|
@@ -163,9 +163,9 @@
|
|
|
163
163
|
</Header>
|
|
164
164
|
|
|
165
165
|
<!-- Content area -->
|
|
166
|
-
<main class="flex-1 overflow-y-auto
|
|
166
|
+
<main class="flex-1 overflow-y-auto px-4 py-5 sm:px-6 md:px-8 md:py-7">
|
|
167
167
|
{#key getPath()}
|
|
168
|
-
<div in:fly={{ x: 20, duration: 150 }} out:fade={{ duration: 80 }}>
|
|
168
|
+
<div class="mx-auto w-full max-w-[1680px]" in:fly={{ x: 20, duration: 150 }} out:fade={{ duration: 80 }}>
|
|
169
169
|
{@render children()}
|
|
170
170
|
</div>
|
|
171
171
|
{/key}
|