@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.
Files changed (144) hide show
  1. package/dist/app.css +8 -4
  2. package/dist/app.theme.css +8 -4
  3. package/dist/components/AICommandBar.svelte +16 -15
  4. package/dist/components/AboutSettings.svelte +12 -11
  5. package/dist/components/AdminApp.svelte +144 -70
  6. package/dist/components/AdminApp.svelte.d.ts +6 -3
  7. package/dist/components/ApiSettings.svelte +19 -16
  8. package/dist/components/AppearanceSettings.svelte +18 -16
  9. package/dist/components/AuditLogDrawer.svelte +6 -3
  10. package/dist/components/AuditLogViewer.svelte +18 -15
  11. package/dist/components/Authenticated.svelte +4 -4
  12. package/dist/components/AutoForm.svelte +19 -16
  13. package/dist/components/AutoSaveIndicator.svelte +7 -5
  14. package/dist/components/AutoTable.svelte +85 -71
  15. package/dist/components/AutoTable.svelte.d.ts +1 -1
  16. package/dist/components/Breadcrumbs.svelte +17 -13
  17. package/dist/components/CancelTaskButton.svelte +5 -2
  18. package/dist/components/CatchAllNavigate.svelte +3 -3
  19. package/dist/components/ChatDialog.svelte +22 -19
  20. package/dist/components/ComboboxField.svelte +6 -4
  21. package/dist/components/CommandPalette.svelte +20 -20
  22. package/dist/components/ConfigErrorScreen.svelte +12 -10
  23. package/dist/components/ConfirmDialog.svelte +6 -4
  24. package/dist/components/CreatePage.svelte +4 -2
  25. package/dist/components/DevTools.svelte +11 -9
  26. package/dist/components/DrawerForm.svelte +5 -2
  27. package/dist/components/EditPage.svelte +8 -5
  28. package/dist/components/EmptyState.svelte +5 -3
  29. package/dist/components/ErrorBoundary.svelte +5 -3
  30. package/dist/components/ErrorComponent.svelte +6 -4
  31. package/dist/components/ErrorPage.svelte +12 -10
  32. package/dist/components/FieldRenderer.svelte +17 -15
  33. package/dist/components/ForgotPasswordPage.svelte +22 -19
  34. package/dist/components/Header.svelte +7 -4
  35. package/dist/components/InfiniteList.svelte +6 -3
  36. package/dist/components/IntegrationsSettings.svelte +9 -6
  37. package/dist/components/KeyboardShortcuts.svelte +4 -2
  38. package/dist/components/Layout.svelte +12 -13
  39. package/dist/components/LazyPage.svelte +47 -0
  40. package/dist/components/LazyPage.svelte.d.ts +14 -0
  41. package/dist/components/ListPage.svelte +8 -5
  42. package/dist/components/LoginPage.svelte +24 -22
  43. package/dist/components/MarkdownRenderer.svelte +89 -35
  44. package/dist/components/ModalForm.svelte +4 -2
  45. package/dist/components/NavigateToResource.svelte +3 -3
  46. package/dist/components/NotificationsSettings.svelte +26 -24
  47. package/dist/components/PasswordInput.svelte +4 -2
  48. package/dist/components/PermissionMatrix.svelte +15 -12
  49. package/dist/components/ProfilePage.svelte +23 -21
  50. package/dist/components/RegisterPage.svelte +21 -19
  51. package/dist/components/ResourceOperationsPage.svelte +3 -2
  52. package/dist/components/RetryTaskButton.svelte +5 -2
  53. package/dist/components/RolesSettings.svelte +10 -8
  54. package/dist/components/SecuritySettings.svelte +26 -23
  55. package/dist/components/SettingsPage.svelte +14 -11
  56. package/dist/components/ShowPage.svelte +9 -7
  57. package/dist/components/Sidebar.svelte +36 -28
  58. package/dist/components/SidebarItem.svelte +4 -4
  59. package/dist/components/SidebarItem.svelte.d.ts +1 -1
  60. package/dist/components/StepsForm.svelte +11 -8
  61. package/dist/components/TaskDetails.svelte +14 -11
  62. package/dist/components/TaskList.svelte +14 -11
  63. package/dist/components/TaskQueueDrawer.svelte +34 -32
  64. package/dist/components/TaskStatusBadge.svelte +5 -2
  65. package/dist/components/Toast.svelte +18 -4
  66. package/dist/components/TooltipButton.svelte +25 -3
  67. package/dist/components/TwoFactorAuthPage.svelte +26 -24
  68. package/dist/components/UndoableNotification.svelte +5 -2
  69. package/dist/components/UpdatePasswordPage.svelte +18 -15
  70. package/dist/components/VirtualTable.svelte +0 -4
  71. package/dist/components/account/CompanyProfilePage.svelte +13 -11
  72. package/dist/components/account/GetStartedPage.svelte +15 -13
  73. package/dist/components/account/ImportMembersPage.svelte +14 -12
  74. package/dist/components/account/MembersStarterPage.svelte +10 -8
  75. package/dist/components/account/SecurityLogPage.svelte +24 -22
  76. package/dist/components/account/SettingsEnterprisePage.svelte +6 -4
  77. package/dist/components/account/SettingsPlainPage.svelte +14 -12
  78. package/dist/components/account/SettingsSidebarPage.svelte +11 -9
  79. package/dist/components/account/TeamMembersPage.svelte +10 -8
  80. package/dist/components/buttons/CloneButton.svelte +4 -2
  81. package/dist/components/buttons/CreateButton.svelte +4 -2
  82. package/dist/components/buttons/DeleteButton.svelte +6 -4
  83. package/dist/components/buttons/EditButton.svelte +4 -2
  84. package/dist/components/buttons/ExportButton.svelte +4 -2
  85. package/dist/components/buttons/ImportButton.svelte +4 -2
  86. package/dist/components/buttons/RefreshButton.svelte +4 -2
  87. package/dist/components/buttons/SaveButton.svelte +4 -2
  88. package/dist/components/buttons/ShowButton.svelte +4 -2
  89. package/dist/components/fieldComponentMap.js +0 -3
  90. package/dist/components/fields/ArrayField.svelte +6 -4
  91. package/dist/components/fields/JsonField.svelte +5 -3
  92. package/dist/components/fields/RelationField.svelte +3 -3
  93. package/dist/components/fields/RichTextField.svelte +4 -2
  94. package/dist/components/network/TeamCrewTablePage.svelte +18 -16
  95. package/dist/components/network/UserCardsNFTPage.svelte +11 -9
  96. package/dist/components/profile/ActivityTimeline.svelte +10 -8
  97. package/dist/components/profile/ProfileCard.svelte +13 -11
  98. package/dist/components/profile/ProjectsGrid.svelte +7 -5
  99. package/dist/components/profile/PublicProfilePage.svelte +18 -16
  100. package/dist/components/profile/TeamsShowcase.svelte +7 -5
  101. package/dist/components/toast-host.svelte.d.ts +5 -0
  102. package/dist/components/toast-host.svelte.js +21 -0
  103. package/dist/components/ui/command/command-dialog.svelte +29 -13
  104. package/dist/components/ui/command/command-dialog.svelte.d.ts +4 -3
  105. package/dist/components/ui/command/command-empty.svelte +18 -0
  106. package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
  107. package/dist/components/ui/command/command-group.svelte +41 -0
  108. package/dist/components/ui/command/command-group.svelte.d.ts +8 -0
  109. package/dist/components/ui/command/command-input.svelte +23 -0
  110. package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
  111. package/dist/components/ui/command/command-item.svelte +43 -0
  112. package/dist/components/ui/command/command-item.svelte.d.ts +11 -0
  113. package/dist/components/ui/command/command-list.svelte +18 -0
  114. package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
  115. package/dist/components/ui/command/command-loading.svelte +12 -0
  116. package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
  117. package/dist/components/ui/command/command-separator.svelte +23 -0
  118. package/dist/components/ui/command/command-separator.svelte.d.ts +7 -0
  119. package/dist/components/ui/command/command.svelte +26 -0
  120. package/dist/components/ui/command/command.svelte.d.ts +4 -0
  121. package/dist/components/ui/command/index.d.ts +55 -2
  122. package/dist/components/ui/command/index.js +21 -2
  123. package/dist/components/ui/sheet/sheet-content.svelte +1 -4
  124. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +1 -3
  125. package/dist/components/ui/tabs/tabs-content.svelte +6 -2
  126. package/dist/components/ui/tabs/tabs-context.svelte.d.ts +5 -0
  127. package/dist/components/ui/tabs/tabs-context.svelte.js +2 -0
  128. package/dist/components/ui/tabs/tabs-trigger.svelte +49 -4
  129. package/dist/components/ui/tabs/tabs.svelte +12 -1
  130. package/dist/components/ui/tooltip/index.d.ts +4 -1
  131. package/dist/components/ui/tooltip/index.js +3 -1
  132. package/dist/components/ui/tooltip/tooltip-content.svelte +308 -35
  133. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +42 -3
  134. package/dist/components/ui/tooltip/tooltip-context.svelte.d.ts +37 -0
  135. package/dist/components/ui/tooltip/tooltip-context.svelte.js +220 -0
  136. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.d.ts +11 -0
  137. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.js +93 -0
  138. package/dist/components/ui/tooltip/tooltip-trigger.svelte +56 -3
  139. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +23 -2
  140. package/dist/components/ui/tooltip/tooltip.svelte +63 -3
  141. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +23 -2
  142. package/dist/router-state.svelte.d.ts +13 -0
  143. package/dist/router-state.svelte.js +72 -37
  144. package/package.json +13 -11
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
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 { t } from '@svadmin/core/i18n';
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(getLocale());
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, t } from '@svadmin/core';
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 { t } from '@svadmin/core/i18n';
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 { useIsAuthenticated, getRouterProvider } from '@svadmin/core';
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 = getRouterProvider();
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 { t } from '@svadmin/core/i18n';
6
- import { navigate } from '@svadmin/core/router';
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 { t } from '@svadmin/core/i18n';
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}