@svadmin/ui 0.73.0 → 0.75.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 (303) hide show
  1. package/README.md +53 -28
  2. package/dist/app.css +3733 -4119
  3. package/dist/app.theme.css +3733 -4119
  4. package/dist/component-registry.d.ts +14977 -0
  5. package/dist/component-registry.js +216 -0
  6. package/dist/components/AboutSettings.svelte +7 -1
  7. package/dist/components/AboutSettings.svelte.d.ts +4 -1
  8. package/dist/components/ActivityFeed.svelte +9 -3
  9. package/dist/components/ActivityFeed.svelte.d.ts +3 -0
  10. package/dist/components/AdminApp.svelte +24 -15
  11. package/dist/components/AdminApp.svelte.d.ts +6 -0
  12. package/dist/components/ApiSettings.svelte +41 -5
  13. package/dist/components/ApprovalCenter.svelte +283 -0
  14. package/dist/components/ApprovalCenter.svelte.d.ts +10 -0
  15. package/dist/components/AuditLogViewer.svelte +3 -1
  16. package/dist/components/AutoForm.svelte +33 -3
  17. package/dist/components/AutoForm.svelte.d.ts +1 -0
  18. package/dist/components/AutoTable.svelte +610 -47
  19. package/dist/components/AutoTable.svelte.d.ts +17 -1
  20. package/dist/components/BoundRecordDetailDrawer.svelte +9 -0
  21. package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +2 -0
  22. package/dist/components/Breadcrumbs.svelte +11 -1
  23. package/dist/components/CancelTaskButton.svelte +12 -42
  24. package/dist/components/ColorPicker.svelte +73 -0
  25. package/dist/components/ColorPicker.svelte.d.ts +15 -0
  26. package/dist/components/ComboboxField.svelte +114 -27
  27. package/dist/components/ComboboxField.svelte.d.ts +1 -0
  28. package/dist/components/DashboardView.svelte +99 -0
  29. package/dist/components/DashboardView.svelte.d.ts +14 -0
  30. package/dist/components/DatePicker.svelte +10 -0
  31. package/dist/components/DatePicker.svelte.d.ts +6 -0
  32. package/dist/components/DateRangeInput.svelte +85 -5
  33. package/dist/components/DateRangeInput.svelte.d.ts +10 -0
  34. package/dist/components/DateRangePicker.svelte +10 -0
  35. package/dist/components/DateRangePicker.svelte.d.ts +6 -0
  36. package/dist/components/DateTimeInput.svelte +85 -16
  37. package/dist/components/DateTimeInput.svelte.d.ts +19 -11
  38. package/dist/components/DateTimePicker.svelte +10 -0
  39. package/dist/components/DateTimePicker.svelte.d.ts +6 -0
  40. package/dist/components/DevTools.svelte +34 -26
  41. package/dist/components/DevframePanel.svelte +123 -0
  42. package/dist/components/DevframePanel.svelte.d.ts +3 -0
  43. package/dist/components/ErrorSummary.svelte +52 -0
  44. package/dist/components/ErrorSummary.svelte.d.ts +14 -0
  45. package/dist/components/FieldRenderer.svelte +82 -81
  46. package/dist/components/FileUpload.svelte +214 -52
  47. package/dist/components/FileUpload.svelte.d.ts +11 -0
  48. package/dist/components/FilterBuilder.svelte +88 -66
  49. package/dist/components/FilterCollectionInput.svelte +80 -0
  50. package/dist/components/FilterCollectionInput.svelte.d.ts +10 -0
  51. package/dist/components/GanttChart.svelte +66 -14
  52. package/dist/components/GanttChart.svelte.d.ts +6 -0
  53. package/dist/components/Header.svelte +18 -2
  54. package/dist/components/ImportWizard.svelte +339 -17
  55. package/dist/components/ImportWizard.svelte.d.ts +11 -0
  56. package/dist/components/JsonSchemaForm.svelte +120 -52
  57. package/dist/components/JsonSchemaForm.svelte.d.ts +7 -1
  58. package/dist/components/KanbanBoard.svelte +195 -40
  59. package/dist/components/KanbanBoard.svelte.d.ts +9 -2
  60. package/dist/components/Layout.svelte +4 -2
  61. package/dist/components/LazyAutoForm.svelte +28 -0
  62. package/dist/components/LazyAutoForm.svelte.d.ts +8 -0
  63. package/dist/components/LazyAutoTable.svelte +28 -0
  64. package/dist/components/LazyAutoTable.svelte.d.ts +8 -0
  65. package/dist/components/LazyPage.svelte +3 -3
  66. package/dist/components/LazyPage.svelte.d.ts +2 -2
  67. package/dist/components/LazySettingsPage.svelte +28 -0
  68. package/dist/components/LazySettingsPage.svelte.d.ts +8 -0
  69. package/dist/components/LazyShowPage.svelte +28 -0
  70. package/dist/components/LazyShowPage.svelte.d.ts +8 -0
  71. package/dist/components/ListPage.svelte +5 -1
  72. package/dist/components/ListPage.svelte.d.ts +3 -0
  73. package/dist/components/LoginPage.svelte +1 -1
  74. package/dist/components/MultiTabKeepAlive.svelte +11 -6
  75. package/dist/components/NotificationsSettings.svelte +69 -2
  76. package/dist/components/NotificationsSettings.svelte.d.ts +25 -1
  77. package/dist/components/PasswordInput.svelte +3 -3
  78. package/dist/components/PasswordInput.svelte.d.ts +2 -1
  79. package/dist/components/PermissionMatrix.svelte +20 -1
  80. package/dist/components/PivotExportButton.svelte +157 -0
  81. package/dist/components/PivotExportButton.svelte.d.ts +16 -0
  82. package/dist/components/PivotTable.svelte +229 -74
  83. package/dist/components/PivotTable.svelte.d.ts +19 -2
  84. package/dist/components/RadioGroup.svelte +121 -0
  85. package/dist/components/RadioGroup.svelte.d.ts +43 -0
  86. package/dist/components/RangeSlider.svelte +127 -0
  87. package/dist/components/RangeSlider.svelte.d.ts +22 -0
  88. package/dist/components/Rate.svelte +121 -0
  89. package/dist/components/Rate.svelte.d.ts +16 -0
  90. package/dist/components/RecordDetailDrawer.svelte +4 -2
  91. package/dist/components/RecordDetailDrawer.svelte.d.ts +2 -0
  92. package/dist/components/RelationPicker.svelte +341 -0
  93. package/dist/components/RelationPicker.svelte.d.ts +26 -0
  94. package/dist/components/ResourceOperationsPage.svelte +23 -19
  95. package/dist/components/ResourceOperationsPage.svelte.d.ts +2 -2
  96. package/dist/components/RetryTaskButton.svelte +12 -42
  97. package/dist/components/SecuritySettings.svelte +29 -5
  98. package/dist/components/SegmentedControl.svelte +145 -0
  99. package/dist/components/SegmentedControl.svelte.d.ts +38 -0
  100. package/dist/components/SettingsPage.svelte +3 -1
  101. package/dist/components/SettingsPage.svelte.d.ts +1 -0
  102. package/dist/components/ShowPage.svelte +18 -3
  103. package/dist/components/ShowPage.svelte.d.ts +3 -0
  104. package/dist/components/Sidebar.svelte +2 -2
  105. package/dist/components/Slider.svelte +100 -0
  106. package/dist/components/Slider.svelte.d.ts +19 -0
  107. package/dist/components/SpreadsheetView.svelte +1213 -95
  108. package/dist/components/SpreadsheetView.svelte.d.ts +19 -0
  109. package/dist/components/TagsInput.svelte +178 -0
  110. package/dist/components/TagsInput.svelte.d.ts +17 -0
  111. package/dist/components/TimePicker.svelte +10 -0
  112. package/dist/components/TimePicker.svelte.d.ts +6 -0
  113. package/dist/components/TooltipButton.svelte +3 -4
  114. package/dist/components/TreeSelect.svelte +267 -53
  115. package/dist/components/TreeSelect.svelte.d.ts +3 -0
  116. package/dist/components/TreeTable.svelte +484 -82
  117. package/dist/components/TreeTable.svelte.d.ts +24 -1
  118. package/dist/components/TwoFactorAuthPage.svelte +85 -48
  119. package/dist/components/TwoFactorAuthPage.svelte.d.ts +12 -1
  120. package/dist/components/VirtualTable.svelte +116 -20
  121. package/dist/components/VirtualTable.svelte.d.ts +5 -0
  122. package/dist/components/account/CompanyProfilePage.svelte +74 -38
  123. package/dist/components/account/GetStartedPage.svelte +2 -1
  124. package/dist/components/account/ImportMembersPage.svelte +22 -167
  125. package/dist/components/account/ImportMembersPage.svelte.d.ts +4 -1
  126. package/dist/components/account/MemberDirectory.svelte +156 -0
  127. package/dist/components/account/MemberDirectory.svelte.d.ts +19 -0
  128. package/dist/components/account/MembersStarterPage.svelte +9 -55
  129. package/dist/components/account/MembersStarterPage.svelte.d.ts +5 -1
  130. package/dist/components/account/SecurityLogPage.svelte +4 -1
  131. package/dist/components/account/SettingsPlainPage.svelte +7 -38
  132. package/dist/components/account/SettingsPlainPage.svelte.d.ts +4 -1
  133. package/dist/components/account/SettingsSidebarPage.svelte +7 -20
  134. package/dist/components/account/TeamMembersPage.svelte +4 -20
  135. package/dist/components/account/TeamMembersPage.svelte.d.ts +5 -1
  136. package/dist/components/account/UserProfilePage.svelte +3 -25
  137. package/dist/components/account/UserProfilePage.svelte.d.ts +17 -2
  138. package/dist/components/buttons/ExportButton.svelte +56 -12
  139. package/dist/components/buttons/ExportButton.svelte.d.ts +11 -0
  140. package/dist/components/content/ContentPageHeader.svelte +1 -1
  141. package/dist/components/content/ContentPageShell.svelte +1 -1
  142. package/dist/components/content/MetricBlock.svelte +1 -1
  143. package/dist/components/content/PageToolbar.svelte +1 -1
  144. package/dist/components/content/ReferenceNotice.svelte +5 -0
  145. package/dist/components/content/ReferenceNotice.svelte.d.ts +18 -0
  146. package/dist/components/content/SectionHeader.svelte +1 -1
  147. package/dist/components/content/SettingsFieldRow.svelte +1 -1
  148. package/dist/components/content/SettingsGroup.svelte +1 -1
  149. package/dist/components/content/StatusBadge.svelte +1 -1
  150. package/dist/components/content/WorkspaceLayout.svelte +1 -1
  151. package/dist/components/export-reference.d.ts +2 -0
  152. package/dist/components/export-reference.js +9 -0
  153. package/dist/components/fieldComponentMap.js +2 -5
  154. package/dist/components/fields/DateField.svelte +12 -13
  155. package/dist/components/fields/MarkdownField.svelte +14 -5
  156. package/dist/components/fields/date-display.d.ts +6 -0
  157. package/dist/components/fields/date-display.js +24 -0
  158. package/dist/components/network/TeamCrewTablePage.svelte +6 -1
  159. package/dist/components/network/UserCardsNFTPage.svelte +2 -0
  160. package/dist/components/profile/PublicProfilePage.svelte +2 -0
  161. package/dist/components/saved-list-views.d.ts +74 -1
  162. package/dist/components/saved-list-views.js +191 -3
  163. package/dist/components/table-contract.d.ts +12 -1
  164. package/dist/components/task-action.svelte.d.ts +13 -0
  165. package/dist/components/task-action.svelte.js +73 -0
  166. package/dist/components/ui/DesignSystemShowcase.svelte +164 -0
  167. package/dist/components/ui/DesignSystemShowcase.svelte.d.ts +3 -0
  168. package/dist/components/ui/badge/badge-variants.d.ts +3 -2
  169. package/dist/components/ui/badge/badge-variants.js +2 -3
  170. package/dist/components/ui/badge/badge.svelte +3 -0
  171. package/dist/components/ui/badge/badge.svelte.d.ts +1 -0
  172. package/dist/components/ui/button/button-variants.d.ts +5 -4
  173. package/dist/components/ui/button/button-variants.js +7 -5
  174. package/dist/components/ui/checkbox/checkbox.svelte +5 -1
  175. package/dist/components/ui/checkbox/checkbox.svelte.d.ts +5 -1
  176. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +1 -2
  177. package/dist/components/ui/input/input.svelte +7 -2
  178. package/dist/components/ui/input/input.svelte.d.ts +4 -1
  179. package/dist/components/ui/select/select.svelte +4 -1
  180. package/dist/components/ui/select/select.svelte.d.ts +2 -1
  181. package/dist/components/ui/textarea/textarea.svelte +2 -2
  182. package/dist/components.css +33 -2
  183. package/dist/date-input-policy.d.ts +4 -0
  184. package/dist/date-input-policy.js +35 -0
  185. package/dist/design-system-components.d.ts +100 -0
  186. package/dist/design-system-components.js +101 -0
  187. package/dist/design-system.js +1 -1
  188. package/dist/devtools-bridge.d.ts +80 -0
  189. package/dist/devtools-bridge.js +92 -0
  190. package/dist/editor-config.svelte.d.ts +4 -2
  191. package/dist/index.d.ts +37 -2
  192. package/dist/index.js +23 -2
  193. package/dist/recipes.d.ts +960 -4
  194. package/dist/recipes.js +264 -4
  195. package/dist/tree-table-model.d.ts +24 -0
  196. package/dist/tree-table-model.js +111 -0
  197. package/dist/types.d.ts +4 -4
  198. package/package.json +22 -10
  199. package/dist/styled-system/css/conditions.js +0 -36
  200. package/dist/styled-system/css/css.d.ts +0 -22
  201. package/dist/styled-system/css/css.js +0 -45
  202. package/dist/styled-system/css/cva.d.ts +0 -6
  203. package/dist/styled-system/css/cva.js +0 -87
  204. package/dist/styled-system/css/cx.d.ts +0 -5
  205. package/dist/styled-system/css/cx.js +0 -15
  206. package/dist/styled-system/css/index.d.ts +0 -5
  207. package/dist/styled-system/css/index.js +0 -4
  208. package/dist/styled-system/css/sva.d.ts +0 -4
  209. package/dist/styled-system/css/sva.js +0 -46
  210. package/dist/styled-system/helpers.js +0 -316
  211. package/dist/styled-system/patterns/aspect-ratio.d.ts +0 -20
  212. package/dist/styled-system/patterns/aspect-ratio.js +0 -38
  213. package/dist/styled-system/patterns/bleed.d.ts +0 -21
  214. package/dist/styled-system/patterns/bleed.js +0 -24
  215. package/dist/styled-system/patterns/box.d.ts +0 -20
  216. package/dist/styled-system/patterns/box.js +0 -15
  217. package/dist/styled-system/patterns/center.d.ts +0 -20
  218. package/dist/styled-system/patterns/center.js +0 -21
  219. package/dist/styled-system/patterns/circle.d.ts +0 -20
  220. package/dist/styled-system/patterns/circle.js +0 -25
  221. package/dist/styled-system/patterns/container.d.ts +0 -20
  222. package/dist/styled-system/patterns/container.js +0 -21
  223. package/dist/styled-system/patterns/cq.d.ts +0 -21
  224. package/dist/styled-system/patterns/cq.js +0 -21
  225. package/dist/styled-system/patterns/divider.d.ts +0 -22
  226. package/dist/styled-system/patterns/divider.js +0 -25
  227. package/dist/styled-system/patterns/flex.d.ts +0 -26
  228. package/dist/styled-system/patterns/flex.js +0 -26
  229. package/dist/styled-system/patterns/float.d.ts +0 -23
  230. package/dist/styled-system/patterns/float.js +0 -52
  231. package/dist/styled-system/patterns/grid-item.d.ts +0 -25
  232. package/dist/styled-system/patterns/grid-item.js +0 -25
  233. package/dist/styled-system/patterns/grid.d.ts +0 -24
  234. package/dist/styled-system/patterns/grid.js +0 -27
  235. package/dist/styled-system/patterns/hstack.d.ts +0 -21
  236. package/dist/styled-system/patterns/hstack.js +0 -24
  237. package/dist/styled-system/patterns/index.d.ts +0 -21
  238. package/dist/styled-system/patterns/index.js +0 -20
  239. package/dist/styled-system/patterns/link-overlay.d.ts +0 -20
  240. package/dist/styled-system/patterns/link-overlay.js +0 -24
  241. package/dist/styled-system/patterns/spacer.d.ts +0 -20
  242. package/dist/styled-system/patterns/spacer.js +0 -25
  243. package/dist/styled-system/patterns/square.d.ts +0 -20
  244. package/dist/styled-system/patterns/square.js +0 -24
  245. package/dist/styled-system/patterns/stack.d.ts +0 -23
  246. package/dist/styled-system/patterns/stack.js +0 -24
  247. package/dist/styled-system/patterns/visually-hidden.d.ts +0 -20
  248. package/dist/styled-system/patterns/visually-hidden.js +0 -18
  249. package/dist/styled-system/patterns/vstack.d.ts +0 -21
  250. package/dist/styled-system/patterns/vstack.js +0 -24
  251. package/dist/styled-system/patterns/wrap.d.ts +0 -24
  252. package/dist/styled-system/patterns/wrap.js +0 -25
  253. package/dist/styled-system/recipes/content-header.d.ts +0 -31
  254. package/dist/styled-system/recipes/content-header.js +0 -65
  255. package/dist/styled-system/recipes/content-page.d.ts +0 -34
  256. package/dist/styled-system/recipes/content-page.js +0 -43
  257. package/dist/styled-system/recipes/create-recipe.js +0 -82
  258. package/dist/styled-system/recipes/index.d.ts +0 -17
  259. package/dist/styled-system/recipes/index.js +0 -16
  260. package/dist/styled-system/recipes/metric-block.d.ts +0 -34
  261. package/dist/styled-system/recipes/metric-block.js +0 -76
  262. package/dist/styled-system/recipes/product-list.d.ts +0 -31
  263. package/dist/styled-system/recipes/product-list.js +0 -73
  264. package/dist/styled-system/recipes/product-section.d.ts +0 -31
  265. package/dist/styled-system/recipes/product-section.js +0 -49
  266. package/dist/styled-system/recipes/product-settings-row.d.ts +0 -34
  267. package/dist/styled-system/recipes/product-settings-row.js +0 -58
  268. package/dist/styled-system/recipes/product-settings.d.ts +0 -31
  269. package/dist/styled-system/recipes/product-settings.js +0 -57
  270. package/dist/styled-system/recipes/product-status.d.ts +0 -34
  271. package/dist/styled-system/recipes/product-status.js +0 -45
  272. package/dist/styled-system/recipes/product-toolbar.d.ts +0 -31
  273. package/dist/styled-system/recipes/product-toolbar.js +0 -41
  274. package/dist/styled-system/recipes/product-workspace.d.ts +0 -34
  275. package/dist/styled-system/recipes/product-workspace.js +0 -58
  276. package/dist/styled-system/recipes/surface-metric.d.ts +0 -38
  277. package/dist/styled-system/recipes/surface-metric.js +0 -63
  278. package/dist/styled-system/recipes/surface-table.d.ts +0 -34
  279. package/dist/styled-system/recipes/surface-table.js +0 -62
  280. package/dist/styled-system/recipes/ui-badge.d.ts +0 -31
  281. package/dist/styled-system/recipes/ui-badge.js +0 -38
  282. package/dist/styled-system/recipes/ui-button.d.ts +0 -32
  283. package/dist/styled-system/recipes/ui-button.js +0 -43
  284. package/dist/styled-system/recipes/ui-input.d.ts +0 -31
  285. package/dist/styled-system/recipes/ui-input.js +0 -49
  286. package/dist/styled-system/recipes/ui-textarea.d.ts +0 -31
  287. package/dist/styled-system/recipes/ui-textarea.js +0 -24
  288. package/dist/styled-system/styles.css +0 -1257
  289. package/dist/styled-system/tokens/index.d.ts +0 -9
  290. package/dist/styled-system/tokens/index.js +0 -268
  291. package/dist/styled-system/tokens/tokens.d.ts +0 -36
  292. package/dist/styled-system/types/composition.d.ts +0 -227
  293. package/dist/styled-system/types/conditions.d.ts +0 -236
  294. package/dist/styled-system/types/csstype.d.ts +0 -22570
  295. package/dist/styled-system/types/index.d.ts +0 -6
  296. package/dist/styled-system/types/parts.d.ts +0 -8
  297. package/dist/styled-system/types/pattern.d.ts +0 -78
  298. package/dist/styled-system/types/prop-type.d.ts +0 -223
  299. package/dist/styled-system/types/recipe.d.ts +0 -181
  300. package/dist/styled-system/types/selectors.d.ts +0 -59
  301. package/dist/styled-system/types/static-css.d.ts +0 -56
  302. package/dist/styled-system/types/style-props.d.ts +0 -8088
  303. package/dist/styled-system/types/system-types.d.ts +0 -151
