@svadmin/ui 0.38.2 → 0.38.5
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 +8 -4
- package/dist/app.theme.css +8 -4
- package/dist/components/AICommandBar.svelte +16 -15
- package/dist/components/AboutSettings.svelte +12 -11
- package/dist/components/AdminApp.svelte +144 -70
- package/dist/components/AdminApp.svelte.d.ts +6 -3
- package/dist/components/ApiSettings.svelte +19 -16
- package/dist/components/AppearanceSettings.svelte +18 -16
- package/dist/components/AuditLogDrawer.svelte +6 -3
- package/dist/components/AuditLogViewer.svelte +18 -15
- package/dist/components/Authenticated.svelte +4 -4
- package/dist/components/AutoForm.svelte +19 -16
- package/dist/components/AutoSaveIndicator.svelte +7 -5
- package/dist/components/AutoTable.svelte +85 -71
- package/dist/components/AutoTable.svelte.d.ts +1 -1
- package/dist/components/Breadcrumbs.svelte +17 -13
- package/dist/components/CancelTaskButton.svelte +5 -2
- package/dist/components/CatchAllNavigate.svelte +3 -3
- package/dist/components/ChatDialog.svelte +22 -19
- package/dist/components/ComboboxField.svelte +6 -4
- package/dist/components/CommandPalette.svelte +20 -20
- package/dist/components/ConfigErrorScreen.svelte +12 -10
- package/dist/components/ConfirmDialog.svelte +6 -4
- package/dist/components/CreatePage.svelte +4 -2
- package/dist/components/DevTools.svelte +11 -9
- package/dist/components/DrawerForm.svelte +5 -2
- package/dist/components/EditPage.svelte +8 -5
- package/dist/components/EmptyState.svelte +5 -3
- package/dist/components/ErrorBoundary.svelte +5 -3
- package/dist/components/ErrorComponent.svelte +6 -4
- package/dist/components/ErrorPage.svelte +12 -10
- package/dist/components/FieldRenderer.svelte +17 -15
- package/dist/components/ForgotPasswordPage.svelte +22 -19
- package/dist/components/Header.svelte +7 -4
- package/dist/components/InfiniteList.svelte +6 -3
- package/dist/components/IntegrationsSettings.svelte +9 -6
- package/dist/components/KeyboardShortcuts.svelte +4 -2
- package/dist/components/Layout.svelte +12 -13
- package/dist/components/LazyPage.svelte +47 -0
- package/dist/components/LazyPage.svelte.d.ts +14 -0
- package/dist/components/ListPage.svelte +8 -5
- package/dist/components/LoginPage.svelte +24 -22
- package/dist/components/MarkdownRenderer.svelte +89 -35
- package/dist/components/ModalForm.svelte +4 -2
- package/dist/components/NavigateToResource.svelte +3 -3
- package/dist/components/NotificationsSettings.svelte +26 -24
- package/dist/components/PasswordInput.svelte +4 -2
- package/dist/components/PermissionMatrix.svelte +15 -12
- package/dist/components/ProfilePage.svelte +23 -21
- package/dist/components/RegisterPage.svelte +21 -19
- package/dist/components/ResourceOperationsPage.svelte +3 -2
- package/dist/components/RetryTaskButton.svelte +5 -2
- package/dist/components/RolesSettings.svelte +10 -8
- package/dist/components/SecuritySettings.svelte +26 -23
- package/dist/components/SettingsPage.svelte +14 -11
- package/dist/components/ShowPage.svelte +9 -7
- package/dist/components/Sidebar.svelte +36 -28
- package/dist/components/SidebarItem.svelte +4 -4
- package/dist/components/SidebarItem.svelte.d.ts +1 -1
- package/dist/components/StepsForm.svelte +11 -8
- package/dist/components/TaskDetails.svelte +14 -11
- package/dist/components/TaskList.svelte +14 -11
- package/dist/components/TaskQueueDrawer.svelte +34 -32
- package/dist/components/TaskStatusBadge.svelte +5 -2
- package/dist/components/Toast.svelte +18 -4
- package/dist/components/TooltipButton.svelte +25 -3
- package/dist/components/TwoFactorAuthPage.svelte +26 -24
- package/dist/components/UndoableNotification.svelte +5 -2
- package/dist/components/UpdatePasswordPage.svelte +18 -15
- package/dist/components/VirtualTable.svelte +0 -4
- package/dist/components/account/CompanyProfilePage.svelte +13 -11
- package/dist/components/account/GetStartedPage.svelte +15 -13
- package/dist/components/account/ImportMembersPage.svelte +14 -12
- package/dist/components/account/MembersStarterPage.svelte +10 -8
- package/dist/components/account/SecurityLogPage.svelte +24 -22
- package/dist/components/account/SettingsEnterprisePage.svelte +6 -4
- package/dist/components/account/SettingsPlainPage.svelte +14 -12
- package/dist/components/account/SettingsSidebarPage.svelte +11 -9
- package/dist/components/account/TeamMembersPage.svelte +10 -8
- package/dist/components/buttons/CloneButton.svelte +4 -2
- package/dist/components/buttons/CreateButton.svelte +4 -2
- package/dist/components/buttons/DeleteButton.svelte +6 -4
- package/dist/components/buttons/EditButton.svelte +4 -2
- package/dist/components/buttons/ExportButton.svelte +4 -2
- package/dist/components/buttons/ImportButton.svelte +4 -2
- package/dist/components/buttons/RefreshButton.svelte +4 -2
- package/dist/components/buttons/SaveButton.svelte +4 -2
- package/dist/components/buttons/ShowButton.svelte +4 -2
- package/dist/components/fieldComponentMap.js +0 -3
- package/dist/components/fields/ArrayField.svelte +6 -4
- package/dist/components/fields/JsonField.svelte +5 -3
- package/dist/components/fields/RelationField.svelte +3 -3
- package/dist/components/fields/RichTextField.svelte +4 -2
- package/dist/components/network/TeamCrewTablePage.svelte +18 -16
- package/dist/components/network/UserCardsNFTPage.svelte +11 -9
- package/dist/components/profile/ActivityTimeline.svelte +10 -8
- package/dist/components/profile/ProfileCard.svelte +13 -11
- package/dist/components/profile/ProjectsGrid.svelte +7 -5
- package/dist/components/profile/PublicProfilePage.svelte +18 -16
- package/dist/components/profile/TeamsShowcase.svelte +7 -5
- package/dist/components/toast-host.svelte.d.ts +5 -0
- package/dist/components/toast-host.svelte.js +21 -0
- package/dist/components/ui/command/command-dialog.svelte +29 -13
- package/dist/components/ui/command/command-dialog.svelte.d.ts +4 -3
- package/dist/components/ui/command/command-empty.svelte +18 -0
- package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-group.svelte +41 -0
- package/dist/components/ui/command/command-group.svelte.d.ts +8 -0
- package/dist/components/ui/command/command-input.svelte +23 -0
- package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-item.svelte +43 -0
- package/dist/components/ui/command/command-item.svelte.d.ts +11 -0
- package/dist/components/ui/command/command-list.svelte +18 -0
- package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-loading.svelte +12 -0
- package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-separator.svelte +23 -0
- package/dist/components/ui/command/command-separator.svelte.d.ts +7 -0
- package/dist/components/ui/command/command.svelte +26 -0
- package/dist/components/ui/command/command.svelte.d.ts +4 -0
- package/dist/components/ui/command/index.d.ts +55 -2
- package/dist/components/ui/command/index.js +21 -2
- package/dist/components/ui/sheet/sheet-content.svelte +1 -4
- package/dist/components/ui/sheet/sheet-content.svelte.d.ts +1 -3
- package/dist/components/ui/tabs/tabs-content.svelte +6 -2
- package/dist/components/ui/tabs/tabs-context.svelte.d.ts +5 -0
- package/dist/components/ui/tabs/tabs-context.svelte.js +2 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte +49 -4
- package/dist/components/ui/tabs/tabs.svelte +12 -1
- package/dist/components/ui/tooltip/index.d.ts +4 -1
- package/dist/components/ui/tooltip/index.js +3 -1
- package/dist/components/ui/tooltip/tooltip-content.svelte +308 -35
- package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +42 -3
- package/dist/components/ui/tooltip/tooltip-context.svelte.d.ts +37 -0
- package/dist/components/ui/tooltip/tooltip-context.svelte.js +220 -0
- package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.d.ts +11 -0
- package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.js +93 -0
- package/dist/components/ui/tooltip/tooltip-trigger.svelte +56 -3
- package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +23 -2
- package/dist/components/ui/tooltip/tooltip.svelte +63 -3
- package/dist/components/ui/tooltip/tooltip.svelte.d.ts +23 -2
- package/dist/router-state.svelte.d.ts +13 -0
- package/dist/router-state.svelte.js +72 -37
- package/package.json +13 -11
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
3
|
+
|
|
3
4
|
import { AlertCircle, Calendar, Check, Copy, Key, Plus, ShieldCheck, Trash2 } from '@lucide/svelte';
|
|
4
5
|
import * as Card from './ui/card/index.js';
|
|
5
6
|
import { Badge } from './ui/badge/index.js';
|
|
@@ -8,6 +9,8 @@
|
|
|
8
9
|
import { Input } from './ui/input/index.js';
|
|
9
10
|
import { Label } from './ui/label/index.js';
|
|
10
11
|
|
|
12
|
+
const i18n = useTranslation();
|
|
13
|
+
|
|
11
14
|
interface ApiKey {
|
|
12
15
|
id: string;
|
|
13
16
|
name: string;
|
|
@@ -76,8 +79,8 @@
|
|
|
76
79
|
|
|
77
80
|
<div class="space-y-6">
|
|
78
81
|
<div>
|
|
79
|
-
<h2 class="text-xl font-semibold text-foreground">{t('settings.api')}</h2>
|
|
80
|
-
<p class="mt-1 text-sm text-muted-foreground">{t('settings.apiDescription')}</p>
|
|
82
|
+
<h2 class="text-xl font-semibold text-foreground">{i18n.t('settings.api')}</h2>
|
|
83
|
+
<p class="mt-1 text-sm text-muted-foreground">{i18n.t('settings.apiDescription')}</p>
|
|
81
84
|
</div>
|
|
82
85
|
|
|
83
86
|
<div class="grid gap-6 lg:grid-cols-3">
|
|
@@ -85,19 +88,19 @@
|
|
|
85
88
|
<Card.CardHeader>
|
|
86
89
|
<Card.CardTitle class="flex items-center gap-2 text-base">
|
|
87
90
|
<Plus class="h-4 w-4" />
|
|
88
|
-
{t('api.generateTitle')}
|
|
91
|
+
{i18n.t('api.generateTitle')}
|
|
89
92
|
</Card.CardTitle>
|
|
90
|
-
<Card.CardDescription>{t('api.generateDesc')}</Card.CardDescription>
|
|
93
|
+
<Card.CardDescription>{i18n.t('api.generateDesc')}</Card.CardDescription>
|
|
91
94
|
</Card.CardHeader>
|
|
92
95
|
<Card.CardContent>
|
|
93
96
|
<form onsubmit={handleGenerateKey} class="space-y-4">
|
|
94
97
|
<div class="space-y-2">
|
|
95
|
-
<Label for="api-key-name">{t('api.keyName')}</Label>
|
|
98
|
+
<Label for="api-key-name">{i18n.t('api.keyName')}</Label>
|
|
96
99
|
<Input id="api-key-name" placeholder="CI deployment token" bind:value={newKeyName} required />
|
|
97
100
|
</div>
|
|
98
101
|
|
|
99
102
|
<div class="space-y-3">
|
|
100
|
-
<Label>{t('api.permissions')}</Label>
|
|
103
|
+
<Label>{i18n.t('api.permissions')}</Label>
|
|
101
104
|
{#each Object.keys(newKeyPermissions) as permission (permission)}
|
|
102
105
|
<div class="flex items-center gap-2">
|
|
103
106
|
<Checkbox id={`perm-${permission}`} bind:checked={newKeyPermissions[permission as keyof typeof newKeyPermissions]} />
|
|
@@ -106,7 +109,7 @@
|
|
|
106
109
|
{/each}
|
|
107
110
|
</div>
|
|
108
111
|
|
|
109
|
-
<Button type="submit" class="w-full">{t('api.generateButton')}</Button>
|
|
112
|
+
<Button type="submit" class="w-full">{i18n.t('api.generateButton')}</Button>
|
|
110
113
|
</form>
|
|
111
114
|
</Card.CardContent>
|
|
112
115
|
</Card.Card>
|
|
@@ -115,9 +118,9 @@
|
|
|
115
118
|
<Card.CardHeader>
|
|
116
119
|
<Card.CardTitle class="flex items-center gap-2 text-base">
|
|
117
120
|
<Key class="h-4 w-4 text-muted-foreground" />
|
|
118
|
-
{t('api.activeKeysTitle')}
|
|
121
|
+
{i18n.t('api.activeKeysTitle')}
|
|
119
122
|
</Card.CardTitle>
|
|
120
|
-
<Card.CardDescription>{t('api.activeKeysDesc')}</Card.CardDescription>
|
|
123
|
+
<Card.CardDescription>{i18n.t('api.activeKeysDesc')}</Card.CardDescription>
|
|
121
124
|
</Card.CardHeader>
|
|
122
125
|
<Card.CardContent>
|
|
123
126
|
{#if showCreatedToken}
|
|
@@ -125,8 +128,8 @@
|
|
|
125
128
|
<div class="flex items-start gap-2 text-sm text-green-700 dark:text-green-300">
|
|
126
129
|
<ShieldCheck class="mt-0.5 h-5 w-5 shrink-0" />
|
|
127
130
|
<div>
|
|
128
|
-
<h4 class="font-semibold">{t('api.successTitle')}</h4>
|
|
129
|
-
<p class="mt-0.5 text-xs">{t('api.successHint')}</p>
|
|
131
|
+
<h4 class="font-semibold">{i18n.t('api.successTitle')}</h4>
|
|
132
|
+
<p class="mt-0.5 text-xs">{i18n.t('api.successHint')}</p>
|
|
130
133
|
</div>
|
|
131
134
|
</div>
|
|
132
135
|
<div class="flex items-center gap-2">
|
|
@@ -140,7 +143,7 @@
|
|
|
140
143
|
</Button>
|
|
141
144
|
</div>
|
|
142
145
|
<div class="flex justify-end">
|
|
143
|
-
<Button variant="ghost" size="sm" onclick={() => { showCreatedToken = false; generatedToken = ''; }}>{t('common.close')}</Button>
|
|
146
|
+
<Button variant="ghost" size="sm" onclick={() => { showCreatedToken = false; generatedToken = ''; }}>{i18n.t('common.close')}</Button>
|
|
144
147
|
</div>
|
|
145
148
|
</div>
|
|
146
149
|
{/if}
|
|
@@ -160,14 +163,14 @@
|
|
|
160
163
|
</div>
|
|
161
164
|
</div>
|
|
162
165
|
<div class="flex shrink-0 items-center gap-1">
|
|
163
|
-
<Button variant="ghost" size="icon-sm" onclick={() => copyToken(key.maskedToken, key.id)} title={t('common.copy')}>
|
|
166
|
+
<Button variant="ghost" size="icon-sm" onclick={() => copyToken(key.maskedToken, key.id)} title={i18n.t('common.copy')}>
|
|
164
167
|
{#if copiedKeyId === key.id}
|
|
165
168
|
<Check class="h-4 w-4 text-green-500" />
|
|
166
169
|
{:else}
|
|
167
170
|
<Copy class="h-4 w-4" />
|
|
168
171
|
{/if}
|
|
169
172
|
</Button>
|
|
170
|
-
<Button variant="ghost" size="icon-sm" onclick={() => deleteKey(key.id)} title={t('common.delete')}>
|
|
173
|
+
<Button variant="ghost" size="icon-sm" onclick={() => deleteKey(key.id)} title={i18n.t('common.delete')}>
|
|
171
174
|
<Trash2 class="h-4 w-4" />
|
|
172
175
|
</Button>
|
|
173
176
|
</div>
|
|
@@ -181,7 +184,7 @@
|
|
|
181
184
|
{:else}
|
|
182
185
|
<div class="flex flex-col items-center gap-2 py-8 text-center text-sm text-muted-foreground">
|
|
183
186
|
<AlertCircle class="h-8 w-8 opacity-60" />
|
|
184
|
-
<span>{t('api.noKeys')}</span>
|
|
187
|
+
<span>{i18n.t('api.noKeys')}</span>
|
|
185
188
|
</div>
|
|
186
189
|
{/each}
|
|
187
190
|
</div>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
3
|
+
|
|
3
4
|
import { getTheme, setTheme, getColorTheme, setColorTheme, getColorPresets } from '@svadmin/core';
|
|
4
|
-
import { getLocale, setLocale, getAvailableLocales } from '@svadmin/core/i18n';
|
|
5
5
|
import * as Card from './ui/card/index.js';
|
|
6
6
|
import { Sun, Moon, Monitor, Check } from '@lucide/svelte';
|
|
7
7
|
|
|
8
|
+
const i18n = useTranslation();
|
|
9
|
+
|
|
8
10
|
// Theme mode
|
|
9
11
|
const themeOptions = [
|
|
10
12
|
{ value: 'light' as const, icon: Sun, label: 'settings.light' },
|
|
@@ -14,8 +16,8 @@
|
|
|
14
16
|
|
|
15
17
|
let currentTheme = $derived(getTheme());
|
|
16
18
|
let currentColor = $derived(getColorTheme());
|
|
17
|
-
let currentLocale = $derived(
|
|
18
|
-
const availableLocales = getAvailableLocales();
|
|
19
|
+
let currentLocale = $derived(i18n.locale);
|
|
20
|
+
const availableLocales = i18n.getAvailableLocales();
|
|
19
21
|
const presets = getColorPresets();
|
|
20
22
|
|
|
21
23
|
// Sidebar density
|
|
@@ -55,14 +57,14 @@
|
|
|
55
57
|
|
|
56
58
|
<div class="space-y-6">
|
|
57
59
|
<div>
|
|
58
|
-
<h2 class="text-xl font-semibold text-foreground">{t('settings.appearance')}</h2>
|
|
59
|
-
<p class="text-sm text-muted-foreground mt-1">{t('settings.settingsDescription')}</p>
|
|
60
|
+
<h2 class="text-xl font-semibold text-foreground">{i18n.t('settings.appearance')}</h2>
|
|
61
|
+
<p class="text-sm text-muted-foreground mt-1">{i18n.t('settings.settingsDescription')}</p>
|
|
60
62
|
</div>
|
|
61
63
|
|
|
62
64
|
<!-- Theme Mode -->
|
|
63
65
|
<Card.Card>
|
|
64
66
|
<Card.CardHeader class="pb-3">
|
|
65
|
-
<Card.CardTitle class="text-base">{t('settings.themeMode')}</Card.CardTitle>
|
|
67
|
+
<Card.CardTitle class="text-base">{i18n.t('settings.themeMode')}</Card.CardTitle>
|
|
66
68
|
</Card.CardHeader>
|
|
67
69
|
<Card.CardContent>
|
|
68
70
|
<div class="grid grid-cols-3 gap-3">
|
|
@@ -85,7 +87,7 @@
|
|
|
85
87
|
<opt.icon class="h-5 w-5" />
|
|
86
88
|
</div>
|
|
87
89
|
<span class="text-xs font-medium {active ? 'text-primary' : 'text-muted-foreground'}">
|
|
88
|
-
{t(opt.label)}
|
|
90
|
+
{i18n.t(opt.label)}
|
|
89
91
|
</span>
|
|
90
92
|
</button>
|
|
91
93
|
{/each}
|
|
@@ -96,7 +98,7 @@
|
|
|
96
98
|
<!-- Color Accent -->
|
|
97
99
|
<Card.Card>
|
|
98
100
|
<Card.CardHeader class="pb-3">
|
|
99
|
-
<Card.CardTitle class="text-base">{t('settings.colorAccent')}</Card.CardTitle>
|
|
101
|
+
<Card.CardTitle class="text-base">{i18n.t('settings.colorAccent')}</Card.CardTitle>
|
|
100
102
|
</Card.CardHeader>
|
|
101
103
|
<Card.CardContent>
|
|
102
104
|
<div class="flex flex-wrap gap-3">
|
|
@@ -121,20 +123,20 @@
|
|
|
121
123
|
<!-- Interface -->
|
|
122
124
|
<Card.Card>
|
|
123
125
|
<Card.CardHeader class="pb-3">
|
|
124
|
-
<Card.CardTitle class="text-base">{t('settings.interface')}</Card.CardTitle>
|
|
126
|
+
<Card.CardTitle class="text-base">{i18n.t('settings.interface')}</Card.CardTitle>
|
|
125
127
|
</Card.CardHeader>
|
|
126
128
|
<Card.CardContent class="space-y-5">
|
|
127
129
|
<!-- Language -->
|
|
128
130
|
<div class="flex items-center justify-between">
|
|
129
131
|
<div>
|
|
130
|
-
<p class="text-sm font-medium text-foreground">{t('settings.language')}</p>
|
|
132
|
+
<p class="text-sm font-medium text-foreground">{i18n.t('settings.language')}</p>
|
|
131
133
|
<p class="text-xs text-muted-foreground">{localeNames[currentLocale] ?? currentLocale}</p>
|
|
132
134
|
</div>
|
|
133
135
|
<select
|
|
134
136
|
class="h-9 rounded-md border border-input bg-background px-3 text-sm ring-offset-background
|
|
135
137
|
focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
|
136
138
|
value={currentLocale}
|
|
137
|
-
onchange={(e) => setLocale((e.target as HTMLSelectElement).value)}
|
|
139
|
+
onchange={(e) => i18n.setLocale((e.target as HTMLSelectElement).value)}
|
|
138
140
|
>
|
|
139
141
|
{#each availableLocales as loc, _i (_i)}
|
|
140
142
|
<option value={loc}>{localeNames[loc] ?? loc}</option>
|
|
@@ -147,19 +149,19 @@
|
|
|
147
149
|
<!-- Sidebar Density -->
|
|
148
150
|
<div class="flex items-center justify-between">
|
|
149
151
|
<div>
|
|
150
|
-
<p class="text-sm font-medium text-foreground">{t('settings.sidebarDensity')}</p>
|
|
152
|
+
<p class="text-sm font-medium text-foreground">{i18n.t('settings.sidebarDensity')}</p>
|
|
151
153
|
</div>
|
|
152
154
|
<div class="flex rounded-lg border border-input overflow-hidden">
|
|
153
155
|
<button
|
|
154
156
|
class="px-3 py-1.5 text-xs font-medium transition-colors
|
|
155
157
|
{density === 'compact' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
|
|
156
158
|
onclick={() => setDensity('compact')}
|
|
157
|
-
>{t('settings.compact')}</button>
|
|
159
|
+
>{i18n.t('settings.compact')}</button>
|
|
158
160
|
<button
|
|
159
161
|
class="px-3 py-1.5 text-xs font-medium transition-colors
|
|
160
162
|
{density === 'standard' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
|
|
161
163
|
onclick={() => setDensity('standard')}
|
|
162
|
-
>{t('settings.standard')}</button>
|
|
164
|
+
>{i18n.t('settings.standard')}</button>
|
|
163
165
|
</div>
|
|
164
166
|
</div>
|
|
165
167
|
|
|
@@ -168,7 +170,7 @@
|
|
|
168
170
|
<!-- Default Page Size -->
|
|
169
171
|
<div class="flex items-center justify-between">
|
|
170
172
|
<div>
|
|
171
|
-
<p class="text-sm font-medium text-foreground">{t('settings.defaultPageSize')}</p>
|
|
173
|
+
<p class="text-sm font-medium text-foreground">{i18n.t('settings.defaultPageSize')}</p>
|
|
172
174
|
</div>
|
|
173
175
|
<div class="flex rounded-lg border border-input overflow-hidden">
|
|
174
176
|
{#each pageSizeOptions as size, _i (_i)}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { getAuditLogProvider,
|
|
2
|
+
import { getAuditLogProvider, useTranslation } from '@svadmin/core';
|
|
3
|
+
|
|
3
4
|
import type { AuditEntry } from '@svadmin/core';
|
|
4
5
|
import * as Sheet from './ui/sheet/index.js';
|
|
5
6
|
import { History, Loader2 } from '@lucide/svelte';
|
|
6
7
|
|
|
8
|
+
const i18n = useTranslation();
|
|
9
|
+
|
|
7
10
|
let { open = $bindable(false), resource, recordId } = $props<{
|
|
8
11
|
open: boolean;
|
|
9
12
|
resource: string;
|
|
@@ -47,7 +50,7 @@
|
|
|
47
50
|
<Sheet.Header>
|
|
48
51
|
<Sheet.Title class="flex items-center gap-2">
|
|
49
52
|
<History class="h-5 w-5" />
|
|
50
|
-
{t('common.history') || 'History'}
|
|
53
|
+
{i18n.t('common.history') || 'History'}
|
|
51
54
|
</Sheet.Title>
|
|
52
55
|
<Sheet.Description>
|
|
53
56
|
Audit log and revision history for this record.
|
|
@@ -63,7 +66,7 @@
|
|
|
63
66
|
<div class="text-center py-10">
|
|
64
67
|
<History class="h-8 w-8 mx-auto text-muted-foreground/30 mb-3" />
|
|
65
68
|
<p class="text-sm text-muted-foreground">
|
|
66
|
-
{t('common.noData') || 'No audit logs found'}
|
|
69
|
+
{i18n.t('common.noData') || 'No audit logs found'}
|
|
67
70
|
</p>
|
|
68
71
|
</div>
|
|
69
72
|
{:else}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
3
|
+
|
|
3
4
|
import { getAuthProvider } from '@svadmin/core';
|
|
4
5
|
import type { AuditLog } from '@svadmin/core';
|
|
5
6
|
import { toast } from '@svadmin/core/toast';
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
import { Input } from './ui/input/index.js';
|
|
10
11
|
import { Loader2, ChevronLeft, ChevronRight, Search, FileSearch, Eye, X } from '@lucide/svelte';
|
|
11
12
|
|
|
13
|
+
const i18n = useTranslation();
|
|
14
|
+
|
|
12
15
|
const authProvider = getAuthProvider({ optional: true });
|
|
13
16
|
|
|
14
17
|
let logs = $state<AuditLog[]>([]);
|
|
@@ -27,7 +30,7 @@
|
|
|
27
30
|
|
|
28
31
|
async function loadLogs() {
|
|
29
32
|
if (!authProvider?.getAuditLogs) {
|
|
30
|
-
error = t('settings.auditNotSupported') ?? 'Audit logs are not supported by the current AuthProvider';
|
|
33
|
+
error = i18n.t('settings.auditNotSupported') ?? 'Audit logs are not supported by the current AuthProvider';
|
|
31
34
|
loading = false;
|
|
32
35
|
return;
|
|
33
36
|
}
|
|
@@ -87,10 +90,10 @@
|
|
|
87
90
|
<div class="mb-6">
|
|
88
91
|
<h2 class="text-xl font-semibold text-foreground flex items-center gap-2">
|
|
89
92
|
<FileSearch class="h-5 w-5 text-muted-foreground" />
|
|
90
|
-
{t('settings.auditLogs') ?? 'Audit Logs'}
|
|
93
|
+
{i18n.t('settings.auditLogs') ?? 'Audit Logs'}
|
|
91
94
|
</h2>
|
|
92
95
|
<p class="text-sm text-muted-foreground mt-1">
|
|
93
|
-
{t('settings.auditDescription') ?? 'Track and review all system operations for compliance and debugging.'}
|
|
96
|
+
{i18n.t('settings.auditDescription') ?? 'Track and review all system operations for compliance and debugging.'}
|
|
94
97
|
</p>
|
|
95
98
|
</div>
|
|
96
99
|
|
|
@@ -104,7 +107,7 @@
|
|
|
104
107
|
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
105
108
|
<Input
|
|
106
109
|
bind:value={searchQuery}
|
|
107
|
-
placeholder={t('settings.searchLogs') ?? 'Search by user, action, or resource...'}
|
|
110
|
+
placeholder={i18n.t('settings.searchLogs') ?? 'Search by user, action, or resource...'}
|
|
108
111
|
class="pl-9"
|
|
109
112
|
/>
|
|
110
113
|
</div>
|
|
@@ -114,19 +117,19 @@
|
|
|
114
117
|
{#if loading}
|
|
115
118
|
<div class="flex items-center justify-center h-48 text-muted-foreground">
|
|
116
119
|
<Loader2 class="h-5 w-5 animate-spin mr-2" />
|
|
117
|
-
{t('common.loading')}
|
|
120
|
+
{i18n.t('common.loading')}
|
|
118
121
|
</div>
|
|
119
122
|
{:else}
|
|
120
123
|
<div class="overflow-auto">
|
|
121
124
|
<Table.Root>
|
|
122
125
|
<Table.Header>
|
|
123
126
|
<Table.Row>
|
|
124
|
-
<Table.Head class="w-[160px]">{t('settings.auditTime') ?? 'Time'}</Table.Head>
|
|
125
|
-
<Table.Head>{t('settings.auditUser') ?? 'User'}</Table.Head>
|
|
126
|
-
<Table.Head>{t('settings.auditAction') ?? 'Action'}</Table.Head>
|
|
127
|
-
<Table.Head>{t('settings.auditResource') ?? 'Resource'}</Table.Head>
|
|
128
|
-
<Table.Head class="w-[120px]">{t('settings.auditIp') ?? 'IP Address'}</Table.Head>
|
|
129
|
-
<Table.Head class="w-[60px] text-center">{t('common.detail') ?? 'Detail'}</Table.Head>
|
|
127
|
+
<Table.Head class="w-[160px]">{i18n.t('settings.auditTime') ?? 'Time'}</Table.Head>
|
|
128
|
+
<Table.Head>{i18n.t('settings.auditUser') ?? 'User'}</Table.Head>
|
|
129
|
+
<Table.Head>{i18n.t('settings.auditAction') ?? 'Action'}</Table.Head>
|
|
130
|
+
<Table.Head>{i18n.t('settings.auditResource') ?? 'Resource'}</Table.Head>
|
|
131
|
+
<Table.Head class="w-[120px]">{i18n.t('settings.auditIp') ?? 'IP Address'}</Table.Head>
|
|
132
|
+
<Table.Head class="w-[60px] text-center">{i18n.t('common.detail') ?? 'Detail'}</Table.Head>
|
|
130
133
|
</Table.Row>
|
|
131
134
|
</Table.Header>
|
|
132
135
|
<Table.Body>
|
|
@@ -152,7 +155,7 @@
|
|
|
152
155
|
{:else}
|
|
153
156
|
<Table.Row>
|
|
154
157
|
<Table.Cell colspan={6} class="h-24 text-center text-muted-foreground">
|
|
155
|
-
{t('common.noData')}
|
|
158
|
+
{i18n.t('common.noData')}
|
|
156
159
|
</Table.Cell>
|
|
157
160
|
</Table.Row>
|
|
158
161
|
{/each}
|
|
@@ -164,7 +167,7 @@
|
|
|
164
167
|
|
|
165
168
|
<!-- Pagination -->
|
|
166
169
|
<div class="mt-4 flex items-center justify-between text-sm text-muted-foreground">
|
|
167
|
-
<span>{total} {t('settings.auditTotal') ?? 'total records'}</span>
|
|
170
|
+
<span>{total} {i18n.t('settings.auditTotal') ?? 'total records'}</span>
|
|
168
171
|
<div class="flex items-center gap-2">
|
|
169
172
|
<Button
|
|
170
173
|
variant="outline" size="sm"
|
|
@@ -199,7 +202,7 @@
|
|
|
199
202
|
<div>
|
|
200
203
|
<h2 class="text-lg font-bold text-foreground flex items-center gap-2">
|
|
201
204
|
<FileSearch class="h-5 w-5 text-primary" />
|
|
202
|
-
{t('settings.auditSnapshot') ?? 'Change Snapshot'}
|
|
205
|
+
{i18n.t('settings.auditSnapshot') ?? 'Change Snapshot'}
|
|
203
206
|
</h2>
|
|
204
207
|
<p class="text-xs text-muted-foreground mt-1">{formatDate(drawerLog.createdAt)} · {drawerLog.userName ?? drawerLog.userId}</p>
|
|
205
208
|
</div>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { captureAdminContext, useIsAuthenticated } from '@svadmin/core';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
|
-
import { navigate } from '@svadmin/core';
|
|
5
4
|
|
|
6
5
|
let {
|
|
7
6
|
children,
|
|
@@ -17,13 +16,14 @@
|
|
|
17
16
|
appendCurrentPathToQuery?: boolean;
|
|
18
17
|
}>();
|
|
19
18
|
|
|
19
|
+
const adminContext = captureAdminContext();
|
|
20
20
|
const isAuth = useIsAuthenticated();
|
|
21
21
|
|
|
22
22
|
function triggerRedirect() {
|
|
23
23
|
if (fallback) return;
|
|
24
24
|
let url = redirectTo;
|
|
25
25
|
if (appendCurrentPathToQuery && typeof window !== 'undefined') {
|
|
26
|
-
const rp =
|
|
26
|
+
const rp = adminContext.routerProvider;
|
|
27
27
|
let currentString = window.location.pathname + window.location.search;
|
|
28
28
|
if (rp) {
|
|
29
29
|
const parsed = rp.parse();
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
const current = encodeURIComponent(currentString);
|
|
34
34
|
url += url.includes('?') ? `&to=${current}` : `?to=${current}`;
|
|
35
35
|
}
|
|
36
|
-
navigate(url);
|
|
36
|
+
adminContext.navigate(url);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
$effect(() => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { useForm, getResource, deriveValidator } from '@svadmin/core';
|
|
2
|
+
import { captureAdminContext, useForm, getResource, deriveValidator } from '@svadmin/core';
|
|
3
3
|
import { slide } from 'svelte/transition';
|
|
4
4
|
import type { FieldDefinition } from '@svadmin/core';
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
6
|
+
|
|
7
7
|
import { Button } from './ui/button/index.js';
|
|
8
8
|
import TooltipButton from './TooltipButton.svelte';
|
|
9
9
|
import * as Card from './ui/card/index.js';
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
|
16
16
|
import type { Snippet } from 'svelte';
|
|
17
17
|
|
|
18
|
+
const i18n = useTranslation();
|
|
19
|
+
|
|
18
20
|
interface Props {
|
|
19
21
|
resourceName: string;
|
|
20
22
|
id?: string | number;
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
let { resourceName, id, mode = 'create', fieldRenderer, formActions, headerContent, onSuccess }: Props = $props();
|
|
31
|
+
const adminContext = captureAdminContext();
|
|
29
32
|
const isReadonly = $derived(mode === 'show');
|
|
30
33
|
|
|
31
34
|
// ─── Resource metadata ────────────────────────────────────────────
|
|
@@ -73,7 +76,7 @@
|
|
|
73
76
|
return d;
|
|
74
77
|
})());
|
|
75
78
|
|
|
76
|
-
const validator = $derived(deriveValidator(formFields));
|
|
79
|
+
const validator = $derived(deriveValidator(formFields, { translate: i18n.t }));
|
|
77
80
|
|
|
78
81
|
// ─── useForm: single source of truth for values, errors, tainted ──
|
|
79
82
|
const form = useForm({
|
|
@@ -95,16 +98,16 @@
|
|
|
95
98
|
await form.submit();
|
|
96
99
|
onSuccess?.();
|
|
97
100
|
} catch (e) {
|
|
98
|
-
submitError = e instanceof Error ? e.message : t('common.operationFailed');
|
|
101
|
+
submitError = e instanceof Error ? e.message : i18n.t('common.operationFailed');
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
const pageTitle = $derived(
|
|
103
106
|
mode === 'create'
|
|
104
|
-
? `${t('common.create')}${resource.label}`
|
|
107
|
+
? `${i18n.t('common.create')}${resource.label}`
|
|
105
108
|
: mode === 'show'
|
|
106
|
-
? `${t('common.detail')}${resource.label}`
|
|
107
|
-
: `${t('common.edit')}${resource.label}`
|
|
109
|
+
? `${i18n.t('common.detail')}${resource.label}`
|
|
110
|
+
: `${i18n.t('common.edit')}${resource.label}`
|
|
108
111
|
);
|
|
109
112
|
|
|
110
113
|
// ─── Unsaved changes guard ────────────────────────────────────────
|
|
@@ -135,8 +138,8 @@
|
|
|
135
138
|
<div class="space-y-6">
|
|
136
139
|
<div class="flex items-center gap-4">
|
|
137
140
|
<TooltipButton
|
|
138
|
-
tooltip={t('common.back')}
|
|
139
|
-
onclick={() => guardNavigate(() => navigate(`/${resourceName}`))}
|
|
141
|
+
tooltip={i18n.t('common.back')}
|
|
142
|
+
onclick={() => guardNavigate(() => adminContext.navigate(`/${resourceName}`))}
|
|
140
143
|
>
|
|
141
144
|
<ArrowLeft class="h-5 w-5" />
|
|
142
145
|
</TooltipButton>
|
|
@@ -145,7 +148,7 @@
|
|
|
145
148
|
{@render headerContent()}
|
|
146
149
|
{/if}
|
|
147
150
|
{#if form.isTainted()}
|
|
148
|
-
<Badge variant="outline" class="border-warning/30 bg-warning/10 text-warning-foreground">{t('common.unsaved')}</Badge>
|
|
151
|
+
<Badge variant="outline" class="border-warning/30 bg-warning/10 text-warning-foreground">{i18n.t('common.unsaved')}</Badge>
|
|
149
152
|
{/if}
|
|
150
153
|
</div>
|
|
151
154
|
|
|
@@ -234,14 +237,14 @@
|
|
|
234
237
|
{:else}
|
|
235
238
|
<Save class="h-4 w-4" data-icon="inline-start" />
|
|
236
239
|
{/if}
|
|
237
|
-
{t('common.save')}
|
|
240
|
+
{i18n.t('common.save')}
|
|
238
241
|
</Button>
|
|
239
242
|
<Button
|
|
240
243
|
type="button"
|
|
241
244
|
variant="outline"
|
|
242
|
-
onclick={() => guardNavigate(() => navigate(`/${resourceName}`))}
|
|
245
|
+
onclick={() => guardNavigate(() => adminContext.navigate(`/${resourceName}`))}
|
|
243
246
|
>
|
|
244
|
-
{t('common.cancel')}
|
|
247
|
+
{i18n.t('common.cancel')}
|
|
245
248
|
</Button>
|
|
246
249
|
{/if}
|
|
247
250
|
</div>
|
|
@@ -251,8 +254,8 @@
|
|
|
251
254
|
|
|
252
255
|
<ConfirmDialog
|
|
253
256
|
open={confirmOpen}
|
|
254
|
-
message={t('common.unsavedChanges')}
|
|
255
|
-
confirmText={t('common.confirm')}
|
|
257
|
+
message={i18n.t('common.unsavedChanges')}
|
|
258
|
+
confirmText={i18n.t('common.confirm')}
|
|
256
259
|
onconfirm={confirmNavigate}
|
|
257
260
|
oncancel={cancelNavigate}
|
|
258
261
|
/>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { fade, fly } from 'svelte/transition';
|
|
3
3
|
import { Loader2, Check, X, Cloud } from '@lucide/svelte';
|
|
4
|
-
import {
|
|
4
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
5
|
+
|
|
6
|
+
const i18n = useTranslation();
|
|
5
7
|
|
|
6
8
|
let {
|
|
7
9
|
status = 'idle',
|
|
@@ -19,16 +21,16 @@
|
|
|
19
21
|
<div class="flex items-center gap-2" in:fly={{ y: -8, duration: 200 }} out:fade={{ duration: 150 }}>
|
|
20
22
|
{#if status === 'idle'}
|
|
21
23
|
<Cloud class="h-4 w-4" />
|
|
22
|
-
<span>{t('autoSave.idle')}</span>
|
|
24
|
+
<span>{i18n.t('autoSave.idle')}</span>
|
|
23
25
|
{:else if status === 'loading'}
|
|
24
26
|
<Loader2 class="h-4 w-4 animate-spin text-primary" />
|
|
25
|
-
<span>{t('autoSave.saving')}</span>
|
|
27
|
+
<span>{i18n.t('autoSave.saving')}</span>
|
|
26
28
|
{:else if status === 'success'}
|
|
27
29
|
<Check class="h-4 w-4 text-green-500" />
|
|
28
|
-
<span class="text-green-500">{t('autoSave.saved')}</span>
|
|
30
|
+
<span class="text-green-500">{i18n.t('autoSave.saved')}</span>
|
|
29
31
|
{:else if status === 'error'}
|
|
30
32
|
<X class="h-4 w-4 text-destructive" />
|
|
31
|
-
<span class="text-destructive" title={error}>{t('autoSave.error')}</span>
|
|
33
|
+
<span class="text-destructive" title={error}>{i18n.t('autoSave.error')}</span>
|
|
32
34
|
{/if}
|
|
33
35
|
</div>
|
|
34
36
|
{/key}
|