@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.
- package/dist/app.css +746 -22
- package/dist/component-registry.svelte.d.ts +1 -0
- package/dist/components/AdminApp.svelte +94 -15
- package/dist/components/AdminApp.svelte.d.ts +32 -2
- package/dist/components/ApiSettings.svelte +191 -0
- package/dist/components/ApiSettings.svelte.d.ts +3 -0
- package/dist/components/AutoForm.svelte +47 -42
- package/dist/components/AutoTable.svelte +29 -24
- package/dist/components/ErrorPage.svelte +58 -0
- package/dist/components/ErrorPage.svelte.d.ts +8 -0
- package/dist/components/FieldRenderer.svelte +2 -2
- package/dist/components/ForgotPasswordPage.svelte +111 -68
- package/dist/components/Header.svelte +6 -7
- package/dist/components/IntegrationsSettings.svelte +70 -0
- package/dist/components/IntegrationsSettings.svelte.d.ts +4 -0
- package/dist/components/Layout.svelte +7 -7
- package/dist/components/LoginPage.svelte +207 -105
- package/dist/components/LoginPage.svelte.d.ts +3 -15
- 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/RegisterPage.svelte +103 -65
- package/dist/components/ResourceOperationsPage.svelte +151 -0
- package/dist/components/ResourceOperationsPage.svelte.d.ts +37 -0
- package/dist/components/SecuritySettings.svelte +179 -0
- package/dist/components/SecuritySettings.svelte.d.ts +4 -0
- package/dist/components/SettingsPage.svelte +120 -87
- package/dist/components/SettingsPage.svelte.d.ts +25 -1
- package/dist/components/ShowPage.svelte +9 -9
- package/dist/components/Sidebar.svelte +278 -189
- package/dist/components/SidebarItem.svelte +77 -18
- 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/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 +135 -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/network/TeamCrewTablePage.svelte +155 -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/pagination/pagination-ellipsis.svelte +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.js +27 -0
- package/dist/router-state.svelte.js +17 -0
- package/package.json +2 -2
|
@@ -5,44 +5,46 @@
|
|
|
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
9
|
import PasswordInput from './PasswordInput.svelte';
|
|
11
10
|
import { Separator } from './ui/separator/index.js';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Social provider config for the login page.
|
|
16
|
-
* Each entry renders a button that calls `onClick()` when clicked.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```ts
|
|
20
|
-
* const socialProviders = [
|
|
21
|
-
* { name: 'Google', icon: '🔵', onClick: () => googleAuth.login({}) },
|
|
22
|
-
* { name: 'GitHub', icon: '🐙', onClick: () => githubAuth.login({}) },
|
|
23
|
-
* ];
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
11
|
+
import { User, Loader2, AlertCircle, Shield } from '@lucide/svelte';
|
|
12
|
+
|
|
26
13
|
interface SocialProvider {
|
|
27
|
-
/** Display name (e.g., 'Google') */
|
|
28
14
|
name: string;
|
|
29
|
-
/** Icon character, emoji, or SVG string */
|
|
30
15
|
icon?: string;
|
|
31
|
-
/** Handler to initiate social login */
|
|
32
16
|
onClick: () => void | Promise<void>;
|
|
33
17
|
}
|
|
34
18
|
|
|
35
|
-
let {
|
|
19
|
+
let {
|
|
20
|
+
title = 'Admin',
|
|
21
|
+
onSuccess,
|
|
22
|
+
socialProviders = [],
|
|
23
|
+
defaultIdentifier = '',
|
|
24
|
+
defaultPassword = '',
|
|
25
|
+
loginHint,
|
|
26
|
+
} = $props<{
|
|
36
27
|
title?: string;
|
|
37
28
|
onSuccess?: () => void;
|
|
38
29
|
socialProviders?: SocialProvider[];
|
|
30
|
+
defaultIdentifier?: string;
|
|
31
|
+
defaultPassword?: string;
|
|
32
|
+
loginHint?: string;
|
|
39
33
|
}>();
|
|
40
34
|
|
|
41
35
|
const login = useLogin({ errorMessage: false });
|
|
42
36
|
const authProvider = getAuthProvider();
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
function getInitialIdentifier() {
|
|
39
|
+
return defaultIdentifier;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getInitialPassword() {
|
|
43
|
+
return defaultPassword;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let identifier = $state(getInitialIdentifier());
|
|
47
|
+
let password = $state(getInitialPassword());
|
|
46
48
|
let error = $state('');
|
|
47
49
|
|
|
48
50
|
async function handleSubmit(e: Event) {
|
|
@@ -61,103 +63,203 @@
|
|
|
61
63
|
}
|
|
62
64
|
</script>
|
|
63
65
|
|
|
64
|
-
<div class="min-h-screen flex
|
|
65
|
-
<div class="w-
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
<div class="min-h-screen flex flex-col lg:flex-row">
|
|
67
|
+
<div class="hidden lg:flex lg:w-[500px] xl:w-[560px] relative overflow-hidden bg-gradient-to-br from-primary/95 via-primary to-primary/85 shrink-0">
|
|
68
|
+
<div class="absolute inset-0">
|
|
69
|
+
<div class="absolute top-[12%] left-[8%] h-32 w-32 rounded-3xl border border-white/10 bg-white/5 rotate-12 backdrop-blur-sm"></div>
|
|
70
|
+
<div class="absolute top-[54%] left-[54%] h-44 w-44 rounded-full border border-white/10 bg-white/5 -rotate-6 backdrop-blur-sm"></div>
|
|
71
|
+
<div class="absolute bottom-[14%] left-[13%] h-28 w-28 rounded-2xl border border-white/10 bg-white/5 -rotate-12 backdrop-blur-sm"></div>
|
|
72
|
+
<div class="absolute top-[30%] right-[10%] h-20 w-20 rounded-xl border border-white/10 bg-white/5 rotate-45 backdrop-blur-sm"></div>
|
|
73
|
+
<div class="absolute inset-0 opacity-[0.04]" style="background-image: radial-gradient(circle, white 1px, transparent 1px); background-size: 24px 24px;"></div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div class="relative z-10 flex flex-col justify-between p-10 xl:p-12 w-full">
|
|
77
|
+
<div class="flex items-center gap-3">
|
|
78
|
+
<div class="flex h-11 w-11 items-center justify-center rounded-xl bg-white/15 backdrop-blur-sm">
|
|
79
|
+
<Shield class="h-5 w-5 text-white" />
|
|
70
80
|
</div>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
<span class="text-xl font-semibold text-white tracking-tight">{title}</span>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="max-w-[360px] space-y-4">
|
|
85
|
+
<h2 class="text-4xl font-bold text-white leading-tight">
|
|
86
|
+
{t('auth.welcomeBack')}
|
|
87
|
+
</h2>
|
|
88
|
+
<p class="text-lg text-white/75 leading-relaxed">
|
|
89
|
+
{t('auth.welcomeMessage')}
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="flex items-center gap-3">
|
|
94
|
+
<div class="h-1.5 w-10 rounded-full bg-white/40"></div>
|
|
95
|
+
<div class="h-1.5 w-5 rounded-full bg-white/20"></div>
|
|
96
|
+
<div class="h-1.5 w-2 rounded-full bg-white/15"></div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div class="flex-1 flex items-center justify-center bg-background p-6 sm:p-10 lg:p-14">
|
|
102
|
+
<div class="w-full max-w-[460px] space-y-8">
|
|
103
|
+
<div class="flex items-center gap-3 mb-8 lg:hidden">
|
|
104
|
+
<div class="flex h-11 w-11 items-center justify-center rounded-xl bg-primary/10 text-primary">
|
|
105
|
+
<Shield class="h-5 w-5" />
|
|
106
|
+
</div>
|
|
107
|
+
<span class="text-xl font-semibold tracking-tight text-foreground">{title}</span>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="space-y-2.5">
|
|
111
|
+
<h1 class="text-3xl font-semibold tracking-tight text-foreground">
|
|
112
|
+
{t('auth.welcomeBack')}
|
|
113
|
+
</h1>
|
|
114
|
+
<p class="text-base text-muted-foreground leading-relaxed">
|
|
115
|
+
{t('auth.welcomeMessage')}
|
|
116
|
+
</p>
|
|
117
|
+
</div>
|
|
82
118
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
{#if loginHint || defaultIdentifier || defaultPassword}
|
|
120
|
+
<div class="rounded-2xl border border-primary/15 bg-primary/[0.06] p-4 shadow-sm">
|
|
121
|
+
{#if loginHint}
|
|
122
|
+
<p class="text-sm font-medium text-primary">{loginHint}</p>
|
|
123
|
+
{/if}
|
|
124
|
+
<div class="mt-3 grid gap-2 sm:grid-cols-2">
|
|
125
|
+
{#if defaultIdentifier}
|
|
126
|
+
<div class="rounded-xl border border-border/70 bg-background/80 px-3 py-2">
|
|
127
|
+
<p class="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
|
|
128
|
+
{t('auth.usernameOrEmail')}
|
|
129
|
+
</p>
|
|
130
|
+
<p class="mt-1 font-mono text-sm font-semibold text-foreground">{defaultIdentifier}</p>
|
|
131
|
+
</div>
|
|
132
|
+
{/if}
|
|
133
|
+
{#if defaultPassword}
|
|
134
|
+
<div class="rounded-xl border border-border/70 bg-background/80 px-3 py-2">
|
|
135
|
+
<p class="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
|
|
136
|
+
{t('auth.password')}
|
|
137
|
+
</p>
|
|
138
|
+
<p class="mt-1 font-mono text-sm font-semibold text-foreground">{defaultPassword}</p>
|
|
139
|
+
</div>
|
|
140
|
+
{/if}
|
|
96
141
|
</div>
|
|
142
|
+
</div>
|
|
143
|
+
{/if}
|
|
97
144
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
145
|
+
<form onsubmit={handleSubmit} class="space-y-6 login-form">
|
|
146
|
+
{#if error}
|
|
147
|
+
<Alert.Root variant="destructive" class="rounded-xl border border-destructive/20 bg-destructive/5">
|
|
148
|
+
<AlertCircle class="h-4 w-4" />
|
|
149
|
+
<Alert.Description class="text-sm font-medium">{error}</Alert.Description>
|
|
150
|
+
</Alert.Root>
|
|
151
|
+
{/if}
|
|
152
|
+
|
|
153
|
+
<div class="space-y-2.5">
|
|
154
|
+
<Label for="login-identifier" class="text-sm font-semibold text-foreground/80 pl-0.5">
|
|
155
|
+
{t('auth.usernameOrEmail')}
|
|
156
|
+
</Label>
|
|
157
|
+
<div class="relative">
|
|
158
|
+
<User class="absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground pointer-events-none z-[1]" />
|
|
159
|
+
<Input
|
|
160
|
+
id="login-identifier"
|
|
161
|
+
type="text"
|
|
162
|
+
placeholder={t('auth.identifierPlaceholder')}
|
|
163
|
+
bind:value={identifier}
|
|
164
|
+
class="pl-12"
|
|
165
|
+
autocomplete="username"
|
|
112
166
|
/>
|
|
113
167
|
</div>
|
|
168
|
+
</div>
|
|
114
169
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
170
|
+
<div class="space-y-2.5">
|
|
171
|
+
<div class="flex items-center justify-between pl-0.5">
|
|
172
|
+
<Label for="login-password" class="text-sm font-semibold text-foreground/80">
|
|
173
|
+
{t('auth.password')}
|
|
174
|
+
</Label>
|
|
175
|
+
{#if authProvider.forgotPassword}
|
|
176
|
+
<Button variant="link" class="text-xs h-auto p-0 font-medium hover:text-primary transition-colors" onclick={() => navigate('/forgot-password')}>
|
|
177
|
+
{t('auth.forgotPasswordLink')}
|
|
178
|
+
</Button>
|
|
118
179
|
{/if}
|
|
119
|
-
{t('auth.loginButton')}
|
|
120
|
-
</Button>
|
|
121
|
-
</form>
|
|
122
|
-
|
|
123
|
-
{#if socialProviders.length > 0}
|
|
124
|
-
<div class="relative my-5">
|
|
125
|
-
<Separator />
|
|
126
|
-
<span class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-card px-3 text-xs text-muted-foreground">
|
|
127
|
-
{t('auth.orContinueWith')}
|
|
128
|
-
</span>
|
|
129
180
|
</div>
|
|
181
|
+
<PasswordInput
|
|
182
|
+
id="login-password"
|
|
183
|
+
label=""
|
|
184
|
+
bind:value={password}
|
|
185
|
+
autocomplete="current-password"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
130
188
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{#if provider.icon}
|
|
139
|
-
<span class="mr-2">{provider.icon}</span>
|
|
140
|
-
{/if}
|
|
141
|
-
{provider.name}
|
|
142
|
-
</Button>
|
|
143
|
-
{/each}
|
|
144
|
-
</div>
|
|
145
|
-
{/if}
|
|
189
|
+
<Button type="submit" class="w-full h-14 rounded-xl text-base font-semibold shadow-md shadow-primary/20 hover:shadow-lg hover:shadow-primary/25 hover:-translate-y-[1px] active:translate-y-0 transition-all duration-200 mt-2" disabled={login.isLoading}>
|
|
190
|
+
{#if login.isLoading}
|
|
191
|
+
<Loader2 class="h-5 w-5 animate-spin mr-2" />
|
|
192
|
+
{/if}
|
|
193
|
+
{t('auth.loginButton')}
|
|
194
|
+
</Button>
|
|
195
|
+
</form>
|
|
146
196
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
197
|
+
{#if socialProviders.length > 0}
|
|
198
|
+
<div class="relative my-8">
|
|
199
|
+
<Separator />
|
|
200
|
+
<span class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-background px-4 text-xs font-semibold text-muted-foreground tracking-wider uppercase">
|
|
201
|
+
{t('auth.orContinueWith')}
|
|
202
|
+
</span>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div class="grid gap-3" class:grid-cols-2={socialProviders.length >= 2}>
|
|
206
|
+
{#each socialProviders as provider (provider.name)}
|
|
207
|
+
<Button
|
|
208
|
+
variant="outline"
|
|
209
|
+
class="w-full h-12 rounded-xl font-semibold border-input/60 hover:bg-muted/50 hover:text-accent-foreground transition-all duration-200"
|
|
210
|
+
onclick={provider.onClick}
|
|
211
|
+
>
|
|
212
|
+
{#if provider.icon}
|
|
213
|
+
<span class="mr-2 text-lg">{provider.icon}</span>
|
|
214
|
+
{/if}
|
|
215
|
+
{provider.name}
|
|
152
216
|
</Button>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
217
|
+
{/each}
|
|
218
|
+
</div>
|
|
219
|
+
{/if}
|
|
220
|
+
|
|
221
|
+
{#if authProvider.register}
|
|
222
|
+
<div class="flex items-center justify-center gap-1.5 mt-8 pt-6 border-t border-border/60">
|
|
223
|
+
<span class="text-sm text-muted-foreground">{t('auth.noAccount')}</span>
|
|
224
|
+
<Button variant="link" class="text-sm h-auto p-0 font-semibold text-primary hover:underline" onclick={() => navigate('/register')}>
|
|
225
|
+
{t('auth.register')}
|
|
226
|
+
</Button>
|
|
227
|
+
</div>
|
|
228
|
+
{/if}
|
|
157
229
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
230
|
+
<p class="text-xs text-muted-foreground/50 mt-8 text-center tracking-wide font-medium">
|
|
231
|
+
Powered by {title}
|
|
232
|
+
</p>
|
|
233
|
+
</div>
|
|
161
234
|
</div>
|
|
162
235
|
</div>
|
|
163
236
|
|
|
237
|
+
<style>
|
|
238
|
+
.login-form :global(input[data-slot='input']) {
|
|
239
|
+
height: 3.5rem;
|
|
240
|
+
border-radius: 0.75rem;
|
|
241
|
+
border-color: color-mix(in oklch, var(--border) 88%, var(--foreground));
|
|
242
|
+
background-color: var(--background);
|
|
243
|
+
font-size: 0.975rem;
|
|
244
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.login-form :global(input[data-slot='input']:focus-visible) {
|
|
248
|
+
border-color: var(--primary);
|
|
249
|
+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--primary) 18%, transparent);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.login-form :global(.relative > svg:first-of-type) {
|
|
253
|
+
left: 1rem;
|
|
254
|
+
height: 1.25rem;
|
|
255
|
+
width: 1.25rem;
|
|
256
|
+
color: color-mix(in oklch, var(--muted-foreground) 82%, var(--foreground));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.login-form :global(.relative > button) {
|
|
260
|
+
right: 0.625rem;
|
|
261
|
+
height: 2.25rem;
|
|
262
|
+
width: 2.25rem;
|
|
263
|
+
border-radius: 0.5rem;
|
|
264
|
+
}
|
|
265
|
+
</style>
|
|
@@ -1,27 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Social provider config for the login page.
|
|
3
|
-
* Each entry renders a button that calls `onClick()` when clicked.
|
|
4
|
-
*
|
|
5
|
-
* @example
|
|
6
|
-
* ```ts
|
|
7
|
-
* const socialProviders = [
|
|
8
|
-
* { name: 'Google', icon: '🔵', onClick: () => googleAuth.login({}) },
|
|
9
|
-
* { name: 'GitHub', icon: '🐙', onClick: () => githubAuth.login({}) },
|
|
10
|
-
* ];
|
|
11
|
-
* ```
|
|
12
|
-
*/
|
|
13
1
|
interface SocialProvider {
|
|
14
|
-
/** Display name (e.g., 'Google') */
|
|
15
2
|
name: string;
|
|
16
|
-
/** Icon character, emoji, or SVG string */
|
|
17
3
|
icon?: string;
|
|
18
|
-
/** Handler to initiate social login */
|
|
19
4
|
onClick: () => void | Promise<void>;
|
|
20
5
|
}
|
|
21
6
|
type $$ComponentProps = {
|
|
22
7
|
title?: string;
|
|
23
8
|
onSuccess?: () => void;
|
|
24
9
|
socialProviders?: SocialProvider[];
|
|
10
|
+
defaultIdentifier?: string;
|
|
11
|
+
defaultPassword?: string;
|
|
12
|
+
loginHint?: string;
|
|
25
13
|
};
|
|
26
14
|
declare const LoginPage: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
27
15
|
type LoginPage = ReturnType<typeof LoginPage>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { t } from '@svadmin/core/i18n';
|
|
3
|
+
import { Bell, Mail, Smartphone } from '@lucide/svelte';
|
|
4
|
+
import * as Card from './ui/card/index.js';
|
|
5
|
+
import { Button } from './ui/button/index.js';
|
|
6
|
+
import { Label } from './ui/label/index.js';
|
|
7
|
+
import { Switch } from './ui/switch/index.js';
|
|
8
|
+
|
|
9
|
+
let emailAlerts = $state({ security: true, activity: false, reports: true });
|
|
10
|
+
let pushAlerts = $state({ security: true, activity: true, reports: false });
|
|
11
|
+
let smsAlerts = $state({ security: true });
|
|
12
|
+
let isSaved = $state(false);
|
|
13
|
+
|
|
14
|
+
function saveSettings() {
|
|
15
|
+
isSaved = true;
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
isSaved = false;
|
|
18
|
+
}, 3000);
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<div class="space-y-6">
|
|
23
|
+
<div class="flex flex-col justify-between gap-3 sm:flex-row sm:items-start">
|
|
24
|
+
<div>
|
|
25
|
+
<h2 class="text-xl font-semibold text-foreground">{t('settings.notifications')}</h2>
|
|
26
|
+
<p class="mt-1 text-sm text-muted-foreground">{t('settings.notificationsDescription')}</p>
|
|
27
|
+
</div>
|
|
28
|
+
<Button onclick={saveSettings}>{isSaved ? t('common.autoSaved') : t('common.save')}</Button>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<Card.Card>
|
|
32
|
+
<Card.CardHeader>
|
|
33
|
+
<Card.CardTitle class="flex items-center gap-2 text-base">
|
|
34
|
+
<Mail class="h-4 w-4 text-muted-foreground" />
|
|
35
|
+
{t('notifications.emailTitle')}
|
|
36
|
+
</Card.CardTitle>
|
|
37
|
+
<Card.CardDescription>{t('notifications.emailDescription')}</Card.CardDescription>
|
|
38
|
+
</Card.CardHeader>
|
|
39
|
+
<Card.CardContent class="space-y-4">
|
|
40
|
+
<div class="flex items-center justify-between gap-4 border-b py-2">
|
|
41
|
+
<div class="space-y-1">
|
|
42
|
+
<Label for="email-security">{t('notifications.securityAlerts')}</Label>
|
|
43
|
+
<p class="text-xs text-muted-foreground">{t('notifications.securityAlertsDesc')}</p>
|
|
44
|
+
</div>
|
|
45
|
+
<Switch id="email-security" bind:checked={emailAlerts.security} />
|
|
46
|
+
</div>
|
|
47
|
+
<div class="flex items-center justify-between gap-4 border-b py-2">
|
|
48
|
+
<div class="space-y-1">
|
|
49
|
+
<Label for="email-activity">{t('notifications.activityLogs')}</Label>
|
|
50
|
+
<p class="text-xs text-muted-foreground">{t('notifications.activityLogsDesc')}</p>
|
|
51
|
+
</div>
|
|
52
|
+
<Switch id="email-activity" bind:checked={emailAlerts.activity} />
|
|
53
|
+
</div>
|
|
54
|
+
<div class="flex items-center justify-between gap-4 py-2">
|
|
55
|
+
<div class="space-y-1">
|
|
56
|
+
<Label for="email-reports">{t('notifications.systemAlerts')}</Label>
|
|
57
|
+
<p class="text-xs text-muted-foreground">{t('notifications.systemAlertsDesc')}</p>
|
|
58
|
+
</div>
|
|
59
|
+
<Switch id="email-reports" bind:checked={emailAlerts.reports} />
|
|
60
|
+
</div>
|
|
61
|
+
</Card.CardContent>
|
|
62
|
+
</Card.Card>
|
|
63
|
+
|
|
64
|
+
<Card.Card>
|
|
65
|
+
<Card.CardHeader>
|
|
66
|
+
<Card.CardTitle class="flex items-center gap-2 text-base">
|
|
67
|
+
<Bell class="h-4 w-4 text-muted-foreground" />
|
|
68
|
+
{t('notifications.pushTitle')}
|
|
69
|
+
</Card.CardTitle>
|
|
70
|
+
<Card.CardDescription>{t('notifications.pushDescription')}</Card.CardDescription>
|
|
71
|
+
</Card.CardHeader>
|
|
72
|
+
<Card.CardContent class="space-y-4">
|
|
73
|
+
<div class="flex items-center justify-between gap-4 border-b py-2">
|
|
74
|
+
<div class="space-y-1">
|
|
75
|
+
<Label for="push-security">{t('notifications.securityAlerts')}</Label>
|
|
76
|
+
<p class="text-xs text-muted-foreground">{t('notifications.securityPushDesc')}</p>
|
|
77
|
+
</div>
|
|
78
|
+
<Switch id="push-security" bind:checked={pushAlerts.security} />
|
|
79
|
+
</div>
|
|
80
|
+
<div class="flex items-center justify-between gap-4 border-b py-2">
|
|
81
|
+
<div class="space-y-1">
|
|
82
|
+
<Label for="push-activity">{t('notifications.activityLogs')}</Label>
|
|
83
|
+
<p class="text-xs text-muted-foreground">{t('notifications.activityPushDesc')}</p>
|
|
84
|
+
</div>
|
|
85
|
+
<Switch id="push-activity" bind:checked={pushAlerts.activity} />
|
|
86
|
+
</div>
|
|
87
|
+
<div class="flex items-center justify-between gap-4 py-2">
|
|
88
|
+
<div class="space-y-1">
|
|
89
|
+
<Label for="push-reports">{t('notifications.systemAlerts')}</Label>
|
|
90
|
+
<p class="text-xs text-muted-foreground">{t('notifications.systemPushDesc')}</p>
|
|
91
|
+
</div>
|
|
92
|
+
<Switch id="push-reports" bind:checked={pushAlerts.reports} />
|
|
93
|
+
</div>
|
|
94
|
+
</Card.CardContent>
|
|
95
|
+
</Card.Card>
|
|
96
|
+
|
|
97
|
+
<Card.Card>
|
|
98
|
+
<Card.CardHeader>
|
|
99
|
+
<Card.CardTitle class="flex items-center gap-2 text-base">
|
|
100
|
+
<Smartphone class="h-4 w-4 text-muted-foreground" />
|
|
101
|
+
{t('notifications.smsTitle')}
|
|
102
|
+
</Card.CardTitle>
|
|
103
|
+
<Card.CardDescription>{t('notifications.smsDescription')}</Card.CardDescription>
|
|
104
|
+
</Card.CardHeader>
|
|
105
|
+
<Card.CardContent>
|
|
106
|
+
<div class="flex items-center justify-between gap-4 py-2">
|
|
107
|
+
<div class="space-y-1">
|
|
108
|
+
<Label for="sms-security">{t('notifications.criticalSecurity')}</Label>
|
|
109
|
+
<p class="text-xs text-muted-foreground">{t('notifications.criticalSecurityDesc')}</p>
|
|
110
|
+
</div>
|
|
111
|
+
<Switch id="sms-security" bind:checked={smsAlerts.security} />
|
|
112
|
+
</div>
|
|
113
|
+
</Card.CardContent>
|
|
114
|
+
</Card.Card>
|
|
115
|
+
</div>
|
|
@@ -19,19 +19,21 @@
|
|
|
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
|
+
<Breadcrumbs />
|
|
26
|
+
</div>
|
|
25
27
|
{/if}
|
|
26
|
-
<div class="flex flex-col gap-
|
|
27
|
-
<div>
|
|
28
|
-
<h1 class="text-
|
|
28
|
+
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
|
|
29
|
+
<div class="min-w-0">
|
|
30
|
+
<h1 class="truncate text-2xl font-semibold tracking-tight text-foreground">{title}</h1>
|
|
29
31
|
{#if description}
|
|
30
|
-
<p class="text-sm text-muted-foreground">{description}</p>
|
|
32
|
+
<p class="mt-1 max-w-3xl text-sm text-muted-foreground">{description}</p>
|
|
31
33
|
{/if}
|
|
32
34
|
</div>
|
|
33
35
|
{#if actions}
|
|
34
|
-
<div class="flex items-center gap-2">
|
|
36
|
+
<div class="flex w-full flex-wrap items-center gap-2 lg:w-auto lg:justify-end">
|
|
35
37
|
{@render actions()}
|
|
36
38
|
</div>
|
|
37
39
|
{/if}
|