@@ -1,176 +1,31 @@
1
1
  <script lang="ts">
2
+ import { captureAdminContext } from '@svadmin/core';
2
3
  import { useTranslation } from '@svadmin/core/i18n';
3
- import * as Card from '../ui/card/index.js';
4
+ import ImportWizard from '../ImportWizard.svelte';
5
+ import ContentPageShell from '../content/ContentPageShell.svelte';
6
+ import ContentPageHeader from '../content/ContentPageHeader.svelte';
7
+ import FeedbackNotice from '../content/FeedbackNotice.svelte';
4
8
  import { Button } from '../ui/button/index.js';
5
- import { Switch } from '../ui/switch/index.js';
6
- import { Label } from '../ui/label/index.js';
7
- import { Upload, Download, FileText, CheckCircle2, Loader2 } from '@lucide/svelte';
9
+ import { Upload } from '@lucide/svelte';
8
10
 
11
+ let { resourceName = 'users' }: { resourceName?: string } = $props();
12
+ const context = captureAdminContext();
9
13
  const i18n = useTranslation();
10
-
11
- let isDragging = $state(false);
12
- let fileName = $state('');
13
- let importState = $state<'idle' | 'uploading' | 'processing' | 'complete' | 'error'>('idle');
14
- let progress = $state(0);
15
- let importCount = $state(0);
16
-
17
- const importOptions = [
18
- { key: 'account.importCreateUsers', checked: true },
19
- { key: 'account.importUpdateUsers', checked: false },
20
- { key: 'account.importNotifyPassword', checked: false },
21
- { key: 'account.importExternalIds', checked: false },
22
- { key: 'account.importWelcomeEmail', checked: true },
23
- ];
24
- let optionStates = $state<boolean[]>(importOptions.map(o => o.checked));
25
-
26
- function handleDragOver(e: DragEvent) {
27
- e.preventDefault();
28
- isDragging = true;
29
- }
30
-
31
- function handleDragLeave() {
32
- isDragging = false;
33
- }
34
-
35
- function handleDrop(e: DragEvent) {
36
- e.preventDefault();
37
- isDragging = false;
38
- const file = e.dataTransfer?.files?.[0];
39
- if (file) processFile(file);
40
- }
41
-
42
- function handleFileSelect(e: Event) {
43
- const file = e.target instanceof HTMLInputElement ? e.target.files?.[0] : undefined;
44
- if (file) processFile(file);
45
- }
46
-
47
- function processFile(file: File) {
48
- fileName = file.name;
49
- importState = 'uploading';
50
- progress = 0;
51
-
52
- // Simulate upload
53
- const interval = setInterval(() => {
54
- progress += 10;
55
- if (progress >= 100) {
56
- clearInterval(interval);
57
- importState = 'processing';
58
- // Simulate processing
59
- setTimeout(() => {
60
- importCount = Math.floor(Math.random() * 50) + 20;
61
- importState = 'complete';
62
- }, 1500);
63
- }
64
- }, 200);
65
- }
66
-
67
- function resetImport() {
68
- fileName = '';
69
- importState = 'idle';
70
- progress = 0;
71
- importCount = 0;
72
- }
14
+ const resource = $derived(context.resources.find(item => item.name === resourceName));
15
+ const available = $derived(resource && resource.canCreate !== false);
16
+ let open = $state(false);
17
+ let result = $state<{ succeeded: number; failed: number } | null>(null);
18
+ $effect(() => { void resourceName; void context.authProvider; void context.tenantCacheKey; open = false; result = null; });
73
19
  </script>
