@svadmin/ui 0.38.7 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/app.css +0 -4
  2. package/dist/app.theme.css +0 -4
  3. package/dist/assets/nft/nft-cover-01.jpg +0 -0
  4. package/dist/assets/nft/nft-cover-02.jpg +0 -0
  5. package/dist/assets/nft/nft-cover-03.jpg +0 -0
  6. package/dist/assets/nft/nft-cover-04.jpg +0 -0
  7. package/dist/assets/nft/nft-cover-05.jpg +0 -0
  8. package/dist/assets/nft/nft-cover-06.jpg +0 -0
  9. package/dist/assets/nft/nft-cover-07.jpg +0 -0
  10. package/dist/assets/nft/nft-cover-08.jpg +0 -0
  11. package/dist/assets/nft/nft-cover-09.jpg +0 -0
  12. package/dist/components/AdminApp.svelte +29 -12
  13. package/dist/components/ApiSettings.svelte +86 -1
  14. package/dist/components/AutoTable.svelte +40 -13
  15. package/dist/components/Breadcrumbs.svelte +39 -6
  16. package/dist/components/Breadcrumbs.svelte.d.ts +5 -1
  17. package/dist/components/ChatDialog.svelte +4 -2
  18. package/dist/components/ChatDialog.svelte.d.ts +2 -0
  19. package/dist/components/DevTools.svelte +3 -1
  20. package/dist/components/DevTools.svelte.d.ts +4 -1
  21. package/dist/components/ForgotPasswordPage.svelte +5 -8
  22. package/dist/components/Header.svelte +5 -3
  23. package/dist/components/Header.svelte.d.ts +2 -0
  24. package/dist/components/Layout.svelte +10 -1
  25. package/dist/components/LoginPage.svelte +5 -8
  26. package/dist/components/RegisterPage.svelte +5 -8
  27. package/dist/components/SettingsPage.svelte +14 -2
  28. package/dist/components/Sidebar.svelte +10 -4
  29. package/dist/components/SvadminLogo.svelte +16 -0
  30. package/dist/components/SvadminLogo.svelte.d.ts +7 -0
  31. package/dist/components/SystemPageShell.svelte +34 -0
  32. package/dist/components/SystemPageShell.svelte.d.ts +8 -0
  33. package/dist/components/UpdatePasswordPage.svelte +5 -8
  34. package/dist/components/account/CompanyProfilePage.svelte +56 -1
  35. package/dist/components/account/ImportMembersPage.svelte +31 -0
  36. package/dist/components/account/SettingsEnterprisePage.svelte +172 -19
  37. package/dist/components/account/SettingsPlainPage.svelte +121 -17
  38. package/dist/components/account/SettingsSidebarPage.svelte +282 -70
  39. package/dist/components/account/TeamMembersPage.svelte +95 -4
  40. package/dist/components/account/UserProfilePage.svelte +206 -0
  41. package/dist/components/account/UserProfilePage.svelte.d.ts +4 -0
  42. package/dist/components/network/TeamCrewTablePage.svelte +37 -5
  43. package/dist/components/network/UserCardsNFTPage.svelte +26 -16
  44. package/dist/components/profile/ActivityTimeline.svelte +24 -2
  45. package/dist/components/profile/ActivityTimeline.svelte.d.ts +1 -0
  46. package/dist/components/profile/ProfileVariantSections.svelte +452 -0
  47. package/dist/components/profile/ProfileVariantSections.svelte.d.ts +8 -0
  48. package/dist/components/profile/ProjectsGrid.svelte +38 -8
  49. package/dist/components/profile/ProjectsGrid.svelte.d.ts +1 -0
  50. package/dist/components/profile/PublicProfilePage.svelte +47 -26
  51. package/dist/components/profile/PublicProfilePage.svelte.d.ts +1 -0
  52. package/dist/components/profile/TeamsShowcase.svelte +31 -12
  53. package/dist/components/profile/TeamsShowcase.svelte.d.ts +1 -0
  54. package/dist/components/ui/alert/alert.svelte.d.ts +14 -14
  55. package/dist/components/ui/avatar/avatar.svelte.d.ts +14 -14
  56. package/dist/components/ui/badge/badge.svelte.d.ts +20 -20
  57. package/dist/components/ui/button/button.svelte.d.ts +44 -44
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.js +3 -0
  60. package/dist/router-state.svelte.js +4 -0
  61. package/package.json +15 -15
