@svadmin/ui 0.38.1 → 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 +10 -5
  2. package/dist/app.theme.css +1374 -0
  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 +18 -12
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Input } from '../ui/input/index.js';
6
6
  import { Label } from '../ui/label/index.js';
7
7
  import { Upload, Loader2 } from '@lucide/svelte';
8
8
 
9
+ const i18n = useTranslation();
10
+
9
11
  let companyName = $state('Acme Corporation');
10
12
  let industry = $state('Technology');
11
13
  let website = $state('https://acme.com');
@@ -23,8 +25,8 @@
23
25
 
24
26
  <div class="space-y-6" data-svadmin-content-page="account">
25
27
  <div>
26
- <h2 class="text-xl font-semibold text-foreground">{t('account.companyProfile')}</h2>
27
- <p class="mt-1 text-sm text-muted-foreground">{t('account.companyProfileDescription')}</p>
28
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.companyProfile')}</h2>
29
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.companyProfileDescription')}</p>
28
30
  </div>
29
31
 
30
32
  <!-- Company Avatar & Banner -->
@@ -54,33 +56,33 @@
54
56
  <!-- Company Details Form -->
55
57
  <Card.Card class="border-border/60">
56
58
  <Card.CardHeader>
57
- <Card.CardTitle class="text-base">{t('profile.companyName')}</Card.CardTitle>
59
+ <Card.CardTitle class="text-base">{i18n.t('profile.companyName')}</Card.CardTitle>
58
60
  </Card.CardHeader>
59
61
  <Card.CardContent class="space-y-4">
60
62
  <div class="grid gap-4 sm:grid-cols-2">
61
63
  <div class="space-y-2">
62
- <Label for="company-name">{t('profile.companyName')}</Label>
64
+ <Label for="company-name">{i18n.t('profile.companyName')}</Label>
63
65
  <Input id="company-name" bind:value={companyName} />
64
66
  </div>
65
67
  <div class="space-y-2">
66
- <Label for="company-industry">{t('profile.industry')}</Label>
68
+ <Label for="company-industry">{i18n.t('profile.industry')}</Label>
67
69
  <Input id="company-industry" bind:value={industry} />
68
70
  </div>
69
71
  <div class="space-y-2">
70
- <Label for="company-website">{t('profile.website')}</Label>
72
+ <Label for="company-website">{i18n.t('profile.website')}</Label>
71
73
  <Input id="company-website" bind:value={website} type="url" />
72
74
  </div>
73
75
  <div class="space-y-2">
74
- <Label for="company-employees">{t('profile.employees')}</Label>
76
+ <Label for="company-employees">{i18n.t('profile.employees')}</Label>
75
77
  <Input id="company-employees" bind:value={employeeCount} />
76
78
  </div>
77
79
  <div class="space-y-2">
78
- <Label for="company-founded">{t('profile.founded')}</Label>
80
+ <Label for="company-founded">{i18n.t('profile.founded')}</Label>
79
81
  <Input id="company-founded" bind:value={foundedYear} />
80
82
  </div>
81
83
  </div>
82
84
  <div class="space-y-2">
83
- <Label for="company-description">{t('profile.companyDescription')}</Label>
85
+ <Label for="company-description">{i18n.t('profile.companyDescription')}</Label>
84
86
  <textarea
85
87
  id="company-description"
86
88
  bind:value={description}
@@ -91,7 +93,7 @@
91
93
  <div class="flex justify-end">
92
94
  <Button onclick={handleSave} disabled={saving}>
93
95
  {#if saving}<Loader2 class="h-4 w-4 animate-spin mr-2" />{/if}
94
- {t('common.save')}
96
+ {i18n.t('common.save')}
95
97
  </Button>
96
98
  </div>
97
99
  </Card.CardContent>
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Badge } from '../ui/badge/index.js';
6
6
  import { Camera, UserPlus, Puzzle, Users, CheckCircle2, ArrowRight, Sparkles } from '@lucide/svelte';
7
7
  import type { Component } from 'svelte';
8
8
 
