@svadmin/ui 0.44.0 → 0.45.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/components/ForgotPasswordPage.svelte +4 -8
- package/dist/components/LoginPage.svelte +1 -1
- package/dist/components/ProfilePage.svelte +4 -23
- package/dist/components/SecuritySettings.svelte +4 -10
- package/dist/components/Toast.svelte +7 -5
- package/dist/components/content/FeedbackNotice.svelte +61 -0
- package/dist/components/content/FeedbackNotice.svelte.d.ts +13 -0
- package/dist/components/content/index.d.ts +2 -0
- package/dist/components/content/index.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { Label } from './ui/label/index.js';
|
|
8
8
|
import * as Alert from './ui/alert/index.js';
|
|
9
9
|
import SvadminLogo from './SvadminLogo.svelte';
|
|
10
|
-
import { User, ArrowLeft,
|
|
10
|
+
import { User, ArrowLeft, Loader2, AlertCircle } from '@lucide/svelte';
|
|
11
11
|
|
|
12
12
|
const i18n = useTranslation();
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
}>();
|
|
17
17
|
|
|
18
18
|
const adminContext = captureAdminContext();
|
|
19
|
-
const forgot = useForgotPassword();
|
|
19
|
+
const forgot = useForgotPassword({ successNotification: false });
|
|
20
20
|
|
|
21
21
|
let identifier = $state('');
|
|
22
22
|
let error = $state('');
|
|
@@ -82,14 +82,14 @@
|
|
|
82
82
|
<div class="space-y-1 mb-8">
|
|
83
83
|
<h1 class="text-2xl font-semibold tracking-tight">
|
|
84
84
|
{#if sent}
|
|
85
|
-
{i18n.t('auth.
|
|
85
|
+
{i18n.t('auth.resetLinkSentTitle')}
|
|
86
86
|
{:else}
|
|
87
87
|
{i18n.t('auth.forgotPassword')}
|
|
88
88
|
{/if}
|
|
89
89
|
</h1>
|
|
90
90
|
<p class="text-sm text-muted-foreground">
|
|
91
91
|
{#if sent}
|
|
92
|
-
{i18n.t('auth.
|
|
92
|
+
{i18n.t('auth.resetLinkSentDescription')}
|
|
93
93
|
{:else}
|
|
94
94
|
{i18n.t('auth.forgotPasswordDescription')}
|
|
95
95
|
{/if}
|
|
@@ -98,10 +98,6 @@
|
|
|
98
98
|
|
|
99
99
|
{#if sent}
|
|
100
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">{i18n.t('auth.resetLinkSent')}</p>
|
|
104
|
-
</div>
|
|
105
101
|
<Button variant="outline" class="w-full" onclick={() => adminContext.navigate('/login')}>
|
|
106
102
|
<ArrowLeft class="h-4 w-4 mr-2" />
|
|
107
103
|
{i18n.t('auth.backToLogin')}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
}>();
|
|
36
36
|
|
|
37
37
|
const adminContext = captureAdminContext();
|
|
38
|
-
const login = useLogin({ errorMessage: false });
|
|
38
|
+
const login = useLogin({ successNotification: false, errorMessage: false });
|
|
39
39
|
const authProvider = $derived(adminContext.authProvider);
|
|
40
40
|
|
|
41
41
|
function getInitialIdentifier() {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
captureAdminContext,
|
|
4
4
|
useGetIdentity,
|
|
5
|
+
useNotification,
|
|
5
6
|
useUpdatePassword,
|
|
6
7
|
type AuthProvider,
|
|
7
8
|
} from '@svadmin/core';
|
|
@@ -12,12 +13,13 @@
|
|
|
12
13
|
import * as Card from './ui/card/index.js';
|
|
13
14
|
import * as Alert from './ui/alert/index.js';
|
|
14
15
|
import PasswordInput from './PasswordInput.svelte';
|
|
15
|
-
import { User, Mail, Lock, Loader2, AlertCircle,
|
|
16
|
+
import { User, Mail, Lock, Loader2, AlertCircle, Camera } from '@lucide/svelte';
|
|
16
17
|
|
|
17
18
|
const i18n = useTranslation();
|
|
18
19
|
const adminContext = captureAdminContext();
|
|
19
20
|
|
|
20
21
|
const identity = useGetIdentity();
|
|
22
|
+
const notification = useNotification();
|
|
21
23
|
const updatePw = useUpdatePassword();
|
|
22
24
|
|
|
23
25
|
const authProvider = $derived(adminContext.authProvider);
|
|
@@ -27,7 +29,6 @@
|
|
|
27
29
|
let editingProfile = $state(false);
|
|
28
30
|
let editName = $state('');
|
|
29
31
|
let profileSaving = $state(false);
|
|
30
|
-
let profileSuccess = $state(false);
|
|
31
32
|
let profileError = $state('');
|
|
32
33
|
let avatarUploading = $state(false);
|
|
33
34
|
let fileInput = $state<HTMLInputElement>();
|
|
@@ -35,21 +36,18 @@
|
|
|
35
36
|
let newPassword = $state('');
|
|
36
37
|
let confirmPassword = $state('');
|
|
37
38
|
let pwError = $state('');
|
|
38
|
-
let pwSuccess = $state(false);
|
|
39
39
|
let scopeEpoch = 0;
|
|
40
40
|
|
|
41
41
|
function resetProfileScope(): void {
|
|
42
42
|
editingProfile = false;
|
|
43
43
|
editName = '';
|
|
44
44
|
profileSaving = false;
|
|
45
|
-
profileSuccess = false;
|
|
46
45
|
profileError = '';
|
|
47
46
|
avatarUploading = false;
|
|
48
47
|
currentPassword = '';
|
|
49
48
|
newPassword = '';
|
|
50
49
|
confirmPassword = '';
|
|
51
50
|
pwError = '';
|
|
52
|
-
pwSuccess = false;
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
function isActiveScope(
|
|
@@ -75,7 +73,6 @@
|
|
|
75
73
|
function startEditProfile() {
|
|
76
74
|
editName = identity.data?.name ?? '';
|
|
77
75
|
editingProfile = true;
|
|
78
|
-
profileSuccess = false;
|
|
79
76
|
profileError = '';
|
|
80
77
|
}
|
|
81
78
|
|
|
@@ -90,9 +87,9 @@
|
|
|
90
87
|
const result = await provider.updateProfile({ name: editName });
|
|
91
88
|
if (!isActiveScope(epoch, provider, scopedTenantIdentity)) return;
|
|
92
89
|
if (result.success) {
|
|
93
|
-
profileSuccess = true;
|
|
94
90
|
editingProfile = false;
|
|
95
91
|
identity.refetch();
|
|
92
|
+
notification.success(i18n.t('common.updateSuccess'), 3000);
|
|
96
93
|
} else {
|
|
97
94
|
profileError = result.error?.message ?? i18n.t('common.operationFailed');
|
|
98
95
|
}
|
|
@@ -123,7 +120,6 @@
|
|
|
123
120
|
async function handlePasswordChange(e: SubmitEvent) {
|
|
124
121
|
e.preventDefault();
|
|
125
122
|
pwError = '';
|
|
126
|
-
pwSuccess = false;
|
|
127
123
|
|
|
128
124
|
if (!newPassword) { pwError = i18n.t('auth.passwordRequired'); return; }
|
|
129
125
|
if (newPassword !== confirmPassword) { pwError = i18n.t('auth.passwordMismatch'); return; }
|
|
@@ -138,7 +134,6 @@
|
|
|
138
134
|
});
|
|
139
135
|
if (!isActiveScope(epoch, provider, scopedTenantIdentity)) return;
|
|
140
136
|
if (result.success) {
|
|
141
|
-
pwSuccess = true;
|
|
142
137
|
currentPassword = '';
|
|
143
138
|
newPassword = '';
|
|
144
139
|
confirmPassword = '';
|
|
@@ -174,13 +169,6 @@
|
|
|
174
169
|
{i18n.t('common.loading')}
|
|
175
170
|
</div>
|
|
176
171
|
{:else if identity.data}
|
|
177
|
-
{#if profileSuccess}
|
|
178
|
-
<Alert.Root class="border-success/30 bg-success/5 text-success mb-4">
|
|
179
|
-
<CheckCircle class="h-4 w-4" />
|
|
180
|
-
<Alert.Description>{i18n.t('common.updateSuccess')}</Alert.Description>
|
|
181
|
-
</Alert.Root>
|
|
182
|
-
{/if}
|
|
183
|
-
|
|
184
172
|
<div class="flex items-start gap-6">
|
|
185
173
|
<!-- Avatar -->
|
|
186
174
|
<div class="shrink-0 relative group">
|
|
@@ -292,13 +280,6 @@
|
|
|
292
280
|
</Alert.Root>
|
|
293
281
|
{/if}
|
|
294
282
|
|
|
295
|
-
{#if pwSuccess}
|
|
296
|
-
<Alert.Root class="border-success/30 bg-success/5 text-success">
|
|
297
|
-
<CheckCircle class="h-4 w-4" />
|
|
298
|
-
<Alert.Description>{i18n.t('profile.passwordChanged')}</Alert.Description>
|
|
299
|
-
</Alert.Root>
|
|
300
|
-
{/if}
|
|
301
|
-
|
|
302
283
|
<PasswordInput
|
|
303
284
|
id="current-password"
|
|
304
285
|
label={i18n.t('profile.currentPassword')}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { useNotification } from '@svadmin/core';
|
|
2
3
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
3
4
|
|
|
4
|
-
import { AlertTriangle,
|
|
5
|
+
import { AlertTriangle, KeyRound, Monitor, Shield, ShieldAlert, ShieldCheck, Smartphone, Trash2 } from '@lucide/svelte';
|
|
5
6
|
import type { Component } from 'svelte';
|
|
6
7
|
import * as Alert from './ui/alert/index.js';
|
|
7
8
|
import * as Card from './ui/card/index.js';
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
import PasswordInput from './PasswordInput.svelte';
|
|
12
13
|
|
|
13
14
|
const i18n = useTranslation();
|
|
15
|
+
const notification = useNotification();
|
|
14
16
|
|
|
15
17
|
interface SessionInfo {
|
|
16
18
|
id: string;
|
|
@@ -32,13 +34,11 @@
|
|
|
32
34
|
let currentPassword = $state('');
|
|
33
35
|
let newPassword = $state('');
|
|
34
36
|
let confirmPassword = $state('');
|
|
35
|
-
let passwordSuccessMessage = $state('');
|
|
36
37
|
let passwordErrorMessage = $state('');
|
|
37
38
|
let lastChanged = $state('3 months ago');
|
|
38
39
|
|
|
39
40
|
function handlePasswordChange(event: SubmitEvent) {
|
|
40
41
|
event.preventDefault();
|
|
41
|
-
passwordSuccessMessage = '';
|
|
42
42
|
passwordErrorMessage = '';
|
|
43
43
|
|
|
44
44
|
if (!currentPassword) {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
notification.success(i18n.t('profile.passwordChanged'), 3000);
|
|
58
58
|
lastChanged = 'Just now';
|
|
59
59
|
currentPassword = '';
|
|
60
60
|
newPassword = '';
|
|
@@ -111,12 +111,6 @@
|
|
|
111
111
|
</Card.CardHeader>
|
|
112
112
|
<Card.CardContent>
|
|
113
113
|
<form onsubmit={handlePasswordChange} class="max-w-sm space-y-4">
|
|
114
|
-
{#if passwordSuccessMessage}
|
|
115
|
-
<Alert.Root class="border-success/30 bg-success/5 text-success">
|
|
116
|
-
<CheckCircle2 class="h-4 w-4" />
|
|
117
|
-
<Alert.Description>{passwordSuccessMessage}</Alert.Description>
|
|
118
|
-
</Alert.Root>
|
|
119
|
-
{/if}
|
|
120
114
|
{#if passwordErrorMessage}
|
|
121
115
|
<Alert.Root variant="destructive">
|
|
122
116
|
<AlertTriangle class="h-4 w-4" />
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
const queue = getToastQueue();
|
|
21
21
|
if (queue.length > 0) {
|
|
22
22
|
for (const t of queue) {
|
|
23
|
+
const options = { duration: t.duration, id: t.key ?? t.id };
|
|
23
24
|
switch (t.type) {
|
|
24
|
-
case 'success': sonner.success(t.message,
|
|
25
|
-
case 'error': sonner.error(t.message,
|
|
26
|
-
case 'warning': sonner.warning(t.message,
|
|
27
|
-
case 'info': sonner.info(t.message,
|
|
25
|
+
case 'success': sonner.success(t.message, options); break;
|
|
26
|
+
case 'error': sonner.error(t.message, options); break;
|
|
27
|
+
case 'warning': sonner.warning(t.message, options); break;
|
|
28
|
+
case 'info': sonner.info(t.message, options); break;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
consumeToastQueue();
|
|
@@ -46,7 +47,8 @@
|
|
|
46
47
|
position: "top-right",
|
|
47
48
|
richColors: true,
|
|
48
49
|
closeButton: true,
|
|
49
|
-
expand:
|
|
50
|
+
expand: false,
|
|
51
|
+
visibleToasts: 3,
|
|
50
52
|
theme,
|
|
51
53
|
toastOptions: {
|
|
52
54
|
classes: {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { CircleAlert, Info, TriangleAlert } from '@lucide/svelte';
|
|
4
|
+
import { cn } from '../../utils.js';
|
|
5
|
+
|
|
6
|
+
export type FeedbackNoticeTone = 'info' | 'warning' | 'danger';
|
|
7
|
+
export type FeedbackNoticePriority = 'contextual' | 'blocking';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
message: string;
|
|
11
|
+
tone?: FeedbackNoticeTone;
|
|
12
|
+
priority?: FeedbackNoticePriority;
|
|
13
|
+
action?: Snippet;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
message,
|
|
19
|
+
tone = 'info',
|
|
20
|
+
priority = 'contextual',
|
|
21
|
+
action,
|
|
22
|
+
class: className = '',
|
|
23
|
+
}: Props = $props();
|
|
24
|
+
|
|
25
|
+
const isBlocking = $derived(priority === 'blocking' || tone === 'danger');
|
|
26
|
+
const toneClass = $derived(
|
|
27
|
+
tone === 'danger'
|
|
28
|
+
? 'border-destructive/40 bg-destructive/5 text-destructive'
|
|
29
|
+
: tone === 'warning'
|
|
30
|
+
? 'border-warning/50 bg-warning/10 text-warning-foreground'
|
|
31
|
+
: 'border-border bg-muted/40 text-foreground',
|
|
32
|
+
);
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<div
|
|
36
|
+
data-svadmin-feedback-notice
|
|
37
|
+
data-tone={tone}
|
|
38
|
+
data-priority={priority}
|
|
39
|
+
role={isBlocking ? 'alert' : 'status'}
|
|
40
|
+
aria-live={isBlocking ? 'assertive' : 'polite'}
|
|
41
|
+
class={cn(
|
|
42
|
+
'flex w-full flex-col gap-3 rounded-md border px-3 py-2.5 text-sm sm:flex-row sm:items-center',
|
|
43
|
+
isBlocking && 'border-l-2',
|
|
44
|
+
toneClass,
|
|
45
|
+
className,
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<div class="flex min-w-0 flex-1 items-start gap-2.5">
|
|
49
|
+
{#if tone === 'danger'}
|
|
50
|
+
<CircleAlert class="mt-0.5 size-4 shrink-0" />
|
|
51
|
+
{:else if tone === 'warning'}
|
|
52
|
+
<TriangleAlert class="mt-0.5 size-4 shrink-0" />
|
|
53
|
+
{:else}
|
|
54
|
+
<Info class="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
|
55
|
+
{/if}
|
|
56
|
+
<p class="min-w-0 leading-5">{message}</p>
|
|
57
|
+
</div>
|
|
58
|
+
{#if action}
|
|
59
|
+
<div class="shrink-0">{@render action()}</div>
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type FeedbackNoticeTone = 'info' | 'warning' | 'danger';
|
|
3
|
+
export type FeedbackNoticePriority = 'contextual' | 'blocking';
|
|
4
|
+
interface Props {
|
|
5
|
+
message: string;
|
|
6
|
+
tone?: FeedbackNoticeTone;
|
|
7
|
+
priority?: FeedbackNoticePriority;
|
|
8
|
+
action?: Snippet;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const FeedbackNotice: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type FeedbackNotice = ReturnType<typeof FeedbackNotice>;
|
|
13
|
+
export default FeedbackNotice;
|
|
@@ -30,3 +30,5 @@ export type { NetworkColumn } from './NetworkTable.types.js';
|
|
|
30
30
|
export { default as OtpInput } from './OtpInput.svelte';
|
|
31
31
|
export { default as TwoFactorStepper } from './TwoFactorStepper.svelte';
|
|
32
32
|
export { default as SystemErrorState } from './SystemErrorState.svelte';
|
|
33
|
+
export { default as FeedbackNotice } from './FeedbackNotice.svelte';
|
|
34
|
+
export type { FeedbackNoticeTone, FeedbackNoticePriority } from './FeedbackNotice.svelte';
|
|
@@ -20,3 +20,4 @@ export { default as NetworkTable } from './NetworkTable.svelte';
|
|
|
20
20
|
export { default as OtpInput } from './OtpInput.svelte';
|
|
21
21
|
export { default as TwoFactorStepper } from './TwoFactorStepper.svelte';
|
|
22
22
|
export { default as SystemErrorState } from './SystemErrorState.svelte';
|
|
23
|
+
export { default as FeedbackNotice } from './FeedbackNotice.svelte';
|
package/dist/index.d.ts
CHANGED
|
@@ -63,6 +63,8 @@ export { default as InlineEdit } from "./components/InlineEdit.svelte";
|
|
|
63
63
|
export { default as VirtualTable } from "./components/VirtualTable.svelte";
|
|
64
64
|
export { default as DraggableHeader } from "./components/DraggableHeader.svelte";
|
|
65
65
|
export { default as SettingsPage } from "./components/SettingsPage.svelte";
|
|
66
|
+
export { default as FeedbackNotice } from './components/content/FeedbackNotice.svelte';
|
|
67
|
+
export type { FeedbackNoticeTone, FeedbackNoticePriority, } from './components/content/FeedbackNotice.svelte';
|
|
66
68
|
export { setRichTextEditor, getRichTextEditor } from "./editor-config.svelte.js";
|
|
67
69
|
export { default as AppearanceSettings } from "./components/AppearanceSettings.svelte";
|
|
68
70
|
export { default as AboutSettings } from "./components/AboutSettings.svelte";
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,7 @@ export { default as InlineEdit } from "./components/InlineEdit.svelte";
|
|
|
66
66
|
export { default as VirtualTable } from "./components/VirtualTable.svelte";
|
|
67
67
|
export { default as DraggableHeader } from "./components/DraggableHeader.svelte";
|
|
68
68
|
export { default as SettingsPage } from "./components/SettingsPage.svelte";
|
|
69
|
+
export { default as FeedbackNotice } from './components/content/FeedbackNotice.svelte';
|
|
69
70
|
// Editor Registry
|
|
70
71
|
export { setRichTextEditor, getRichTextEditor } from "./editor-config.svelte.js";
|
|
71
72
|
// Settings & Preferences
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"./components/*": "./dist/components/*"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@svadmin/core": "^0.
|
|
42
|
+
"@svadmin/core": "^0.38.0",
|
|
43
43
|
"@tanstack/svelte-query": "^6.1.38",
|
|
44
44
|
"highlight.js": "^11.11.1",
|
|
45
45
|
"isomorphic-dompurify": "^3.22.0",
|