@@ -6,10 +6,33 @@
6
6
  import { Label } from '../ui/label/index.js';
7
7
  import { Switch } from '../ui/switch/index.js';
8
8
  import { Badge } from '../ui/badge/index.js';
9
- import { Loader2, Shield, Building2, Lock, Globe } from '@lucide/svelte';
9
+ import { Loader2, Shield, Building2, Lock, Globe, Calendar, Link2, Database } from '@lucide/svelte';
10
10
 
11
11
  const i18n = useTranslation();
12
12
 
13
+ const trustedDevices = [
14
+ { name: 'MacBook Pro 16"', detail: 'macOS · Shanghai, CN', lastSeen: '2026-08-04 09:12', current: true },
15
+ { name: 'iPhone 17', detail: 'iOS · Shanghai, CN', lastSeen: '2026-08-03 22:41', current: false },
16
+ { name: 'Windows Workstation', detail: 'Windows · Beijing, CN', lastSeen: '2026-07-28 18:05', current: false },
17
+ ];
18
+
19
+ const paymentHistory = [
20
+ { date: '2026-08-01', type: 'Enterprise · Monthly', amount: '$299.00' },
21
+ { date: '2026-07-01', type: 'Enterprise · Monthly', amount: '$299.00' },
22
+ { date: '2026-06-01', type: 'Enterprise · Monthly', amount: '$299.00' },
23
+ ];
24
+
25
+ const calendarAccounts = [
26
+ { name: 'Work Calendar', account: 'alex@acme.com', active: true },
27
+ { name: 'Personal Calendar', account: 'alex@example.com', active: false },
28
+ ];
29
+
30
+ const connections = [
31
+ { name: 'Source Control', account: 'alexchen', linked: true },
32
+ { name: 'ChatOps', account: '@alex.chen', linked: true },
33
+ { name: 'Cloud Drive', account: 'alex@acme.com', linked: false },
34
+ ];
35
+
13
36
  let ssoEnabled = $state(false);
14
37
  let ssoProvider = $state('');
15
38
  let domainRestriction = $state('');
@@ -41,26 +64,26 @@
41
64
  <Card.CardHeader>
42
65
  <Card.CardTitle class="flex items-center gap-2 text-base">
43
66
  <Shield class="h-4 w-4 text-muted-foreground" />
44
- 单点登录 (SSO)
67
+ {i18n.t('account.singleSignOn')}
45
68
  </Card.CardTitle>
46
- <Card.CardDescription>配置企业身份提供商以实现统一登录体验。</Card.CardDescription>
69
+ <Card.CardDescription>{i18n.t('account.ssoDescription')}</Card.CardDescription>
47
70
  </Card.CardHeader>
48
71
  <Card.CardContent class="space-y-4">
49
72
  <div class="flex items-center justify-between gap-4">
50
73
  <div class="space-y-1">
51
- <Label for="sso-enabled">启用 SSO</Label>
52
- <p class="text-xs text-muted-foreground">允许员工通过企业 IdP 登录</p>
74
+ <Label for="sso-enabled">{i18n.t('account.enableSso')}</Label>
75
+ <p class="text-xs text-muted-foreground">{i18n.t('account.enableSsoDesc')}</p>
53
76
  </div>
54
77
  <Switch id="sso-enabled" bind:checked={ssoEnabled} />
55
78
  </div>