9
+ const i18n = useTranslation();
10
+
9
11
  interface Step {
10
12
  key: string;
11
13
  title: string;
@@ -17,10 +19,10 @@
17
19
  let currentStep = $state(0);
18
20
 
19
21
  let steps = $state<Step[]>([
20
- { key: 'avatar', title: t('account.uploadAvatar'), description: t('account.getStartedDescription'), icon: Camera, completed: false },
21
- { key: 'profile', title: t('account.setupProfile'), description: t('account.getStartedDescription'), icon: UserPlus, completed: false },
22
- { key: 'connect', title: t('account.connectProvider'), description: t('account.getStartedDescription'), icon: Puzzle, completed: false },
23
- { key: 'team', title: t('account.setupTeam'), description: t('account.getStartedDescription'), icon: Users, completed: false },
22
+ { key: 'avatar', title: i18n.t('account.uploadAvatar'), description: i18n.t('account.getStartedDescription'), icon: Camera, completed: false },
23
+ { key: 'profile', title: i18n.t('account.setupProfile'), description: i18n.t('account.getStartedDescription'), icon: UserPlus, completed: false },
24
+ { key: 'connect', title: i18n.t('account.connectProvider'), description: i18n.t('account.getStartedDescription'), icon: Puzzle, completed: false },
25
+ { key: 'team', title: i18n.t('account.setupTeam'), description: i18n.t('account.getStartedDescription'), icon: Users, completed: false },
24
26
  ]);
25
27
 
26
28
  const completedCount = $derived(steps.filter(s => s.completed).length);
@@ -50,8 +52,8 @@
50
52
  <div class="space-y-6" data-svadmin-content-page="account">
51
53
  <div class="flex items-start justify-between gap-4">
52
54
  <div>
53
- <h2 class="text-xl font-semibold text-foreground">{t('account.getStarted')}</h2>
54
- <p class="mt-1 text-sm text-muted-foreground">{t('account.getStartedDescription')}</p>
55
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.getStarted')}</h2>
56
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.getStartedDescription')}</p>
55
57
  </div>
56
58
  <Badge variant="secondary">{progressPercent}%</Badge>
57
59
  </div>
@@ -61,7 +63,7 @@
61
63
  <div class="h-2 rounded-full bg-muted overflow-hidden">
62
64
  <div class="h-full rounded-full bg-primary transition-all duration-500" style="width: {progressPercent}%"></div>
63
65
  </div>
64
- <p class="text-xs text-muted-foreground">{t('account.step', { current: completedCount, total: steps.length })}</p>
66
+ <p class="text-xs text-muted-foreground">{i18n.t('account.step', { current: completedCount, total: steps.length })}</p>
65
67
  </div>
66
68
 
67
69
  {#if allCompleted}
@@ -71,8 +73,8 @@
71
73
  <Sparkles class="h-6 w-6" />
72
74
  </div>
73
75
  <div>
74
- <h3 class="font-semibold text-success">{t('account.completeSetup')}</h3>
75
- <p class="text-sm text-success/70">{t('account.getStartedDescription')}</p>
76
+ <h3 class="font-semibold text-success">{i18n.t('account.completeSetup')}</h3>
77
+ <p class="text-sm text-success/70">{i18n.t('account.getStartedDescription')}</p>
76
78
  </div>
77
79
  </Card.CardContent>
78
80
  </Card.Card>
@@ -98,7 +100,7 @@
98
100
  <div class="flex items-center gap-2">
99
101
  <h4 class="font-medium text-foreground">{step.title}</h4>
100
102
  {#if currentStep === i && !step.completed}
101
- <Badge variant="outline" class="text-[10px]">{t('account.step', { current: i + 1, total: steps.length })}</Badge>
103
+ <Badge variant="outline" class="text-[10px]">{i18n.t('account.step', { current: i + 1, total: steps.length })}</Badge>
102
104
  {/if}
103
105
  </div>
104
106
  <p class="text-sm text-muted-foreground mt-0.5">{step.description}</p>
@@ -106,10 +108,10 @@
106
108
  {#if currentStep === i && !step.completed}
107
109
  <div class="mt-3 flex gap-2">
108
110
  <Button size="sm" onclick={() => markComplete(i)}>
109
- {t('account.completeSetup')} <ArrowRight class="h-3.5 w-3.5 ml-1" />
111
+ {i18n.t('account.completeSetup')} <ArrowRight class="h-3.5 w-3.5 ml-1" />
110
112
  </Button>
111
113
  <Button size="sm" variant="ghost" onclick={skipStep}>
112
- {t('account.skipSetup')}
114
+ {i18n.t('account.skipSetup')}
113
115
  </Button>
114
116
  </div>
115
117
  {/if}
@@ -1,9 +1,11 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Upload, Download, FileText, CheckCircle2, Loader2 } from '@lucide/svelte';
6
6
 
7
+ const i18n = useTranslation();
8
+
7
9
  let isDragging = $state(false);
8
10
  let fileName = $state('');
9
11
  let importState = $state<'idle' | 'uploading' | 'processing' | 'complete' | 'error'>('idle');
@@ -61,8 +63,8 @@
61
63
 
62
64
  <div class="space-y-6" data-svadmin-content-page="account">
63
65
  <div>
64
- <h2 class="text-xl font-semibold text-foreground">{t('account.importMembers')}</h2>
65
- <p class="mt-1 text-sm text-muted-foreground">{t('account.importMembersDescription')}</p>
66
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.importMembers')}</h2>
67
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.importMembersDescription')}</p>
66
68
  </div>
67
69
 
68
70
  {#if importState === 'complete'}
@@ -72,10 +74,10 @@
72
74
  <CheckCircle2 class="h-7 w-7" />
73
75
  </div>
74
76
  <div class="text-center">
75
- <h3 class="text-lg font-semibold text-success">{t('account.importComplete')}</h3>
76
- <p class="text-sm text-success/70 mt-1">{t('account.imported', { count: importCount })}</p>
77
+ <h3 class="text-lg font-semibold text-success">{i18n.t('account.importComplete')}</h3>
78
+ <p class="text-sm text-success/70 mt-1">{i18n.t('account.imported', { count: importCount })}</p>
77
79
  </div>
78
- <Button variant="outline" onclick={resetImport}>{t('common.back')}</Button>
80
+ <Button variant="outline" onclick={resetImport}>{i18n.t('common.back')}</Button>
79
81
  </Card.CardContent>
80
82
  </Card.Card>
81
83
  {:else}
@@ -92,11 +94,11 @@
92
94
  >
93
95
  {#if importState === 'idle'}
94
96
  <Upload class="mx-auto h-10 w-10 text-muted-foreground" />
95
- <p class="mt-3 text-sm font-medium text-foreground">{t('account.dragOrClick')}</p>
96
- <p class="mt-1 text-xs text-muted-foreground">{t('account.supportedFormats')}</p>
97
+ <p class="mt-3 text-sm font-medium text-foreground">{i18n.t('account.dragOrClick')}</p>
98
+ <p class="mt-1 text-xs text-muted-foreground">{i18n.t('account.supportedFormats')}</p>
97
99
  <div class="mt-4">
98
100
  <span class="inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium shadow-xs transition-colors hover:bg-accent hover:text-accent-foreground">
99
- {t('common.upload')}
101
+ {i18n.t('common.upload')}
100
102
  </span>
101
103
  <input id="file-input" type="file" accept=".csv,.xlsx" class="sr-only" onchange={handleFileSelect} />
102
104
  </div>
@@ -114,7 +116,7 @@
114
116
  <div class="h-full rounded-full bg-primary transition-all duration-300" style="width: {progress}%"></div>
115
117
  </div>
116
118
  <p class="text-xs text-muted-foreground">
117
- {importState === 'uploading' ? t('account.importProgress') : t('account.startImport')}...
119
+ {importState === 'uploading' ? i18n.t('account.importProgress') : i18n.t('account.startImport')}...
118
120
  </p>
119
121
  </div>
120
122
  {/if}
@@ -130,12 +132,12 @@
130
132
  <FileText class="h-4 w-4" />
131
133
  </div>
132
134
  <div>
133
- <p class="text-sm font-medium text-foreground">{t('account.downloadTemplate')}</p>
135
+ <p class="text-sm font-medium text-foreground">{i18n.t('account.downloadTemplate')}</p>
134
136
  <p class="text-xs text-muted-foreground">CSV template.csv</p>
135
137
  </div>
136
138
  </div>
137
139
  <Button variant="outline" size="sm">
138
- <Download class="h-3.5 w-3.5 mr-1" />{t('account.downloadTemplate')}
140
+ <Download class="h-3.5 w-3.5 mr-1" />{i18n.t('account.downloadTemplate')}
139
141
  </Button>
140
142
  </Card.CardContent>
141
143
  </Card.Card>
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Badge } from '../ui/badge/index.js';
@@ -7,6 +7,8 @@
7
7
  import { Input } from '../ui/input/index.js';
8
8
  import { Search, UserPlus, MoreHorizontal, Clock, CheckCircle2, XCircle } from '@lucide/svelte';
9
9
 
10
+ const i18n = useTranslation();
11
+
10
12
  interface Member {
11
13
  id: string;
12
14
  name: string;
@@ -35,9 +37,9 @@
35
37
  );
36
38
 
37
39
  const statusConfig = {
38
- 'active': { icon: CheckCircle2, color: 'text-green-500', label: t('account.active') },
39
- 'invited': { icon: Clock, color: 'text-amber-500', label: t('account.invited') },
40
- 'inactive': { icon: XCircle, color: 'text-muted-foreground', label: t('account.inactive') },
40
+ 'active': { icon: CheckCircle2, color: 'text-green-500', label: i18n.t('account.active') },
41
+ 'invited': { icon: Clock, color: 'text-amber-500', label: i18n.t('account.invited') },
42
+ 'inactive': { icon: XCircle, color: 'text-muted-foreground', label: i18n.t('account.inactive') },
41
43
  };
42
44
 
43
45
  const initials = (name: string) => name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
@@ -46,11 +48,11 @@
46
48
  <div class="space-y-6" data-svadmin-content-page="account">
47
49
  <div class="flex items-start justify-between gap-4">
48
50
  <div>
49
- <h2 class="text-xl font-semibold text-foreground">{t('account.membersStarter')}</h2>
50
- <p class="mt-1 text-sm text-muted-foreground">{t('account.membersStarterDescription')}</p>
51
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.membersStarter')}</h2>
52
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.membersStarterDescription')}</p>
51
53
  </div>
52
54
  <Button size="sm">
53
- <UserPlus class="h-4 w-4 mr-1" />{t('account.inviteMember')}
55
+ <UserPlus class="h-4 w-4 mr-1" />{i18n.t('account.inviteMember')}
54
56
  </Button>
55
57
  </div>
56
58
 
@@ -58,7 +60,7 @@
58
60
  <div class="relative">
59
61
  <Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
60
62
  <Input
61
- placeholder={t('common.search')}
63
+ placeholder={i18n.t('common.search')}
62
64
  bind:value={searchQuery}
63
65
  class="pl-9"
64
66
  />
@@ -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
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Input } from '../ui/input/index.js';
6
6
  import { Search, Download } from '@lucide/svelte';
7
7
 
8
+ const i18n = useTranslation();
9
+
8
10
  interface LogEntry {
9
11
  id: string;
10
12
  event: string;
@@ -20,14 +22,14 @@
20
22
  let filterSeverity = $state<'all' | 'info' | 'warning' | 'critical'>('all');
21
23
 
22
24
  const logs: LogEntry[] = [
23
- { id: '1', event: 'loginSuccess', eventLabel: t('securityLog.loginSuccess'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '2 minutes ago', severity: 'info' },
24
- { id: '2', event: 'apiKeyCreated', eventLabel: t('securityLog.apiKeyCreated'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '1 hour ago', severity: 'info' },
25
- { id: '3', event: 'loginFailed', eventLabel: t('securityLog.loginFailed'), ip: '203.0.113.42', device: 'Firefox / Windows', location: 'Unknown', time: '3 hours ago', severity: 'warning' },
26
- { id: '4', event: 'twoFactorEnabled', eventLabel: t('securityLog.twoFactorEnabled'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '1 day ago', severity: 'info' },
27
- { id: '5', event: 'passwordChanged', eventLabel: t('securityLog.passwordChanged'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '2 days ago', severity: 'info' },
28
- { id: '6', event: 'loginFailed', eventLabel: t('securityLog.loginFailed'), ip: '198.51.100.77', device: 'Safari / iOS', location: 'Beijing, CN', time: '3 days ago', severity: 'critical' },
29
- { id: '7', event: 'sessionRevoked', eventLabel: t('securityLog.sessionRevoked'), ip: '192.168.1.105', device: 'Safari / iOS', location: 'Shanghai, CN', time: '5 days ago', severity: 'info' },
30
- { id: '8', event: 'apiKeyRevoked', eventLabel: t('securityLog.apiKeyRevoked'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '1 week ago', severity: 'warning' },
25
+ { id: '1', event: 'loginSuccess', eventLabel: i18n.t('securityLog.loginSuccess'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '2 minutes ago', severity: 'info' },
26
+ { id: '2', event: 'apiKeyCreated', eventLabel: i18n.t('securityLog.apiKeyCreated'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '1 hour ago', severity: 'info' },
27
+ { id: '3', event: 'loginFailed', eventLabel: i18n.t('securityLog.loginFailed'), ip: '203.0.113.42', device: 'Firefox / Windows', location: 'Unknown', time: '3 hours ago', severity: 'warning' },
28
+ { id: '4', event: 'twoFactorEnabled', eventLabel: i18n.t('securityLog.twoFactorEnabled'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '1 day ago', severity: 'info' },
29
+ { id: '5', event: 'passwordChanged', eventLabel: i18n.t('securityLog.passwordChanged'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '2 days ago', severity: 'info' },
30
+ { id: '6', event: 'loginFailed', eventLabel: i18n.t('securityLog.loginFailed'), ip: '198.51.100.77', device: 'Safari / iOS', location: 'Beijing, CN', time: '3 days ago', severity: 'critical' },
31
+ { id: '7', event: 'sessionRevoked', eventLabel: i18n.t('securityLog.sessionRevoked'), ip: '192.168.1.105', device: 'Safari / iOS', location: 'Shanghai, CN', time: '5 days ago', severity: 'info' },
32
+ { id: '8', event: 'apiKeyRevoked', eventLabel: i18n.t('securityLog.apiKeyRevoked'), ip: '192.168.1.102', device: 'Chrome / macOS', location: 'Shanghai, CN', time: '1 week ago', severity: 'warning' },
31
33
  ];
32
34
 
33
35
  const filteredLogs = $derived(
@@ -46,11 +48,11 @@
46
48
  <div class="space-y-6" data-svadmin-content-page="account">
47
49
  <div class="flex items-start justify-between gap-4">
48
50
  <div>
49
- <h2 class="text-xl font-semibold text-foreground">{t('account.securityLog')}</h2>
50
- <p class="mt-1 text-sm text-muted-foreground">{t('account.securityLogDescription')}</p>
51
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.securityLog')}</h2>
52
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.securityLogDescription')}</p>
51
53
  </div>
52
54
  <Button variant="outline" size="sm">
53
- <Download class="h-3.5 w-3.5 mr-1" />{t('common.export')}
55
+ <Download class="h-3.5 w-3.5 mr-1" />{i18n.t('common.export')}
54
56
  </Button>
55
57
  </div>
56
58
 
@@ -58,7 +60,7 @@
58
60
  <div class="flex flex-wrap gap-3">
59
61
  <div class="relative flex-1 min-w-[200px]">
60
62
  <Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
61
- <Input placeholder={t('common.search')} bind:value={searchQuery} class="pl-9" />
63
+ <Input placeholder={i18n.t('common.search')} bind:value={searchQuery} class="pl-9" />
62
64
  </div>
63
65
  <div class="flex rounded-lg border border-input overflow-hidden">
64
66
  {#each ['all', 'info', 'warning', 'critical'] as sev (sev)}
@@ -66,7 +68,7 @@
66
68
  class="px-3 py-1.5 text-xs font-medium transition-colors capitalize
67
69
  {filterSeverity === sev ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
68
70
  onclick={() => filterSeverity = sev as typeof filterSeverity}
69
- >{sev === 'all' ? t('common.filter') : sev}</button>
71
+ >{sev === 'all' ? i18n.t('common.filter') : sev}</button>
70
72
  {/each}
71
73
  </div>
72
74
  </div>
@@ -78,11 +80,11 @@
78
80
  <table class="w-full">
79
81
  <thead>
80
82
  <tr class="border-b bg-muted/30">
81
- <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{t('securityLog.event')}</th>
82
- <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{t('securityLog.ip')}</th>
83
- <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{t('securityLog.device')}</th>
84
- <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{t('securityLog.location')}</th>
85
- <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{t('securityLog.time')}</th>
83
+ <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{i18n.t('securityLog.event')}</th>
84
+ <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{i18n.t('securityLog.ip')}</th>
85
+ <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{i18n.t('securityLog.device')}</th>
86
+ <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{i18n.t('securityLog.location')}</th>
87
+ <th class="px-4 py-3 text-left text-xs font-semibold text-muted-foreground">{i18n.t('securityLog.time')}</th>
86
88
  </tr>
87
89
  </thead>
88
90
  <tbody class="divide-y">
@@ -115,15 +117,15 @@
115
117
  </div>
116
118
  <div class="grid gap-2 text-xs text-muted-foreground">
117
119
  <div class="flex items-center justify-between gap-3">
118
- <span>{t('securityLog.ip')}</span>
120
+ <span>{i18n.t('securityLog.ip')}</span>
119
121
  <span class="font-mono text-foreground">{log.ip}</span>
120
122
  </div>
121
123
  <div class="flex items-center justify-between gap-3">
122
- <span>{t('securityLog.device')}</span>
124
+ <span>{i18n.t('securityLog.device')}</span>
123
125
  <span class="truncate text-foreground">{log.device}</span>
124
126
  </div>
125
127
  <div class="flex items-center justify-between gap-3">
126
- <span>{t('securityLog.location')}</span>
128
+ <span>{i18n.t('securityLog.location')}</span>
127
129
  <span class="truncate text-foreground">{log.location}</span>
128
130
  </div>
129
131
  </div>
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Input } from '../ui/input/index.js';
@@ -8,6 +8,8 @@
8
8
  import { Badge } from '../ui/badge/index.js';
9
9
  import { Loader2, Shield, Building2, Lock, Globe } from '@lucide/svelte';
10
10
 
11
+ const i18n = useTranslation();
12
+
11
13
  let ssoEnabled = $state(false);
12
14
  let ssoProvider = $state('');
13
15
  let domainRestriction = $state('');
@@ -28,8 +30,8 @@
28
30
  <div class="space-y-6" data-svadmin-content-page="account">
29
31
  <div class="flex items-start justify-between gap-4">
30
32
  <div>
31
- <h2 class="text-xl font-semibold text-foreground">{t('account.settingsEnterprise')}</h2>
32
- <p class="mt-1 text-sm text-muted-foreground">{t('account.settingsEnterpriseDescription')}</p>
33
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.settingsEnterprise')}</h2>
34
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.settingsEnterpriseDescription')}</p>
33
35
  </div>
34
36
  <Badge variant="secondary"><Building2 class="h-3 w-3 mr-1" />Enterprise</Badge>
35
37
  </div>
@@ -123,7 +125,7 @@
123
125
  <div class="flex justify-end">
124
126
  <Button onclick={handleSave} disabled={saving}>
125
127
  {#if saving}<Loader2 class="h-4 w-4 animate-spin mr-2" />{/if}
126
- {t('common.save')}
128
+ {i18n.t('common.save')}
127
129
  </Button>
128
130
  </div>
129
131
  </Card.CardContent>
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Input } from '../ui/input/index.js';
@@ -8,6 +8,8 @@
8
8
  import { Separator } from '../ui/separator/index.js';
9
9
  import { Loader2 } from '@lucide/svelte';
10
10
 
11
+ const i18n = useTranslation();
12
+
11
13
  let displayName = $state('Alex Chen');
12
14
  let email = $state('alex@example.com');
13
15
  let emailNotifications = $state(true);
@@ -24,18 +26,18 @@
24
26
 
25
27
  <div class="space-y-6" data-svadmin-content-page="account">
26
28
  <div>
27
- <h2 class="text-xl font-semibold text-foreground">{t('account.settingsPlain')}</h2>
28
- <p class="mt-1 text-sm text-muted-foreground">{t('account.settingsPlainDescription')}</p>
29
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.settingsPlain')}</h2>
30
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.settingsPlainDescription')}</p>
29
31
  </div>
30
32
 
31
33
  <Card.Card class="border-border/60">
32
34
  <Card.CardHeader>
33
- <Card.CardTitle class="text-base">{t('settings.general')}</Card.CardTitle>
35
+ <Card.CardTitle class="text-base">{i18n.t('settings.general')}</Card.CardTitle>
34
36
  </Card.CardHeader>
35
37
  <Card.CardContent class="space-y-4">
36
38
  <div class="grid gap-4 sm:grid-cols-2">
37
39
  <div class="space-y-2">
38
- <Label for="display-name">{t('profile.name')}</Label>
40
+ <Label for="display-name">{i18n.t('profile.name')}</Label>
39
41
  <Input id="display-name" bind:value={displayName} />
40
42
  </div>
41
43
  <div class="space-y-2">
@@ -48,24 +50,24 @@
48
50
 
49
51
  <div class="flex items-center justify-between gap-4">
50
52
  <div class="space-y-1">
51
- <Label for="email-notif">{t('notifications.emailTitle')}</Label>
52
- <p class="text-xs text-muted-foreground">{t('notifications.emailDescription')}</p>
53
+ <Label for="email-notif">{i18n.t('notifications.emailTitle')}</Label>
54
+ <p class="text-xs text-muted-foreground">{i18n.t('notifications.emailDescription')}</p>
53
55
  </div>
54
56
  <Switch id="email-notif" bind:checked={emailNotifications} />
55
57
  </div>
56
58
 
57
59
  <div class="flex items-center justify-between gap-4">
58
60
  <div class="space-y-1">
59
- <Label for="push-notif">{t('notifications.pushTitle')}</Label>
60
- <p class="text-xs text-muted-foreground">{t('notifications.pushDescription')}</p>
61
+ <Label for="push-notif">{i18n.t('notifications.pushTitle')}</Label>
62
+ <p class="text-xs text-muted-foreground">{i18n.t('notifications.pushDescription')}</p>
61
63
  </div>
62
64
  <Switch id="push-notif" bind:checked={pushNotifications} />
63
65
  </div>
64
66
 
65
67
  <div class="flex items-center justify-between gap-4">
66
68
  <div class="space-y-1">
67
- <Label for="two-factor">{t('security.twoFactorAuth')}</Label>
68
- <p class="text-xs text-muted-foreground">{t('security.twoFactorDescription')}</p>
69
+ <Label for="two-factor">{i18n.t('security.twoFactorAuth')}</Label>
70
+ <p class="text-xs text-muted-foreground">{i18n.t('security.twoFactorDescription')}</p>
69
71
  </div>
70
72
  <Switch id="two-factor" bind:checked={twoFactor} />
71
73
  </div>
@@ -73,7 +75,7 @@
73
75
  <div class="flex justify-end">
74
76
  <Button onclick={handleSave} disabled={saving}>
75
77
  {#if saving}<Loader2 class="h-4 w-4 animate-spin mr-2" />{/if}
76
- {t('common.save')}
78
+ {i18n.t('common.save')}
77
79
  </Button>
78
80
  </div>
79
81
  </Card.CardContent>
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
2
+ import { useTranslation } from '@svadmin/core/i18n';
3
3
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Label } from '../ui/label/index.js';
@@ -7,6 +7,8 @@
7
7
  import { Separator } from '../ui/separator/index.js';
8
8
  import { Loader2, PanelLeft } from '@lucide/svelte';
9
9
 
10
+ const i18n = useTranslation();
11
+
10
12
  let defaultCollapsed = $state(false);
11
13
  let pinSidebar = $state(true);
12
14
  let showLabels = $state(true);
@@ -23,15 +25,15 @@
23
25
 
24
26
  <div class="space-y-6" data-svadmin-content-page="account">
25
27
  <div>
26
- <h2 class="text-xl font-semibold text-foreground">{t('account.settingsSidebar')}</h2>
27
- <p class="mt-1 text-sm text-muted-foreground">{t('account.settingsSidebarDescription')}</p>
28
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.settingsSidebar')}</h2>
29
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.settingsSidebarDescription')}</p>
28
30
  </div>
29
31
 
30
32
  <Card.Card class="border-border/60">
31
33
  <Card.CardHeader>
32
34
  <Card.CardTitle class="flex items-center gap-2 text-base">
33
35
  <PanelLeft class="h-4 w-4 text-muted-foreground" />
34
- {t('account.settingsSidebar')}
36
+ {i18n.t('account.settingsSidebar')}
35
37
  </Card.CardTitle>
36
38
  </Card.CardHeader>
37
39
  <Card.CardContent class="space-y-4">
@@ -47,7 +49,7 @@
47
49
 
48
50
  <div class="flex items-center justify-between gap-4">
49
51
  <div class="space-y-1">
50
- <Label for="pin-sidebar">{t('settings.general')}</Label>
52
+ <Label for="pin-sidebar">{i18n.t('settings.general')}</Label>
51
53
  <p class="text-xs text-muted-foreground">固定侧边栏,防止自动收起</p>
52
54
  </div>
53
55
  <Switch id="pin-sidebar" bind:checked={pinSidebar} />
@@ -72,25 +74,25 @@
72
74
  <Separator />
73
75
 
74
76
  <div class="space-y-2">
75
- <Label>{t('settings.sidebarDensity')}</Label>
77
+ <Label>{i18n.t('settings.sidebarDensity')}</Label>
76
78
  <div class="flex rounded-lg border border-input overflow-hidden">
77
79
  <button
78
80
  class="flex-1 px-4 py-2 text-sm font-medium transition-colors
79
81
  {density === 'compact' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
80
82
  onclick={() => density = 'compact'}
81
- >{t('settings.compact')}</button>
83
+ >{i18n.t('settings.compact')}</button>
82
84
  <button
83
85
  class="flex-1 px-4 py-2 text-sm font-medium transition-colors
84
86
  {density === 'standard' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
85
87
  onclick={() => density = 'standard'}
86
- >{t('settings.standard')}</button>
88
+ >{i18n.t('settings.standard')}</button>
87
89
  </div>
88
90
  </div>
89
91
 
90
92
  <div class="flex justify-end">
91
93
  <Button onclick={handleSave} disabled={saving}>
92
94
  {#if saving}<Loader2 class="h-4 w-4 animate-spin mr-2" />{/if}
93
- {t('common.save')}
95
+ {i18n.t('common.save')}
94
96
  </Button>
95
97
  </div>
96
98
  </Card.CardContent>
@@ -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
  import * as Card from '../ui/card/index.js';
4
4
  import { Button } from '../ui/button/index.js';
5
5
  import { Input } from '../ui/input/index.js';
6
6
  import { Search, UserPlus, MoreHorizontal } from '@lucide/svelte';
7
7
 
8
+ const i18n = useTranslation();
9
+
8
10
  interface TeamMember {
9
11
  id: string;
10
12
  name: string;
@@ -48,26 +50,26 @@
48
50
  const initials = (name: string) => name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
49
51
 
50
52
  function statusLabel(status: TeamMember['status']) {
51
- if (status === 'active') return t('account.active');
52
- if (status === 'invited') return t('account.invited');
53
- return t('account.inactive');
53
+ if (status === 'active') return i18n.t('account.active');
54
+ if (status === 'invited') return i18n.t('account.invited');
55
+ return i18n.t('account.inactive');
54
56
  }
55
57
  </script>
56
58
 
57
59
  <div class="space-y-6" data-svadmin-content-page="account">
58
60
  <div class="flex items-start justify-between gap-4">
59
61
  <div>
60
- <h2 class="text-xl font-semibold text-foreground">{t('account.teamMembers')}</h2>
61
- <p class="mt-1 text-sm text-muted-foreground">{t('account.teamMembersDescription')}</p>
62
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.teamMembers')}</h2>
63
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.teamMembersDescription')}</p>
62
64
  </div>
63
65
  <Button size="sm">
64
- <UserPlus class="h-4 w-4 mr-1" />{t('account.inviteMember')}
66
+ <UserPlus class="h-4 w-4 mr-1" />{i18n.t('account.inviteMember')}
65
67
  </Button>
66
68
  </div>
67
69
 
68
70
  <div class="relative">
69
71
  <Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
70
- <Input placeholder={t('common.search')} bind:value={searchQuery} class="pl-9" />
72
+ <Input placeholder={i18n.t('common.search')} bind:value={searchQuery} class="pl-9" />
71
73
  </div>
72
74
 
73
75
  <!-- Members grid -->