74
20
 
75
- <div class="svadmin-u-b3542e058833" data-svadmin-content-page="account">
76
- <div>
77
- <h2 class="svadmin-u-d5c9b0001e7e svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{i18n.t('account.importMembers')}</h2>
78
- <p class="svadmin-u-b6b02c0ebef6 svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{i18n.t('account.importMembersDescription')}</p>
79
- </div>
80
-
81
- {#if importState === 'complete'}
82
- <Card.Card class="svadmin-u-18a6e7a36f29 svadmin-u-338625ff877e">
83
- <Card.CardContent class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-3960ffc248d9 svadmin-u-0c3bc98565dd svadmin-u-845f53365c8d">
84
- <div class="svadmin-u-60fbb7713999 svadmin-u-73a1340934c0 svadmin-u-7e74e5fe798a svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-68f2db624df7 svadmin-u-17a9f7af2265 svadmin-u-76747e5e02ff">
85
- <CheckCircle2 class="svadmin-u-d0a52b312f7d svadmin-u-cbbf90f9a828" />
86
- </div>
87
- <div class="svadmin-u-ca6bf63030aa">
88
- <h3 class="svadmin-u-42536e69e639 svadmin-u-e83a7042bc91 svadmin-u-76747e5e02ff">{i18n.t('account.importComplete')}</h3>
89
- <p class="svadmin-u-fc7473ca09eb svadmin-u-083abb355258 svadmin-u-b6b02c0ebef6">{i18n.t('account.imported', { count: importCount })}</p>
90
- </div>
91
- <Button variant="outline" onclick={resetImport}>{i18n.t('common.back')}</Button>
92
- </Card.CardContent>
93
- </Card.Card>
21
+ <ContentPageShell pageId="account-import-members" width="wide">
22
+ <ContentPageHeader title={i18n.t('account.importMembers')} />
23
+ <p>{i18n.locale === 'zh-CN' ? '导入成员记录到所配置的数据源;不会自动发送邀请或欢迎邮件。' : 'Import member records into the configured data source. Invitations and welcome emails are not sent automatically.'}</p>
24
+ {#if available}
25
+ <Button onclick={() => open = true}><Upload size={16} />{i18n.t('common.import')}</Button>
26
+ <ImportWizard {resourceName} bind:open onSuccess={(value) => result = value} />
27
+ {#if result}<p role="status">{i18n.locale === 'zh-CN' ? '成功 / 失败' : 'Succeeded / failed'}: {result.succeeded} / {result.failed}</p>{/if}
94
28
  {:else}
95
- <!-- Upload area -->
96
- <Card.Card class="svadmin-u-05faf5c801ff">
97
- <Card.CardContent class="svadmin-u-0478c89a150f">
98
- <label
99
- for="file-input"
100
- class="svadmin-u-0214b4b355d1 svadmin-u-34516836730d svadmin-u-a217b4eaa918 svadmin-u-65935df577ba svadmin-u-a29b7a649c77 svadmin-u-845f53365c8d svadmin-u-ca6bf63030aa svadmin-u-ceb69a6b0e5f
101
- {isDragging ? 'svadmin-u-6cbc84dd9e1a svadmin-u-989c466fdbe7' : 'svadmin-u-05faf5c801ff svadmin-u-7d7df768f912'}"
102
- ondragover={handleDragOver}
103
- ondragleave={handleDragLeave}
104
- ondrop={handleDrop}
105
- >
106
- {#if importState === 'idle'}
107
- <Upload class="svadmin-u-0e12dc7de920 svadmin-u-426b8b75185b svadmin-u-d854e5698b57 svadmin-u-bfa603190748" />
108
- <p class="svadmin-u-eccd13ef4f2f svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{i18n.t('account.dragOrClick')}</p>
109
- <p class="svadmin-u-b6b02c0ebef6 svadmin-u-359090c2d529 svadmin-u-bfa603190748">{i18n.t('account.supportedFormats')}</p>
110
- <div class="svadmin-u-0ab8667228fd">
111
- <span class="svadmin-u-52083e7da442 svadmin-u-ed8a5df7b2fb svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-e5795dad4d22 svadmin-u-e6f9e383a762 svadmin-u-0e17f2bd9074 svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-cef5b893cf23 svadmin-u-ceb69a6b0e5f svadmin-u-0557b88819cd svadmin-u-3a99b2b8fbbe">
112
- {i18n.t('common.upload')}
113
- </span>
114
- <input id="file-input" type="file" accept=".csv,.xlsx" class="svadmin-u-2daa8e5e2f2e" onchange={handleFileSelect} />
115
- </div>
116
- {:else if importState === 'uploading' || importState === 'processing'}
117
- <div class="svadmin-u-6ed543e2fbbb">
118
- <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-77a2a20e90d4 svadmin-u-fc7473ca09eb">
119
- {#if importState === 'processing'}
120
- <Loader2 class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-afbdd13a380e" />
121
- {:else}
122
- <FileText class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-bfa603190748" />
123
- {/if}
124
- <span class="svadmin-u-2689f3958069">{fileName}</span>
125
- </div>
126
- <div class="svadmin-u-2f2a842e50fa svadmin-u-74b2435a1d40 svadmin-u-0e12dc7de920 svadmin-u-ac204c108886 svadmin-u-2ef11f1cb219 svadmin-u-2cd02d11d1af">
127
- <div class="svadmin-u-668b21aa5409 svadmin-u-ac204c108886 svadmin-u-75b1bec3ea0e svadmin-u-0fe7d7d814d0 svadmin-u-7890552ecd63" style="width: {progress}%"></div>
128
- </div>
129
- <p class="svadmin-u-359090c2d529 svadmin-u-bfa603190748">
130
- {importState === 'uploading' ? i18n.t('account.importProgress') : i18n.t('account.startImport')}...
131
- </p>
132
- </div>
133
- {/if}
134
- </label>
135
- </Card.CardContent>
136
- </Card.Card>
137
-
138
- <!-- Template download -->
139
- <Card.Card class="svadmin-u-05faf5c801ff">
140
- <Card.CardContent class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-8e63407b5ceb">
141
- <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-1004c0c3954c">
142
- <div class="svadmin-u-60fbb7713999 svadmin-u-e7a768f922d2 svadmin-u-ae2181c7b10f svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-5f22e64f2282 svadmin-u-375dc44df6e9 svadmin-u-20aaf08a7ed1">
143
- <FileText class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" />
144
- </div>
145
- <div>
146
- <p class="svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{i18n.t('account.downloadTemplate')}</p>
147
- <p class="svadmin-u-359090c2d529 svadmin-u-bfa603190748">CSV template.csv</p>
148
- </div>
149
- </div>
150
- <Button variant="outline" size="sm">
151
- <Download class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c svadmin-u-618162408e7a" />{i18n.t('account.downloadTemplate')}
152
- </Button>
153
- </Card.CardContent>
154
- </Card.Card>
155
-
156
- <!-- Import options -->
157
- <Card.Card class="svadmin-u-05faf5c801ff">
158
- <Card.CardHeader>
159
- <Card.CardTitle class="svadmin-u-4ee734926ff6">{i18n.t('account.importOptions')}</Card.CardTitle>
160
- </Card.CardHeader>
161
- <Card.CardContent class="svadmin-u-3e7ce58d64fa">
162
- {#each importOptions as option, i (option.key)}
163
- <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-0c3bc98565dd">
164
- <Label for="import-option-{i}" class="svadmin-u-fc7473ca09eb svadmin-u-8ecebc9f80e6 svadmin-u-d4108abe6359">{i18n.t(option.key)}</Label>
165
- <Switch id="import-option-{i}" checked={optionStates[i] ?? option.checked} onCheckedChange={(checked) => { optionStates[i] = checked; }} />
166
- </div>
167
- {/each}
168
- <div class="svadmin-u-60fbb7713999 svadmin-u-77c08e015d14 svadmin-u-b950dda299d3 svadmin-u-173fa8f06789">
169
- <Button onclick={() => { const input = document.getElementById('file-input'); input?.click(); }}>
170
- {i18n.t('account.startImport')}
171
- </Button>
172
- </div>
173
- </Card.CardContent>
174
- </Card.Card>
29
+ <FeedbackNotice tone="warning" message={i18n.locale === 'zh-CN' ? '未配置可导入的成员资源。' : 'No importable member resource is configured.'} />
175
30
  {/if}
176
- </div>
31
+ </ContentPageShell>
@@ -1,3 +1,6 @@
1
- declare const ImportMembersPage: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ resourceName?: string;
3
+ };
4
+ declare const ImportMembersPage: import("svelte").Component<$$ComponentProps, {}, "">;
2
5
  type ImportMembersPage = ReturnType<typeof ImportMembersPage>;
3
6
  export default ImportMembersPage;
@@ -0,0 +1,156 @@
1
+ <script lang="ts" module>
2
+ export interface DirectoryMember {
3
+ id: string;
4
+ name: string;
5
+ email: string;
6
+ role: string;
7
+ }
8
+ export interface MemberDirectoryProvider {
9
+ /** 宿主提供的目录来源或服务边界说明。 */
10
+ description?: string;
11
+ list: () => Promise<DirectoryMember[]>;
12
+ invite?: (email: string) => Promise<void>;
13
+ invitationUrl?: string;
14
+ }
15
+ </script>
16
+ <script lang="ts">
17
+ import { captureAdminContext } from '@svadmin/core';
18
+ import { useTranslation } from '@svadmin/core/i18n';
19
+ import { Button } from '../ui/button/index.js';
20
+ import { Input } from '../ui/input/index.js';
21
+ import DataState from '../content/DataState.svelte';
22
+ import FeedbackNotice from '../content/FeedbackNotice.svelte';
23
+ import { Copy, Send } from '@lucide/svelte';
24
+
25
+ let { provider }: { provider?: MemberDirectoryProvider | undefined } = $props();
26
+ const context = captureAdminContext();
27
+ const i18n = useTranslation();
28
+ const isZh = $derived(i18n.locale === 'zh-CN');
29
+ const controlId = $props.id();
30
+ let rows = $state<DirectoryMember[]>([]);
31
+ let query = $state('');
32
+ let email = $state('');
33
+ let loading = $state(false);
34
+ let sending = $state(false);
35
+ let error = $state('');
36
+ let loadError = $state('');
37
+ let status = $state('');
38
+ let epoch = 0;
39
+ const filtered = $derived(rows.filter(row => `${row.name} ${row.email}`.toLowerCase().includes(query.toLowerCase())));
40
+ async function load() {
41
+ const current = provider;
42
+ const request = ++epoch;
43
+ rows = []; loadError = ''; loading = Boolean(current);
44
+ if (!current) return;
45
+ try {
46
+ const result = await current.list();
47
+ if (request === epoch) rows = result;
48
+ } catch (caught) {
49
+ if (request === epoch) loadError = caught instanceof Error ? caught.message : String(caught);
50
+ } finally {
51
+ if (request === epoch) loading = false;
52
+ }
53
+ }
54
+ $effect(() => {
55
+ void provider; void context.authProvider; void context.tenantCacheKey?.__svadminTenant;
56
+ email = ''; query = ''; error = ''; status = ''; sending = false;
57
+ void load();
58
+ return () => { epoch += 1; };
59
+ });
60
+ async function invite(event: SubmitEvent) {
61
+ event.preventDefault();
62
+ const current = provider;
63
+ if (!current?.invite || sending || loading) return;
64
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
65
+ error = isZh ? '请输入有效邮箱' : 'Enter a valid email address';
66
+ return;
67
+ }
68
+ const request = epoch;
69
+ sending = true; error = ''; status = '';
70
+ try {
71
+ await current.invite(email.trim());
72
+ if (request !== epoch) return;
73
+ email = '';
74
+ status = isZh ? '邀请已提交' : 'Invitation submitted';
75
+ } catch (caught) {
76
+ if (request === epoch) error = caught instanceof Error ? caught.message : String(caught);
77
+ } finally {
78
+ if (request === epoch) sending = false;
79
+ }
80
+ }
81
+ async function copyLink() {
82
+ if (!provider?.invitationUrl) return;
83
+ const request = epoch;
84
+ status = ''; error = '';
85
+ try {
86
+ await navigator.clipboard.writeText(provider.invitationUrl);
87
+ if (request === epoch) status = isZh ? '链接已复制' : 'Link copied';
88
+ } catch {
89
+ if (request === epoch) error = isZh ? '复制失败' : 'Copy failed';
90
+ }
91
+ }
92
+ </script>
93
+
94
+ {#if !provider}
95
+ <FeedbackNotice tone="warning" message={isZh ? '未配置成员目录服务,不会模拟成员或发送邀请。' : 'A member directory is not configured. No members or invitations are simulated.'} />
96
+ {:else}
97
+ <div class="min-w-0 space-y-4">
98
+ {#if provider.description}<p role="note" class="text-sm text-muted-foreground">{provider.description}</p>{/if}
99
+ {#if provider.invite}
100
+ <form onsubmit={invite} class="w-full max-w-xl space-y-2">
101
+ <label for={`${controlId}-invite`} class="block text-sm font-medium text-foreground">{isZh ? '成员邮箱' : 'Member email'}</label>
102
+ <div class="flex flex-col items-stretch gap-2 sm:flex-row">
103
+ <Input id={`${controlId}-invite`} class="min-w-0 flex-1" type="email" required bind:value={email} disabled={sending} />
104
+ <Button type="submit" class="shrink-0" disabled={sending || loading}><Send size={16} />{sending ? i18n.t('common.loading') : i18n.t('account.sendInvite')}</Button>
105
+ </div>
106
+ </form>
107
+ {:else}
108
+ <p class="text-sm text-muted-foreground">{isZh ? '当前目录为只读,未配置邀请发送能力。' : 'This directory is read-only. Invitation delivery is not configured.'}</p>
109
+ {/if}
110
+ {#if provider.invitationUrl}<div><Button variant="outline" onclick={copyLink}><Copy size={16} />{i18n.t('account.copyLink')}</Button></div>{/if}
111
+ {#if error}<p role="alert" class="text-sm text-destructive">{error}</p>{/if}
112
+ {#if status}<p role="status" class="text-sm text-foreground">{status}</p>{/if}
113
+ {#if loading}
114
+ <DataState state="loading" />
115
+ {:else if loadError}
116
+ <DataState state="error" description={loadError} retry={load} />
117
+ {:else}
118
+ <div class="w-full max-w-sm space-y-2">
119
+ <label for={`${controlId}-search`} class="block text-sm font-medium text-foreground">{isZh ? '搜索成员' : 'Search members'}</label>
120
+ <Input id={`${controlId}-search`} type="search" bind:value={query} />
121
+ </div>
122
+ {#if filtered.length === 0}
123
+ <DataState state="empty" title={isZh ? '没有匹配的成员' : 'No matching members'} />
124
+ {:else}
125
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex (横向滚动区域需支持键盘聚焦与滚动) -->
126
+ <div class="member-directory-scroll w-full min-w-0 overflow-x-auto" role="region" aria-label={isZh ? '成员目录' : 'Member directory'} tabindex="0">
127
+ <table class="member-directory-table w-full border-collapse text-left text-sm" aria-label={isZh ? '成员列表' : 'Members'}>
128
+ <thead class="border-b border-border bg-muted text-muted-foreground">
129
+ <tr>
130
+ <th scope="col" class="px-4 py-3 font-medium">{isZh ? '姓名' : 'Name'}</th>
131
+ <th scope="col" class="px-4 py-3 font-medium">{isZh ? '邮箱' : 'Email'}</th>
132
+ <th scope="col" class="px-4 py-3 font-medium">{isZh ? '角色' : 'Role'}</th>
133
+ </tr>
134
+ </thead>
135
+ <tbody>
136
+ {#each filtered as row (row.id)}
137
+ <tr class="border-b border-border hover:bg-muted/50">
138
+ <th scope="row" class="px-4 py-3 font-medium text-foreground break-words">{row.name}</th>
139
+ <td class="px-4 py-3 text-muted-foreground break-all">{row.email}</td>
140
+ <td class="px-4 py-3 text-foreground break-words">{row.role}</td>
141
+ </tr>
142
+ {/each}
143
+ </tbody>
144
+ </table>
145
+ </div>
146
+ {/if}
147
+ {/if}
148
+ </div>
149
+ {/if}
150
+
151
+ <style>
152
+ /* 保持可读列宽;局部尺寸合同优先于 clean-flat 的通用表格收缩规则。 */
153
+ .member-directory-scroll > table.member-directory-table {
154
+ min-width: var(--container-xl, 36rem);
155
+ }
156
+ </style>
@@ -0,0 +1,19 @@
1
+ export interface DirectoryMember {
2
+ id: string;
3
+ name: string;
4
+ email: string;
5
+ role: string;
6
+ }
7
+ export interface MemberDirectoryProvider {
8
+ /** 宿主提供的目录来源或服务边界说明。 */
9
+ description?: string;
10
+ list: () => Promise<DirectoryMember[]>;
11
+ invite?: (email: string) => Promise<void>;
12
+ invitationUrl?: string;
13
+ }
14
+ type $$ComponentProps = {
15
+ provider?: MemberDirectoryProvider | undefined;
16
+ };
17
+ declare const MemberDirectory: import("svelte").Component<$$ComponentProps, {}, "">;
18
+ type MemberDirectory = ReturnType<typeof MemberDirectory>;
19
+ export default MemberDirectory;
@@ -1,63 +1,17 @@
1
1
  <script lang="ts">
2
+ import { captureAdminContext } from '@svadmin/core';
2
3
  import { useTranslation } from '@svadmin/core/i18n';
3
- import { ArrowRight, Check, Download, Mail, Upload, UserPlus, Users } from '@lucide/svelte';
4
- import * as Card from '../ui/card/index.js';
5
- import { Button } from '../ui/button/index.js';
6
- import { Input } from '../ui/input/index.js';
7
- import { Badge } from '../ui/badge/index.js';
4
+ import MemberDirectory, { type MemberDirectoryProvider } from './MemberDirectory.svelte';
8
5
  import ContentPageShell from '../content/ContentPageShell.svelte';
9
6
  import ContentPageHeader from '../content/ContentPageHeader.svelte';
10
-
7
+ import { Button } from '../ui/button/index.js';
8
+ import { Upload } from '@lucide/svelte';
9
+ let { memberProvider }: { memberProvider?: MemberDirectoryProvider } = $props();
10
+ const context = captureAdminContext();
11
11
  const i18n = useTranslation();
12
- let inviteOpen = $state(false);
13
- let email = $state('');
14
- let invited = $state<string[]>([]);
15
- const isZh = $derived(i18n.locale === 'zh-CN');
16
-
17
- function sendInvite() {
18
- const normalized = email.trim();
19
- if (!normalized || invited.includes(normalized)) return;
20
- invited = [...invited, normalized];
21
- email = '';
22
- }
23
12
  </script>
24
-
25
13
  <ContentPageShell pageId="account-members-starter" width="wide">
26
- <ContentPageHeader title={isZh ? '开始组建团队' : 'Start building your team'} description={isZh ? '这是零成员工作区的起始页。邀请第一位成员,或通过 CSV 批量导入。' : 'This is the starting state for a workspace with no members. Invite the first teammate or import a CSV roster.'} />
27
-
28
- <section class="svadmin-u-f3c543ad5fe9 svadmin-u-2cd02d11d1af svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558 svadmin-u-687e17559be1">
29
- <div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-f92d02360b8f svadmin-u-61357c0c2f29 svadmin-u-ca6bf63030aa">
30
- <span class="svadmin-u-60fbb7713999 svadmin-u-7a9ad020b130 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-5f22e64f2282 svadmin-u-375dc44df6e9 svadmin-u-20aaf08a7ed1"><Users class="svadmin-u-9af3b4b6dc9e" /></span>
31
- <Badge variant="outline" class="svadmin-u-fb77735ed17c">0 {isZh ? '名成员' : 'members'}</Badge>
32
- <h2 class="svadmin-u-0ab8667228fd svadmin-u-d5c9b0001e7e svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{isZh ? '邀请你的第一位团队成员' : 'Invite your first team member'}</h2>
33
- <p class="svadmin-u-50d0d216a2f8 svadmin-u-6199866f612f svadmin-u-fc7473ca09eb svadmin-u-18550d5945ae svadmin-u-bfa603190748">{isZh ? '成员加入后,你可以继续分配角色、设置权限、查看活动状态,并在团队成员页批量管理。' : 'After members join, assign roles, configure access, review activity, and manage them in the full team member table.'}</p>
34
- <div class="svadmin-u-31f2553311b6 svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-86843cf1e227 svadmin-u-77a2a20e90d4"><Button onclick={() => inviteOpen = true}><UserPlus class="svadmin-u-f7b5fa971871" />{isZh ? '邀请成员' : 'Invite member'}</Button><Button href="#/account/members/import-members" variant="outline"><Upload class="svadmin-u-f7b5fa971871" />{isZh ? '导入 CSV' : 'Import CSV'}</Button></div>
35
- </div>
36
-
37
- <aside class="svadmin-u-b950dda299d3 svadmin-u-18049387f0af svadmin-u-46daeb0b661f svadmin-u-c07e54fd1439 svadmin-u-bb3e95154365 svadmin-u-7d283214b870">
38
- <h2 class="svadmin-u-fc7473ca09eb svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{isZh ? '团队准备清单' : 'Team setup checklist'}</h2>
39
- <div class="svadmin-u-0ab8667228fd svadmin-u-3e7ce58d64fa">
40
- {#each [isZh ? '邀请至少一名管理员' : 'Invite at least one admin', isZh ? '确认默认成员角色' : 'Confirm the default member role', isZh ? '准备成员 CSV 模板' : 'Prepare the member CSV template'] as item, index (item)}
41
- {@const completed = index === 0 && invited.length > 0}
42
- <div class="svadmin-u-60fbb7713999 svadmin-u-60541e1e26f8 svadmin-u-1004c0c3954c" data-checklist-status={completed ? 'complete' : 'pending'}><span class={'svadmin-u-15e1b1f444fe svadmin-u-60fbb7713999 svadmin-u-9af3b4b6dc9e svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f ' + (completed ? 'svadmin-u-18a6e7a36f29 svadmin-u-17a9f7af2265 svadmin-u-76747e5e02ff' : 'svadmin-u-18049387f0af svadmin-u-bfa603190748')}>{#if completed}<Check class="svadmin-u-783b0d9d1e2c" />{:else}<span class="svadmin-u-359090c2d529">{index + 1}</span>{/if}</span><p class="svadmin-u-fc7473ca09eb svadmin-u-7054e2767710 svadmin-u-bfa603190748">{item}</p></div>
43
- {/each}
44
- </div>
45
- <a href="#/account/members/team-members" class="svadmin-u-31f2553311b6 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-44ee8ba0a421 svadmin-u-b950dda299d3 svadmin-u-18049387f0af svadmin-u-173fa8f06789 svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-20aaf08a7ed1">{isZh ? '查看完整成员管理' : 'Open full member management'}<ArrowRight class="svadmin-u-783b0d9d1e2c" /></a>
46
- <Button variant="outline" size="sm" class="svadmin-u-eccd13ef4f2f svadmin-u-6da6a3c3f741"><Download class="svadmin-u-783b0d9d1e2c" />{isZh ? '下载 CSV 模板' : 'Download CSV template'}</Button>
47
- </aside>
48
- </section>
49
-
50
- {#if inviteOpen}
51
- <Card.Card data-member-invite-panel>
52
- <Card.CardContent class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-c07e54fd1439 svadmin-u-d7bf6780c9a5 svadmin-u-bf60a82ffbd9">
53
- <span class="svadmin-u-60fbb7713999 svadmin-u-7bbb00f9ba7a svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-421ac2be5045 svadmin-u-375dc44df6e9 svadmin-u-20aaf08a7ed1"><Mail class="svadmin-u-f7b5fa971871" /></span>
54
- <label><span class="svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{isZh ? '成员邮箱' : 'Member email'}</span><Input class="svadmin-u-50d0d216a2f8" type="email" bind:value={email} placeholder="teammate@example.com" /></label>
55
- <div class="svadmin-u-60fbb7713999 svadmin-u-77a2a20e90d4"><Button variant="outline" onclick={() => inviteOpen = false}>{i18n.t('common.cancel')}</Button><Button onclick={sendInvite}>{isZh ? '发送邀请' : 'Send invite'}</Button></div>
56
- </Card.CardContent>
57
- </Card.Card>
58
- {/if}
59
-
60
- {#if invited.length > 0}
61
- <section class="svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558"><div class="svadmin-u-65fdbade2025 svadmin-u-18049387f0af svadmin-u-f0faeb26d656 svadmin-u-1b2d54a3fd12"><h2 class="svadmin-u-fc7473ca09eb svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{isZh ? '待接受邀请' : 'Pending invitations'}</h2></div><div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">{#each invited as invite (invite)}<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-1004c0c3954c svadmin-u-f0faeb26d656 svadmin-u-1b2d54a3fd12"><div><p class="svadmin-u-fc7473ca09eb svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{invite}</p><p class="svadmin-u-359090c2d529 svadmin-u-bfa603190748">{isZh ? '刚刚发送' : 'Sent just now'}</p></div><Badge variant="outline">{isZh ? '已邀请' : 'Invited'}</Badge></div>{/each}</div></section>
62
- {/if}
14
+ <ContentPageHeader title={i18n.locale === 'zh-CN' ? '开始组建团队' : 'Start building your team'} />
15
+ <MemberDirectory provider={memberProvider} />
16
+ <Button variant="outline" onclick={() => context.navigate('/account/members/import-members')}><Upload size={16} />{i18n.t('common.import')}</Button>
63
17
  </ContentPageShell>
@@ -1,3 +1,7 @@
1
- declare const MembersStarterPage: import("svelte").Component<Record<string, never>, {}, "">;
1
+ import { type MemberDirectoryProvider } from './MemberDirectory.svelte';
2
+ type $$ComponentProps = {
3
+ memberProvider?: MemberDirectoryProvider;
4
+ };
5
+ declare const MembersStarterPage: import("svelte").Component<$$ComponentProps, {}, "">;
2
6
  type MembersStarterPage = ReturnType<typeof MembersStarterPage>;
3
7
  export default MembersStarterPage;
@@ -10,6 +10,8 @@
10
10
  import SecurityEventTable from '../content/SecurityEventTable.svelte';
11
11
  import type { SecurityEvent } from '../content/SecurityEventTable.svelte';
12
12
  import { referenceDemoData } from '../../reference-data.js';
13
+ import ReferenceNotice from '../content/ReferenceNotice.svelte';
14
+ import { downloadRows } from '../export-reference.js';
13
15
  const i18n = useTranslation();
14
16
  let query = $state('');
15
17
  const events: SecurityEvent[] = referenceDemoData.securityEvents;
@@ -17,8 +19,9 @@
17
19
  </script>
18
20
 
19
21
  <ContentPageShell pageId="account-security-log" width="wide">
22
+ <ReferenceNotice />
20
23
  <ContentPageHeader title={i18n.t('account.securityLog')} description={i18n.t('account.securityLogDescription')} {...definedOptions({ "actions": undefined })} />
21
- <div class="svadmin-u-60fbb7713999 svadmin-u-77c08e015d14"><Button variant="outline" size="sm"><Download class="svadmin-u-783b0d9d1e2c" />{i18n.t('common.export')}</Button></div>
24
+ <div class="svadmin-u-60fbb7713999 svadmin-u-77c08e015d14"><Button variant="outline" size="sm" onclick={() => downloadRows('sample-security-events.json', filtered)}><Download class="svadmin-u-783b0d9d1e2c" />{i18n.t('common.export')}</Button></div>
22
25
  <FilterToolbar bind:query placeholder={i18n.t('common.search')} />
23
26
  <SecurityEventTable events={filtered} />
24
27
  </ContentPageShell>
@@ -1,44 +1,13 @@
1
1
  <script lang="ts">
2
- import { useTranslation } from '@svadmin/core/i18n';
3
- import { Button } from '../ui/button/index.js';
4
- import { Input } from '../ui/input/index.js';
5
- import { Label } from '../ui/label/index.js';
6
- import { Switch } from '../ui/switch/index.js';
2
+ import ProfilePage from '../ProfilePage.svelte';
3
+ import NotificationsSettings from '../NotificationsSettings.svelte';
4
+ import SecuritySettings from '../SecuritySettings.svelte';
7
5
  import ContentPageShell from '../content/ContentPageShell.svelte';
8
- import ContentPageHeader from '../content/ContentPageHeader.svelte';
9
- import SettingsGroup from '../content/SettingsGroup.svelte';
10
- import SettingsFieldRow from '../content/SettingsFieldRow.svelte';
11
- const i18n = useTranslation();
12
- let displayName = $state('Alex Chen');
13
- let email = $state('alex@example.com');
14
- let notifications = $state(true);
15
- let twoFactor = $state(true);
16
- let saving = $state(false);
17
- async function save() { saving = true; await new Promise((resolve) => setTimeout(resolve, 400)); saving = false; }
6
+ let { preferencesProvider }: { preferencesProvider?: import('../NotificationsSettings.svelte').NotificationPreferencesProvider } = $props();
18
7
  </script>
19
8
 
20
- {#snippet headerActions()}
21
- <Button size="sm" onclick={save} disabled={saving}>{i18n.t('common.save')}</Button>
22
- {/snippet}
23
-
24
- {#snippet notificationControl()}
25
- <Switch bind:checked={notifications} aria-label={i18n.t('settings.notifications')} />
26
- {/snippet}
27
-
28
- {#snippet twoFactorControl()}
29
- <Switch bind:checked={twoFactor} aria-label={i18n.t('account.twoFactorAuth')} />
30
- {/snippet}
31
-
32
9
  <ContentPageShell pageId="account-settings-plain" width="narrow">
33
- <ContentPageHeader title={i18n.t('account.settingsPlain')} description={i18n.t('account.settingsPlainDescription')} actions={headerActions} />
34
- <SettingsGroup title={i18n.t('account.basicSettings')} description={i18n.t('account.settingsPlainDescription')} bodyClass="space-y-5">
35
- <div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e00ad81645a2">
36
- <div class="svadmin-u-6f7e013d6499"><Label for="settings-name">{i18n.t('profile.name')}</Label><Input id="settings-name" bind:value={displayName} /></div>
37
- <div class="svadmin-u-6f7e013d6499"><Label for="settings-email">Email</Label><Input id="settings-email" bind:value={email} type="email" /></div>
38
- </div>
39
- <div>
40
- <SettingsFieldRow label={i18n.t('settings.notifications')} description="Receive account and workflow updates." control={notificationControl} />
41
- <SettingsFieldRow label={i18n.t('account.twoFactorAuth')} description="Require an additional verification code when signing in." control={twoFactorControl} separated />
42
- </div>
43
- </SettingsGroup>
10
+ <ProfilePage />
11
+ <NotificationsSettings {...preferencesProvider ? { preferencesProvider } : {}} />
12
+ <SecuritySettings />
44
13
  </ContentPageShell>
@@ -1,3 +1,6 @@
1
- declare const SettingsPlainPage: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ preferencesProvider?: import('../NotificationsSettings.svelte').NotificationPreferencesProvider;
3
+ };
4
+ declare const SettingsPlainPage: import("svelte").Component<$$ComponentProps, {}, "">;
2
5
  type SettingsPlainPage = ReturnType<typeof SettingsPlainPage>;
3
6
  export default SettingsPlainPage;
@@ -2,31 +2,21 @@
2
2
  import { captureAdminContext } from '@svadmin/core';
3
3
  import { useTranslation } from '@svadmin/core/i18n';
4
4
  import { Button } from '../ui/button/index.js';
5
- import { Input } from '../ui/input/index.js';
6
- import { Switch } from '../ui/switch/index.js';
7
5
  import ContentPageShell from '../content/ContentPageShell.svelte';
8
6
  import ContentPageHeader from '../content/ContentPageHeader.svelte';
9
7
  import SettingsGroup from '../content/SettingsGroup.svelte';
10
- import SettingsFieldRow from '../content/SettingsFieldRow.svelte';
11
8
  import FeedbackNotice from '../content/FeedbackNotice.svelte';
9
+ import ProfilePage from '../ProfilePage.svelte';
10
+ import IntegrationsSettings from '../IntegrationsSettings.svelte';
11
+ import AppearanceSettings from '../AppearanceSettings.svelte';
12
12
  const i18n = useTranslation();
13
13
  const adminContext = captureAdminContext();
14
14
  const credentialProvider = $derived(adminContext.credentialProvider);
15
15
  const isZh = $derived(i18n.locale === 'zh-CN');
16
- const sections = [{ id: 'profile', title: i18n.t('account.basicSettings') }, { id: 'signin', title: i18n.t('account.socialSignIn') }, { id: 'preferences', title: i18n.t('account.preferences') }, { id: 'api', title: i18n.t('account.manageApi') }];
16
+ const sections = $derived([{ id: 'profile', title: i18n.t('account.basicSettings') }, { id: 'signin', title: i18n.t('account.socialSignIn') }, { id: 'preferences', title: i18n.t('account.preferences') }, { id: 'api', title: i18n.t('account.manageApi') }]);
17
17
  let active = $state('profile');
18
- let visible = $state(true);
19
- let available = $state(true);
20
18
  </script>
21
19
 
22
- {#snippet visibilityControl()}
23
- <Switch bind:checked={visible} aria-label={i18n.t('account.visibility')} />
24
- {/snippet}
25
-
26
- {#snippet availabilityControl()}
27
- <Switch bind:checked={available} aria-label={i18n.t('account.availability')} />
28
- {/snippet}
29
-
30
20
  <ContentPageShell pageId="account-settings-sidebar" width="wide">
31
21
  <ContentPageHeader title={i18n.t('account.settingsSidebar')} description={i18n.t('account.settingsSidebarDescription')} />
32
22
  <div class="svadmin-u-f3c543ad5fe9 svadmin-u-0d304f904cb0 svadmin-u-7178afa5bad6">
@@ -37,14 +27,11 @@
37
27
  </nav>
38
28
  <div class="svadmin-u-7e0b7cdf1a94">
39
29
  {#if active === 'profile'}
40
- <SettingsGroup title={i18n.t('account.basicSettings')} description={i18n.t('account.settingsPlainDescription')} bodyClass="space-y-4">
41
- <div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e00ad81645a2"><Input value="Alex Chen" aria-label={i18n.t('profile.name')} /><Input value="Nebula Labs" aria-label="Company" /></div>
42
- <div><SettingsFieldRow label={i18n.t('account.visibility')} control={visibilityControl} separated /><SettingsFieldRow label={i18n.t('account.availability')} control={availabilityControl} separated /></div>
43
- </SettingsGroup>
30
+ <ProfilePage />
44
31
  {:else if active === 'signin'}
45
- <SettingsGroup title={i18n.t('account.socialSignIn')} description="Connect the providers your team trusts for sign in." bodyClass="flex flex-wrap gap-2"><Button variant="outline">GitHub</Button><Button variant="outline">Google</Button></SettingsGroup>
32
+ <IntegrationsSettings />
46
33
  {:else if active === 'preferences'}
47
- <SettingsGroup title={i18n.t('account.preferences')} bodyClass="grid gap-4 sm:grid-cols-3"><Input value="English" aria-label={i18n.t('account.language')} /><Input value="Asia/Shanghai" aria-label={i18n.t('account.timezone')} /><Input value="CNY" aria-label={i18n.t('account.currency')} /></SettingsGroup>
34
+ <AppearanceSettings />
48
35
  {:else}
49
36
  <SettingsGroup title={i18n.t('account.manageApi')} description={isZh ? '使用有作用域的真实凭据,并在权限变更时轮换。' : 'Use scoped credentials and rotate them when access changes.'} bodyClass="space-y-3">
50
37
  {#if credentialProvider}