56
79
  {#if ssoEnabled}
57
80
  <div class="grid gap-4 sm:grid-cols-2">
58
81
  <div class="space-y-2">
59
- <Label for="sso-provider">SSO 提供商</Label>
82
+ <Label for="sso-provider">{i18n.t('account.ssoProvider')}</Label>
60
83
  <Input id="sso-provider" bind:value={ssoProvider} placeholder="Okta, Azure AD, etc." />
61
84
  </div>
62
85
  <div class="space-y-2">
63
- <Label for="domain-restriction">域名限制</Label>
86
+ <Label for="domain-restriction">{i18n.t('account.domainRestriction')}</Label>
64
87
  <Input id="domain-restriction" bind:value={domainRestriction} placeholder="@company.com" />
65
88
  </div>
66
89
  </div>
@@ -73,29 +96,29 @@
73
96
  <Card.CardHeader>
74
97
  <Card.CardTitle class="flex items-center gap-2 text-base">
75
98
  <Lock class="h-4 w-4 text-muted-foreground" />
76
- 安全策略
99
+ {i18n.t('account.securityPolicy')}
77
100
  </Card.CardTitle>
78
101
  </Card.CardHeader>
79
102
  <Card.CardContent class="space-y-4">
80
103
  <div class="grid gap-4 sm:grid-cols-2">
81
104
  <div class="space-y-2">
82
- <Label for="password-policy">密码策略</Label>
105
+ <Label for="password-policy">{i18n.t('account.passwordPolicy')}</Label>
83
106
  <select id="password-policy" bind:value={passwordPolicy} class="flex h-9 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring">
84
- <option value="basic">基本</option>
85
- <option value="strong">强密码</option>
86
- <option value="very-strong">非常强</option>
107
+ <option value="basic">{i18n.t('account.policyBasic')}</option>
108
+ <option value="strong">{i18n.t('account.policyStrong')}</option>
109
+ <option value="very-strong">{i18n.t('account.policyVeryStrong')}</option>
87
110
  </select>
88
111
  </div>
89
112
  <div class="space-y-2">
90
- <Label for="session-timeout">会话超时(分钟)</Label>
113
+ <Label for="session-timeout">{i18n.t('account.sessionTimeout')}</Label>
91
114
  <Input id="session-timeout" bind:value={sessionTimeout} type="number" />
92
115
  </div>
93
116
  </div>
94
117
 
95
118
  <div class="space-y-2">
96
- <Label for="ip-whitelist">IP 白名单</Label>
119
+ <Label for="ip-whitelist">{i18n.t('account.ipWhitelist')}</Label>
97
120
  <Input id="ip-whitelist" bind:value={ipWhitelist} placeholder="10.0.0.0/8, 192.168.1.0/24" />
98
- <p class="text-xs text-muted-foreground">留空表示不限制 IP</p>
121
+ <p class="text-xs text-muted-foreground">{i18n.t('account.ipWhitelistHint')}</p>
99
122
  </div>
100
123
  </Card.CardContent>
101
124
  </Card.Card>
@@ -105,20 +128,20 @@
105
128
  <Card.CardHeader>
106
129
  <Card.CardTitle class="flex items-center gap-2 text-base">
107
130
  <Globe class="h-4 w-4 text-muted-foreground" />
108
- 数据与合规
131
+ {i18n.t('account.dataCompliance')}
109
132
  </Card.CardTitle>
110
133
  </Card.CardHeader>
111
134
  <Card.CardContent class="space-y-4">
112
135
  <div class="flex items-center justify-between gap-4">
113
136
  <div class="space-y-1">
114
- <Label for="audit-logging">审计日志</Label>
115
- <p class="text-xs text-muted-foreground">记录所有管理操作以备合规审查</p>
137
+ <Label for="audit-logging">{i18n.t('account.auditLogging')}</Label>
138
+ <p class="text-xs text-muted-foreground">{i18n.t('account.auditLoggingDesc')}</p>
116
139
  </div>
117
140
  <Switch id="audit-logging" bind:checked={auditLogging} />
118
141
  </div>
119
142
 
120
143
  <div class="space-y-2">
121
- <Label for="data-retention">数据保留天数</Label>
144
+ <Label for="data-retention">{i18n.t('account.dataRetention')}</Label>
122
145
  <Input id="data-retention" bind:value={dataRetention} type="number" />
123
146
  </div>
124
147
 
@@ -130,4 +153,134 @@
130
153
  </div>
131
154
  </Card.CardContent>
132
155
  </Card.Card>
156
+
157
+ <div class="grid gap-4 lg:grid-cols-2">
158
+ <!-- Trusted Devices -->
159
+ <Card.Card class="border-border/60">
160
+ <Card.CardHeader>
161
+ <Card.CardTitle class="text-base">{i18n.t('account.trustedDevices')}</Card.CardTitle>
162
+ </Card.CardHeader>
163
+ <Card.CardContent class="p-0">
164
+ <div class="divide-y">
165
+ {#each trustedDevices as device (device.name)}
166
+ <div class="flex items-center justify-between gap-3 px-6 py-3">
167
+ <div class="min-w-0">
168
+ <p class="truncate text-sm font-medium text-foreground">{device.name}</p>
169
+ <p class="truncate text-xs text-muted-foreground">{device.detail} · {device.lastSeen}</p>
170
+ </div>
171
+ {#if device.current}
172
+ <Badge variant="secondary" class="shrink-0 text-[10px]">{i18n.t('security.currentSession')}</Badge>
173
+ {/if}
174
+ </div>
175
+ {/each}
176
+ </div>
177
+ </Card.CardContent>
178
+ </Card.Card>
179
+
180
+ <!-- Current Plan + Payment History -->
181
+ <div class="space-y-4">
182
+ <Card.Card class="border-primary/30 bg-primary/5">
183
+ <Card.CardHeader class="pb-3">
184
+ <div class="flex items-center justify-between">
185
+ <Card.CardTitle class="text-base">{i18n.t('account.currentPlan')}</Card.CardTitle>
186
+ <Badge variant="secondary">Enterprise</Badge>
187
+ </div>
188
+ </Card.CardHeader>
189
+ <Card.CardContent>
190
+ <div class="flex items-baseline gap-1">
191
+ <span class="text-2xl font-bold text-foreground">$299</span>
192
+ <span class="text-sm text-muted-foreground">{i18n.t('account.perMonth')}</span>
193
+ </div>
194
+ <p class="mt-1 text-xs text-muted-foreground">Unlimited seats · SSO · Audit retention 365d</p>
195
+ </Card.CardContent>
196
+ </Card.Card>
197
+
198
+ <Card.Card class="border-border/60">
199
+ <Card.CardHeader class="pb-3">
200
+ <Card.CardTitle class="text-base">{i18n.t('account.paymentHistory')}</Card.CardTitle>
201
+ </Card.CardHeader>
202
+ <Card.CardContent class="p-0">
203
+ <div class="divide-y">
204
+ <div class="grid grid-cols-3 px-6 py-2 text-xs font-medium text-muted-foreground">
205
+ <span>{i18n.t('account.date')}</span>
206
+ <span>{i18n.t('account.type')}</span>
207
+ <span class="text-right">{i18n.t('account.amount')}</span>
208
+ </div>
209
+ {#each paymentHistory as row (row.date)}
210
+ <div class="grid grid-cols-3 px-6 py-2.5 text-sm">
211
+ <span class="text-muted-foreground">{row.date}</span>
212
+ <span class="text-foreground">{row.type}</span>
213
+ <span class="text-right font-medium text-foreground">{row.amount}</span>
214
+ </div>
215
+ {/each}
216
+ </div>
217
+ </Card.CardContent>
218
+ </Card.Card>
219
+ </div>
220
+ </div>
221
+
222
+ <div class="grid gap-4 lg:grid-cols-3">
223
+ <!-- Calendar Accounts -->
224
+ <Card.Card class="border-border/60">
225
+ <Card.CardHeader>
226
+ <Card.CardTitle class="flex items-center gap-2 text-base">
227
+ <Calendar class="h-4 w-4 text-muted-foreground" />{i18n.t('account.calendarAccounts')}
228
+ </Card.CardTitle>
229
+ </Card.CardHeader>
230
+ <Card.CardContent class="space-y-3">
231
+ {#each calendarAccounts as cal (cal.account)}
232
+ <div class="flex items-center justify-between gap-3">
233
+ <div class="min-w-0">
234
+ <p class="truncate text-sm font-medium text-foreground">{cal.name}</p>
235
+ <p class="truncate text-xs text-muted-foreground">{cal.account}</p>
236
+ </div>
237
+ <Badge variant={cal.active ? 'secondary' : 'outline'} class="shrink-0 text-[10px]">
238
+ {cal.active ? i18n.t('account.active') : i18n.t('account.inactive')}
239
+ </Badge>
240
+ </div>
241
+ {/each}
242
+ </Card.CardContent>
243
+ </Card.Card>
244
+
245
+ <!-- Connections -->
246
+ <Card.Card class="border-border/60">
247
+ <Card.CardHeader>
248
+ <Card.CardTitle class="flex items-center gap-2 text-base">
249
+ <Link2 class="h-4 w-4 text-muted-foreground" />{i18n.t('account.connections')}
250
+ </Card.CardTitle>
251
+ </Card.CardHeader>
252
+ <Card.CardContent class="space-y-3">
253
+ {#each connections as conn (conn.name)}
254
+ <div class="flex items-center justify-between gap-3">
255
+ <div class="min-w-0">
256
+ <p class="truncate text-sm font-medium text-foreground">{conn.name}</p>
257
+ <p class="truncate text-xs text-muted-foreground">{conn.account}</p>
258
+ </div>
259
+ <Badge variant={conn.linked ? 'secondary' : 'outline'} class="shrink-0 text-[10px]">
260
+ {conn.linked ? i18n.t('integrations.statusConnected') : i18n.t('integrations.statusDisconnected')}
261
+ </Badge>
262
+ </div>
263
+ {/each}
264
+ </Card.CardContent>
265
+ </Card.Card>
266
+
267
+ <!-- Data import -->
268
+ <Card.Card class="border-border/60">
269
+ <Card.CardHeader>
270
+ <Card.CardTitle class="flex items-center gap-2 text-base">
271
+ <Database class="h-4 w-4 text-muted-foreground" />{i18n.t('account.dataImport')}
272
+ </Card.CardTitle>
273
+ <Card.CardDescription>{i18n.t('account.dataImportDescription')}</Card.CardDescription>
274
+ </Card.CardHeader>
275
+ <Card.CardContent>
276
+ <div class="flex items-center justify-between gap-3 rounded-lg border border-border/60 p-3">
277
+ <div class="min-w-0">
278
+ <p class="truncate text-sm font-medium text-foreground">Google Analytics</p>
279
+ <p class="truncate text-xs text-muted-foreground">analytics.google.com</p>
280
+ </div>
281
+ <Button size="sm" variant="outline">{i18n.t('network.connect')}</Button>
282
+ </div>
283
+ </Card.CardContent>
284
+ </Card.Card>
285
+ </div>
133
286
  </div>
@@ -6,35 +6,67 @@
6
6
  import { Label } from '../ui/label/index.js';
7
7
  import { Switch } from '../ui/switch/index.js';
8
8
  import { Separator } from '../ui/separator/index.js';
9
- import { Loader2 } from '@lucide/svelte';
9
+ import { Loader2, Camera, Trash2 } from '@lucide/svelte';
10
10
 
11
11
  const i18n = useTranslation();
12
12
 
13
13
  let displayName = $state('Alex Chen');
14
14
  let email = $state('alex@example.com');
15
+ let phone = $state('+86 138 **** 9527');
16
+ let dateOfBirth = $state('1995-04-18');
17
+ let bio = $state('Full-stack developer focused on design systems and developer tooling.');
18
+ let country = $state('China');
19
+ let region = $state('Shanghai');
20
+ let city = $state('Shanghai');
21
+ let postcode = $state('200120');
22
+ let currentPassword = $state('');
23
+ let newPassword = $state('');
24
+ let confirmPassword = $state('');
15
25
  let emailNotifications = $state(true);
16
- let pushNotifications = $state(true);
17
26
  let twoFactor = $state(false);
27
+ let confirmDelete = $state(false);
18
28
  let saving = $state(false);
19
29
 
20
30
  async function handleSave() {
21
31
  saving = true;
22
- await new Promise(r => setTimeout(r, 800));
32
+ await new Promise((r) => setTimeout(r, 800));
23
33
  saving = false;
24
34
  }
25
35
  </script>
26
36
 
27
37
  <div class="space-y-6" data-svadmin-content-page="account">
28
- <div>
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>
38
+ <div class="flex items-start justify-between gap-4">
39
+ <div>
40
+ <h2 class="text-xl font-semibold text-foreground">{i18n.t('account.settingsPlain')}</h2>
41
+ <p class="mt-1 text-sm text-muted-foreground">{i18n.t('account.settingsPlainDescription')}</p>
42
+ </div>
43
+ <Button onclick={handleSave} disabled={saving} size="sm">
44
+ {#if saving}<Loader2 class="h-4 w-4 animate-spin mr-2" />{/if}
45
+ {i18n.t('common.save')}
46
+ </Button>
31
47
  </div>
32
48
 
49
+ <!-- General Settings -->
33
50
  <Card.Card class="border-border/60">
34
51
  <Card.CardHeader>
35
52
  <Card.CardTitle class="text-base">{i18n.t('settings.general')}</Card.CardTitle>
36
53
  </Card.CardHeader>
37
54
  <Card.CardContent class="space-y-4">
55
+ <div class="flex items-center gap-4">
56
+ <div class="relative">
57
+ <div class="flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 text-primary text-xl font-bold">AC</div>
58
+ <button class="absolute -bottom-1 -right-1 flex h-6 w-6 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm">
59
+ <Camera class="h-3 w-3" />
60
+ </button>
61
+ </div>
62
+ <div>
63
+ <p class="text-sm font-medium text-foreground">{i18n.t('account.photo')}</p>
64
+ <p class="text-xs text-muted-foreground">150x150px JPEG, PNG Image</p>
65
+ </div>
66
+ </div>
67
+
68
+ <Separator />
69
+
38
70
  <div class="grid gap-4 sm:grid-cols-2">
39
71
  <div class="space-y-2">
40
72
  <Label for="display-name">{i18n.t('profile.name')}</Label>
@@ -44,6 +76,24 @@
44
76
  <Label for="email">Email</Label>
45
77
  <Input id="email" bind:value={email} type="email" />
46
78
  </div>
79
+ <div class="space-y-2">
80
+ <Label for="phone">{i18n.t('account.phone')}</Label>
81
+ <Input id="phone" bind:value={phone} />
82
+ </div>
83
+ <div class="space-y-2">
84
+ <Label for="dob">{i18n.t('account.dateOfBirth')}</Label>
85
+ <Input id="dob" bind:value={dateOfBirth} type="date" />
86
+ </div>
87
+ </div>
88
+
89
+ <div class="space-y-2">
90
+ <Label for="bio">{i18n.t('account.bio')}</Label>
91
+ <textarea
92
+ id="bio"
93
+ bind:value={bio}
94
+ rows={3}
95
+ class="flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
96
+ ></textarea>
47
97
  </div>
48
98
 
49
99
  <Separator />
@@ -58,24 +108,78 @@
58
108
 
59
109
  <div class="flex items-center justify-between gap-4">
60
110
  <div class="space-y-1">
61
- <Label for="push-notif">{i18n.t('notifications.pushTitle')}</Label>
62
- <p class="text-xs text-muted-foreground">{i18n.t('notifications.pushDescription')}</p>
111
+ <Label for="two-factor">{i18n.t('account.twoFactorAuth')}</Label>
112
+ <p class="text-xs text-muted-foreground">{i18n.t('security.twoFactorDescription')}</p>
63
113
  </div>
64
- <Switch id="push-notif" bind:checked={pushNotifications} />
114
+ <Switch id="two-factor" bind:checked={twoFactor} />
65
115
  </div>
116
+ </Card.CardContent>
117
+ </Card.Card>
66
118
 
67
- <div class="flex items-center justify-between gap-4">
68
- <div class="space-y-1">
69
- <Label for="two-factor">{i18n.t('security.twoFactorAuth')}</Label>
70
- <p class="text-xs text-muted-foreground">{i18n.t('security.twoFactorDescription')}</p>
119
+ <!-- Address -->
120
+ <Card.Card class="border-border/60">
121
+ <Card.CardHeader>
122
+ <Card.CardTitle class="text-base">{i18n.t('account.address')}</Card.CardTitle>
123
+ </Card.CardHeader>
124
+ <Card.CardContent>
125
+ <div class="grid gap-4 sm:grid-cols-2">
126
+ <div class="space-y-2">
127
+ <Label for="country">{i18n.t('account.country')}</Label>
128
+ <Input id="country" bind:value={country} />
129
+ </div>
130
+ <div class="space-y-2">
131
+ <Label for="state">{i18n.t('account.state')}</Label>
132
+ <Input id="state" bind:value={region} />
133
+ </div>
134
+ <div class="space-y-2">
135
+ <Label for="city">{i18n.t('account.city')}</Label>
136
+ <Input id="city" bind:value={city} />
137
+ </div>
138
+ <div class="space-y-2">
139
+ <Label for="postcode">{i18n.t('account.postcode')}</Label>
140
+ <Input id="postcode" bind:value={postcode} />
71
141
  </div>
72
- <Switch id="two-factor" bind:checked={twoFactor} />
73
142
  </div>
143
+ </Card.CardContent>
144
+ </Card.Card>
74
145
 
146
+ <!-- Password -->
147
+ <Card.Card class="border-border/60">
148
+ <Card.CardHeader>
149
+ <Card.CardTitle class="text-base">{i18n.t('account.password')}</Card.CardTitle>
150
+ </Card.CardHeader>
151
+ <Card.CardContent>
152
+ <div class="grid gap-4 sm:grid-cols-3">
153
+ <div class="space-y-2">
154
+ <Label for="current-password">{i18n.t('profile.currentPassword')}</Label>
155
+ <Input id="current-password" bind:value={currentPassword} type="password" />
156
+ </div>
157
+ <div class="space-y-2">
158
+ <Label for="new-password">{i18n.t('profile.newPassword')}</Label>
159
+ <Input id="new-password" bind:value={newPassword} type="password" />
160
+ </div>
161
+ <div class="space-y-2">
162
+ <Label for="confirm-password">{i18n.t('auth.confirmPassword')}</Label>
163
+ <Input id="confirm-password" bind:value={confirmPassword} type="password" />
164
+ </div>
165
+ </div>
166
+ </Card.CardContent>
167
+ </Card.Card>
168
+
169
+ <!-- Delete Account -->
170
+ <Card.Card class="border-destructive/30">
171
+ <Card.CardHeader>
172
+ <Card.CardTitle class="text-base text-destructive">{i18n.t('account.deleteAccount')}</Card.CardTitle>
173
+ <Card.CardDescription>{i18n.t('account.deleteAccountDescription')}</Card.CardDescription>
174
+ </Card.CardHeader>
175
+ <Card.CardContent class="space-y-4">
176
+ <div class="flex items-center justify-between gap-4">
177
+ <Label for="confirm-delete" class="text-sm font-normal">{i18n.t('account.confirmDeleteAccount')}</Label>
178
+ <Switch id="confirm-delete" bind:checked={confirmDelete} />
179
+ </div>
75
180
  <div class="flex justify-end">
76
- <Button onclick={handleSave} disabled={saving}>
77
- {#if saving}<Loader2 class="h-4 w-4 animate-spin mr-2" />{/if}
78
- {i18n.t('common.save')}
181
+ <Button variant="destructive" size="sm" disabled={!confirmDelete}>
182
+ <Trash2 class="h-4 w-4 mr-1" />{i18n.t('account.deleteAccount')}
79
183
  </Button>
80
184
  </div>
81
185
  </Card.CardContent>