@svadmin/ui 0.72.0 → 0.74.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.
- package/README.md +62 -22
- package/THIRD_PARTY_NOTICES.md +52 -0
- package/dist/app.css +8488 -6297
- package/dist/app.theme.css +8514 -6374
- package/dist/class-properties.generated.d.ts +1 -0
- package/dist/class-properties.generated.js +2 -0
- package/dist/classnames.d.ts +8 -0
- package/dist/classnames.js +26 -0
- package/dist/component-registry.d.ts +14977 -0
- package/dist/component-registry.js +216 -0
- package/dist/components/AboutSettings.svelte +7 -1
- package/dist/components/AboutSettings.svelte.d.ts +4 -1
- package/dist/components/ActivityFeed.svelte +9 -3
- package/dist/components/ActivityFeed.svelte.d.ts +3 -0
- package/dist/components/AdminApp.svelte +19 -10
- package/dist/components/AdminApp.svelte.d.ts +6 -0
- package/dist/components/ApiSettings.svelte +41 -5
- package/dist/components/ApprovalCenter.svelte +283 -0
- package/dist/components/ApprovalCenter.svelte.d.ts +10 -0
- package/dist/components/AuditLogViewer.svelte +3 -1
- package/dist/components/AutoForm.svelte +53 -9
- package/dist/components/AutoForm.svelte.d.ts +3 -0
- package/dist/components/AutoTable.svelte +846 -43
- package/dist/components/AutoTable.svelte.d.ts +22 -1
- package/dist/components/BoundRecordDetailDrawer.svelte +17 -2
- package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +4 -0
- package/dist/components/Breadcrumbs.svelte +11 -1
- package/dist/components/CancelTaskButton.svelte +12 -42
- package/dist/components/ColorPicker.svelte +73 -0
- package/dist/components/ColorPicker.svelte.d.ts +15 -0
- package/dist/components/ComboboxField.svelte +143 -34
- package/dist/components/ComboboxField.svelte.d.ts +4 -2
- package/dist/components/CreatePage.svelte +4 -0
- package/dist/components/CreatePage.svelte.d.ts +2 -0
- package/dist/components/DashboardView.svelte +99 -0
- package/dist/components/DashboardView.svelte.d.ts +14 -0
- package/dist/components/DatePicker.svelte +10 -0
- package/dist/components/DatePicker.svelte.d.ts +6 -0
- package/dist/components/DateRangeInput.svelte +155 -0
- package/dist/components/DateRangeInput.svelte.d.ts +35 -0
- package/dist/components/DateRangePicker.svelte +10 -0
- package/dist/components/DateRangePicker.svelte.d.ts +6 -0
- package/dist/components/DateTimeInput.svelte +134 -0
- package/dist/components/DateTimeInput.svelte.d.ts +27 -0
- package/dist/components/DateTimePicker.svelte +10 -0
- package/dist/components/DateTimePicker.svelte.d.ts +6 -0
- package/dist/components/DevTools.svelte +36 -28
- package/dist/components/DevframePanel.svelte +123 -0
- package/dist/components/DevframePanel.svelte.d.ts +3 -0
- package/dist/components/EditPage.svelte +4 -0
- package/dist/components/EditPage.svelte.d.ts +2 -0
- package/dist/components/ErrorSummary.svelte +52 -0
- package/dist/components/ErrorSummary.svelte.d.ts +14 -0
- package/dist/components/FieldRenderer.svelte +172 -99
- package/dist/components/FileUpload.svelte +412 -0
- package/dist/components/FileUpload.svelte.d.ts +43 -0
- package/dist/components/FilterBuilder.svelte +244 -265
- package/dist/components/FilterBuilder.svelte.d.ts +3 -0
- package/dist/components/FilterCollectionInput.svelte +80 -0
- package/dist/components/FilterCollectionInput.svelte.d.ts +10 -0
- package/dist/components/GanttChart.svelte +66 -14
- package/dist/components/GanttChart.svelte.d.ts +6 -0
- package/dist/components/Header.svelte +18 -2
- package/dist/components/ImportWizard.svelte +509 -100
- package/dist/components/ImportWizard.svelte.d.ts +11 -0
- package/dist/components/JsonSchemaForm.svelte +322 -98
- package/dist/components/JsonSchemaForm.svelte.d.ts +18 -1
- package/dist/components/KanbanBoard.svelte +195 -40
- package/dist/components/KanbanBoard.svelte.d.ts +9 -2
- package/dist/components/Layout.svelte +4 -3
- package/dist/components/LazyAutoForm.svelte +28 -0
- package/dist/components/LazyAutoForm.svelte.d.ts +8 -0
- package/dist/components/LazyAutoTable.svelte +28 -0
- package/dist/components/LazyAutoTable.svelte.d.ts +8 -0
- package/dist/components/LazySettingsPage.svelte +28 -0
- package/dist/components/LazySettingsPage.svelte.d.ts +8 -0
- package/dist/components/LazyShowPage.svelte +28 -0
- package/dist/components/LazyShowPage.svelte.d.ts +8 -0
- package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +1 -1
- package/dist/components/ListPage.svelte +9 -1
- package/dist/components/ListPage.svelte.d.ts +5 -0
- package/dist/components/LoginPage.svelte +2 -2
- package/dist/components/MoneyInput.svelte +25 -0
- package/dist/components/MoneyInput.svelte.d.ts +20 -0
- package/dist/components/MultiTabKeepAlive.svelte +11 -6
- package/dist/components/NotificationsSettings.svelte +69 -2
- package/dist/components/NotificationsSettings.svelte.d.ts +25 -1
- package/dist/components/NumberInput.svelte +25 -0
- package/dist/components/NumberInput.svelte.d.ts +19 -0
- package/dist/components/NumericInput.svelte +110 -0
- package/dist/components/NumericInput.svelte.d.ts +23 -0
- package/dist/components/PercentInput.svelte +25 -0
- package/dist/components/PercentInput.svelte.d.ts +20 -0
- package/dist/components/PermissionMatrix.svelte +20 -1
- package/dist/components/PivotExportButton.svelte +157 -0
- package/dist/components/PivotExportButton.svelte.d.ts +16 -0
- package/dist/components/PivotTable.svelte +229 -74
- package/dist/components/PivotTable.svelte.d.ts +19 -2
- package/dist/components/QuickEditDrawer.svelte +4 -1
- package/dist/components/QuickEditDrawer.svelte.d.ts +2 -0
- package/dist/components/RadioGroup.svelte +121 -0
- package/dist/components/RadioGroup.svelte.d.ts +43 -0
- package/dist/components/RangeSlider.svelte +127 -0
- package/dist/components/RangeSlider.svelte.d.ts +22 -0
- package/dist/components/Rate.svelte +121 -0
- package/dist/components/Rate.svelte.d.ts +16 -0
- package/dist/components/RecordDetailDrawer.svelte +6 -2
- package/dist/components/RecordDetailDrawer.svelte.d.ts +4 -0
- package/dist/components/RelationPicker.svelte +341 -0
- package/dist/components/RelationPicker.svelte.d.ts +26 -0
- package/dist/components/ResourceOperationsPage.svelte +25 -19
- package/dist/components/ResourceOperationsPage.svelte.d.ts +5 -1
- package/dist/components/RetryTaskButton.svelte +12 -42
- package/dist/components/SecuritySettings.svelte +29 -5
- package/dist/components/SegmentedControl.svelte +145 -0
- package/dist/components/SegmentedControl.svelte.d.ts +38 -0
- package/dist/components/SettingsPage.svelte +3 -1
- package/dist/components/SettingsPage.svelte.d.ts +1 -0
- package/dist/components/ShowInferencer.svelte +4 -2
- package/dist/components/ShowInferencer.svelte.d.ts +2 -0
- package/dist/components/ShowPage.svelte +25 -6
- package/dist/components/ShowPage.svelte.d.ts +5 -0
- package/dist/components/Sidebar.svelte +5 -5
- package/dist/components/Slider.svelte +100 -0
- package/dist/components/Slider.svelte.d.ts +19 -0
- package/dist/components/SpreadsheetView.svelte +1037 -124
- package/dist/components/SpreadsheetView.svelte.d.ts +19 -0
- package/dist/components/StepsForm.svelte +5 -5
- package/dist/components/SvarAutoTable.svelte +39 -0
- package/dist/components/SvarAutoTable.svelte.d.ts +13 -0
- package/dist/components/SvarDataGrid.svelte +362 -0
- package/dist/components/SvarDataGrid.svelte.d.ts +57 -0
- package/dist/components/SvarGridCell.svelte +10 -0
- package/dist/components/SvarGridCell.svelte.d.ts +7 -0
- package/dist/components/SvarResourceRead.svelte +50 -0
- package/dist/components/SvarResourceRead.svelte.d.ts +23 -0
- package/dist/components/SvarResourceTable.svelte +63 -0
- package/dist/components/SvarResourceTable.svelte.d.ts +42 -0
- package/dist/components/SvarResourceTableView.svelte +364 -0
- package/dist/components/SvarResourceTableView.svelte.d.ts +4 -0
- package/dist/components/TagsInput.svelte +178 -0
- package/dist/components/TagsInput.svelte.d.ts +17 -0
- package/dist/components/TaskQueueDrawer.svelte +72 -16
- package/dist/components/TimePicker.svelte +10 -0
- package/dist/components/TimePicker.svelte.d.ts +6 -0
- package/dist/components/TreeSelect.svelte +440 -46
- package/dist/components/TreeSelect.svelte.d.ts +6 -1
- package/dist/components/TreeTable.svelte +484 -82
- package/dist/components/TreeTable.svelte.d.ts +24 -1
- package/dist/components/TwoFactorAuthPage.svelte +85 -48
- package/dist/components/TwoFactorAuthPage.svelte.d.ts +12 -1
- package/dist/components/VirtualTable.svelte +116 -20
- package/dist/components/VirtualTable.svelte.d.ts +5 -0
- package/dist/components/account/CompanyProfilePage.svelte +74 -38
- package/dist/components/account/GetStartedPage.svelte +2 -1
- package/dist/components/account/ImportMembersPage.svelte +22 -167
- package/dist/components/account/ImportMembersPage.svelte.d.ts +4 -1
- package/dist/components/account/MemberDirectory.svelte +156 -0
- package/dist/components/account/MemberDirectory.svelte.d.ts +19 -0
- package/dist/components/account/MembersStarterPage.svelte +9 -55
- package/dist/components/account/MembersStarterPage.svelte.d.ts +5 -1
- package/dist/components/account/SecurityLogPage.svelte +4 -1
- package/dist/components/account/SettingsPlainPage.svelte +7 -38
- package/dist/components/account/SettingsPlainPage.svelte.d.ts +4 -1
- package/dist/components/account/SettingsSidebarPage.svelte +7 -20
- package/dist/components/account/TeamMembersPage.svelte +4 -20
- package/dist/components/account/TeamMembersPage.svelte.d.ts +5 -1
- package/dist/components/account/UserProfilePage.svelte +3 -25
- package/dist/components/account/UserProfilePage.svelte.d.ts +17 -2
- package/dist/components/auto-table-grid.d.ts +26 -0
- package/dist/components/auto-table-grid.js +1 -0
- package/dist/components/buttons/ExportButton.svelte +56 -12
- package/dist/components/buttons/ExportButton.svelte.d.ts +11 -0
- package/dist/components/content/ContentPageHeader.svelte +12 -9
- package/dist/components/content/ContentPageShell.svelte +6 -9
- package/dist/components/content/FileBrowser.svelte +35 -0
- package/dist/components/content/FileBrowser.svelte.d.ts +14 -0
- package/dist/components/content/MetricBlock.svelte +10 -13
- package/dist/components/content/NetworkTable.svelte +3 -3
- package/dist/components/content/NetworkTable.svelte.d.ts +1 -0
- package/dist/components/content/PageToolbar.svelte +6 -10
- package/dist/components/content/ReferenceNotice.svelte +5 -0
- package/dist/components/content/ReferenceNotice.svelte.d.ts +18 -0
- package/dist/components/content/SectionHeader.svelte +8 -13
- package/dist/components/content/SettingsFieldRow.svelte +9 -23
- package/dist/components/content/SettingsGroup.svelte +11 -27
- package/dist/components/content/StatusBadge.svelte +3 -8
- package/dist/components/content/WorkspaceLayout.svelte +8 -12
- package/dist/components/enterprise/filter-tree.d.ts +48 -0
- package/dist/components/enterprise/filter-tree.js +224 -0
- package/dist/components/enterprise/json-schema-form.d.ts +39 -0
- package/dist/components/enterprise/json-schema-form.js +195 -0
- package/dist/components/enterprise/recursive-schema-form.d.ts +30 -0
- package/dist/components/enterprise/recursive-schema-form.js +196 -0
- package/dist/components/enterprise/spreadsheet-formula.d.ts +12 -0
- package/dist/components/enterprise/spreadsheet-formula.js +245 -0
- package/dist/components/export-reference.d.ts +2 -0
- package/dist/components/export-reference.js +9 -0
- package/dist/components/fieldComponentMap.js +2 -0
- package/dist/components/fields/DateField.svelte +12 -12
- package/dist/components/fields/date-display.d.ts +6 -0
- package/dist/components/fields/date-display.js +24 -0
- package/dist/components/fields/date-field-value.d.ts +2 -0
- package/dist/components/fields/date-field-value.js +13 -0
- package/dist/components/json-schema-form-state.d.ts +37 -0
- package/dist/components/json-schema-form-state.js +168 -0
- package/dist/components/network/TeamCrewTablePage.svelte +6 -1
- package/dist/components/network/UserCardsNFTPage.svelte +2 -0
- package/dist/components/profile/PublicProfilePage.svelte +2 -0
- package/dist/components/record-detail.svelte.d.ts +3 -0
- package/dist/components/record-detail.svelte.js +10 -4
- package/dist/components/saved-list-views.d.ts +74 -1
- package/dist/components/saved-list-views.js +191 -3
- package/dist/components/svar-grid-cells.d.ts +14 -0
- package/dist/components/svar-grid-cells.js +2 -0
- package/dist/components/svar-grid-contract.d.ts +80 -0
- package/dist/components/svar-grid-contract.js +159 -0
- package/dist/components/svar-grid-interactions.d.ts +54 -0
- package/dist/components/svar-grid-interactions.js +174 -0
- package/dist/components/svar-grid-loading.d.ts +40 -0
- package/dist/components/svar-grid-loading.js +180 -0
- package/dist/components/svar-grid-operations.d.ts +37 -0
- package/dist/components/svar-grid-operations.js +186 -0
- package/dist/components/svar-grid-panes.d.ts +34 -0
- package/dist/components/svar-grid-panes.js +66 -0
- package/dist/components/svar-grid-resource-loading.d.ts +18 -0
- package/dist/components/svar-grid-resource-loading.js +68 -0
- package/dist/components/svar-grid-resource-session.svelte.d.ts +29 -0
- package/dist/components/svar-grid-resource-session.svelte.js +61 -0
- package/dist/components/table-contract.d.ts +12 -1
- package/dist/components/task-action.svelte.d.ts +13 -0
- package/dist/components/task-action.svelte.js +73 -0
- package/dist/components/ui/DesignSystemShowcase.svelte +164 -0
- package/dist/components/ui/DesignSystemShowcase.svelte.d.ts +3 -0
- package/dist/components/ui/badge/badge-variants.d.ts +3 -2
- package/dist/components/ui/badge/badge-variants.js +2 -1
- package/dist/components/ui/badge/badge.svelte +3 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +1 -0
- package/dist/components/ui/button/button-variants.d.ts +5 -4
- package/dist/components/ui/button/button-variants.js +7 -2
- package/dist/components/ui/checkbox/checkbox.svelte +5 -1
- package/dist/components/ui/checkbox/checkbox.svelte.d.ts +5 -1
- package/dist/components/ui/input/input.svelte +8 -6
- package/dist/components/ui/input/input.svelte.d.ts +4 -1
- package/dist/components/ui/select/select.svelte +4 -1
- package/dist/components/ui/select/select.svelte.d.ts +2 -1
- package/dist/components/ui/textarea/textarea.svelte +2 -1
- package/dist/components.css +3110 -0
- package/dist/date-input-policy.d.ts +4 -0
- package/dist/date-input-policy.js +35 -0
- package/dist/design-contract.d.ts +13 -0
- package/dist/design-contract.js +11 -0
- package/dist/design-system-components.d.ts +100 -0
- package/dist/design-system-components.js +101 -0
- package/dist/design-system.d.ts +15 -0
- package/dist/design-system.js +7 -0
- package/dist/devtools-bridge.d.ts +80 -0
- package/dist/devtools-bridge.js +92 -0
- package/dist/index.d.ts +50 -1
- package/dist/index.js +32 -2
- package/dist/page-chrome.css +12 -49
- package/dist/recipes.d.ts +960 -0
- package/dist/recipes.js +264 -0
- package/dist/rendering/index.d.ts +87 -0
- package/dist/rendering/index.js +117 -0
- package/dist/styles/aliases.css +49 -0
- package/dist/styles/compatibility.epilogue.css +384 -0
- package/dist/styles/compatibility.prelude.css +5513 -0
- package/dist/styles/conditional-states.css +28 -0
- package/dist/styles/conditional-utilities.css +22 -0
- package/dist/tree-table-model.d.ts +24 -0
- package/dist/tree-table-model.js +111 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/package.json +40 -10
- package/styles-compatibility.json +12 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type TreeTableId, type TreeTableSort } from '../tree-table-model.js';
|
|
2
3
|
export interface TreeTableColumn {
|
|
3
4
|
key: string;
|
|
4
5
|
label: string;
|
|
5
6
|
width?: string;
|
|
7
|
+
sortable?: boolean;
|
|
6
8
|
}
|
|
7
9
|
interface Props {
|
|
8
10
|
data: Record<string, unknown>[];
|
|
@@ -21,8 +23,29 @@ interface Props {
|
|
|
21
23
|
record: Record<string, unknown>;
|
|
22
24
|
value: unknown;
|
|
23
25
|
}]>;
|
|
26
|
+
scopeKey?: string | number;
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
error?: string;
|
|
31
|
+
onRetry?: () => void;
|
|
32
|
+
filterText?: string;
|
|
33
|
+
filterable?: boolean;
|
|
34
|
+
filterPlaceholder?: string;
|
|
35
|
+
filterKeys?: string[];
|
|
36
|
+
sort?: TreeTableSort | undefined;
|
|
37
|
+
onsortchange?: (sort: TreeTableSort | undefined) => void;
|
|
38
|
+
page?: number;
|
|
39
|
+
pageSize?: number;
|
|
40
|
+
onpagechange?: (page: number) => void;
|
|
41
|
+
loadChildren?: (record: Record<string, unknown>, id: TreeTableId) => Promise<Record<string, unknown>[]>;
|
|
42
|
+
canLoadChildren?: (record: Record<string, unknown>) => boolean;
|
|
43
|
+
virtualized?: boolean;
|
|
44
|
+
height?: number;
|
|
45
|
+
rowHeight?: number;
|
|
46
|
+
overscan?: number;
|
|
24
47
|
class?: string;
|
|
25
48
|
}
|
|
26
|
-
declare const TreeTable: import("svelte").Component<Props, {}, "selectedKeys">;
|
|
49
|
+
declare const TreeTable: import("svelte").Component<Props, {}, "sort" | "page" | "selectedKeys" | "filterText">;
|
|
27
50
|
type TreeTable = ReturnType<typeof TreeTable>;
|
|
28
51
|
export default TreeTable;
|
|
@@ -1,61 +1,98 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export interface MfaEnrollmentProvider {
|
|
3
|
+
begin: () => Promise<{ secret: string }>;
|
|
4
|
+
verify: (code: string) => Promise<{ recoveryCodes: string[] }>;
|
|
5
|
+
}
|
|
6
|
+
</script>
|
|
7
|
+
|
|
1
8
|
<script lang="ts">
|
|
2
9
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
3
|
-
import {
|
|
10
|
+
import { captureAdminContext } from '@svadmin/core';
|
|
4
11
|
import { Button } from './ui/button/index.js';
|
|
5
|
-
import
|
|
6
|
-
import * as Alert from './ui/alert/index.js';
|
|
12
|
+
import { Input } from './ui/input/index.js';
|
|
7
13
|
import ContentPageShell from './content/ContentPageShell.svelte';
|
|
8
14
|
import ContentPageHeader from './content/ContentPageHeader.svelte';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import { Badge } from './ui/badge/index.js';
|
|
12
|
-
import { Separator } from './ui/separator/index.js';
|
|
15
|
+
import FeedbackNotice from './content/FeedbackNotice.svelte';
|
|
16
|
+
import { Copy, ShieldCheck } from '@lucide/svelte';
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
let { enrollmentProvider }: { enrollmentProvider?: MfaEnrollmentProvider } = $props();
|
|
15
19
|
const i18n = useTranslation();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let
|
|
20
|
+
const context = captureAdminContext();
|
|
21
|
+
const isZh = $derived(i18n.locale === 'zh-CN');
|
|
22
|
+
let secret = $state('');
|
|
23
|
+
let code = $state('');
|
|
24
|
+
let recoveryCodes = $state<string[] | null>(null);
|
|
25
|
+
let pending = $state(false);
|
|
19
26
|
let error = $state('');
|
|
20
27
|
let copied = $state(false);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
let epoch = 0;
|
|
29
|
+
$effect(() => {
|
|
30
|
+
void enrollmentProvider;
|
|
31
|
+
void context.authProvider;
|
|
32
|
+
void context.tenantCacheKey?.__svadminTenant;
|
|
33
|
+
epoch += 1;
|
|
34
|
+
secret = ''; code = ''; recoveryCodes = null; pending = false; error = ''; copied = false;
|
|
35
|
+
return () => { epoch += 1; };
|
|
36
|
+
});
|
|
37
|
+
async function enroll(verify: boolean) {
|
|
38
|
+
const provider = enrollmentProvider;
|
|
39
|
+
if (!provider || pending) return;
|
|
40
|
+
if (verify && !/^\d{6}$/.test(code)) {
|
|
41
|
+
error = i18n.t('auth.twoFactorEnterCode');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const request = epoch;
|
|
45
|
+
pending = true; error = '';
|
|
46
|
+
try {
|
|
47
|
+
if (verify) {
|
|
48
|
+
const result = await provider.verify(code);
|
|
49
|
+
if (request !== epoch) return;
|
|
50
|
+
recoveryCodes = result.recoveryCodes;
|
|
51
|
+
secret = ''; code = '';
|
|
52
|
+
} else {
|
|
53
|
+
const result = await provider.begin();
|
|
54
|
+
if (request !== epoch) return;
|
|
55
|
+
if (!result.secret) throw new Error(isZh ? '未返回登记密钥' : 'No enrollment secret returned');
|
|
56
|
+
secret = result.secret;
|
|
57
|
+
}
|
|
58
|
+
} catch (caught) {
|
|
59
|
+
if (request === epoch) error = caught instanceof Error ? caught.message : String(caught);
|
|
60
|
+
} finally {
|
|
61
|
+
if (request === epoch) pending = false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function copyCodes() {
|
|
65
|
+
if (!recoveryCodes) return;
|
|
66
|
+
const request = epoch;
|
|
67
|
+
try {
|
|
68
|
+
await navigator.clipboard.writeText(recoveryCodes.join('\n'));
|
|
69
|
+
if (request === epoch) copied = true;
|
|
70
|
+
} catch {
|
|
71
|
+
if (request === epoch) error = isZh ? '复制失败,请手动保存恢复码' : 'Copy failed. Save the recovery codes manually.';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
25
74
|
</script>
|
|
26
75
|
|
|
27
76
|
<ContentPageShell pageId="auth-2fa" width="narrow">
|
|
28
|
-
<ContentPageHeader title={i18n.t('auth.twoFactorSetup')}
|
|
29
|
-
{#if !
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{:
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<p class="svadmin-u-b6b02c0ebef6 svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{i18n.t('security.twoFactorActive')}</p>
|
|
50
|
-
</div>
|
|
51
|
-
<Badge variant="secondary" class="svadmin-u-0e12dc7de920 svadmin-u-92e7450ad20d svadmin-u-17a9f7af2265 svadmin-u-76747e5e02ff">
|
|
52
|
-
<CheckCircle2 class="svadmin-u-783b0d9d1e2c" />{i18n.t('securityLog.twoFactorEnabled')}
|
|
53
|
-
</Badge>
|
|
54
|
-
<Separator />
|
|
55
|
-
<Button variant="destructive" onclick={() => { enabled = false; currentStep = 'intro'; }}>
|
|
56
|
-
{i18n.t('auth.twoFactorDisable')}
|
|
57
|
-
</Button>
|
|
58
|
-
</div>
|
|
59
|
-
{/if}
|
|
60
|
-
</Card.CardContent></Card.Card>
|
|
77
|
+
<ContentPageHeader title={i18n.t('auth.twoFactorSetup')} />
|
|
78
|
+
{#if !enrollmentProvider}
|
|
79
|
+
<FeedbackNotice tone="warning" message={isZh ? '未配置验证码登记与验证服务。此页面不会生成模拟密钥、恢复码或宣称已启用 MFA。' : 'Enrollment and verification are not configured. No simulated secrets, recovery codes, or MFA success will be generated.'} />
|
|
80
|
+
<Button variant="outline" onclick={() => context.navigate('/settings/security')}>{i18n.t('settings.security')}</Button>
|
|
81
|
+
{:else if recoveryCodes !== null}
|
|
82
|
+
<p role="status"><ShieldCheck size={18} />{i18n.t('auth.twoFactorVerifySuccess')}</p>
|
|
83
|
+
<p>{i18n.t('auth.twoFactorSaveRecovery')}</p>
|
|
84
|
+
<pre>{recoveryCodes.join('\n')}</pre>
|
|
85
|
+
<Button variant="outline" disabled={recoveryCodes.length === 0} onclick={copyCodes}><Copy size={16} />{copied ? (isZh ? '已复制' : 'Copied') : i18n.t('common.copy')}</Button>
|
|
86
|
+
{:else if secret}
|
|
87
|
+
<p>{isZh ? '将此登记密钥添加到身份验证器,然后输入验证码。' : 'Add this enrollment secret to your authenticator, then enter its verification code.'}</p>
|
|
88
|
+
<code>{secret}</code>
|
|
89
|
+
<form onsubmit={(event) => { event.preventDefault(); void enroll(true); }}>
|
|
90
|
+
<label for="mfa-code">{i18n.t('auth.twoFactorEnterCode')}</label>
|
|
91
|
+
<Input id="mfa-code" bind:value={code} inputmode="numeric" autocomplete="one-time-code" maxlength={6} disabled={pending} />
|
|
92
|
+
<Button type="submit" disabled={pending}>{pending ? i18n.t('common.loading') : i18n.t('common.confirm')}</Button>
|
|
93
|
+
</form>
|
|
94
|
+
{:else}
|
|
95
|
+
<Button disabled={pending} onclick={() => enroll(false)}>{pending ? i18n.t('common.loading') : i18n.t('auth.twoFactorSetup')}</Button>
|
|
96
|
+
{/if}
|
|
97
|
+
{#if error}<p role="alert">{error}</p>{/if}
|
|
61
98
|
</ContentPageShell>
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
export interface MfaEnrollmentProvider {
|
|
2
|
+
begin: () => Promise<{
|
|
3
|
+
secret: string;
|
|
4
|
+
}>;
|
|
5
|
+
verify: (code: string) => Promise<{
|
|
6
|
+
recoveryCodes: string[];
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
type $$ComponentProps = {
|
|
10
|
+
enrollmentProvider?: MfaEnrollmentProvider;
|
|
11
|
+
};
|
|
12
|
+
declare const TwoFactorAuthPage: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
2
13
|
type TwoFactorAuthPage = ReturnType<typeof TwoFactorAuthPage>;
|
|
3
14
|
export default TwoFactorAuthPage;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import { untrack } from 'svelte';
|
|
4
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
5
|
+
import { RotateCw } from '@lucide/svelte';
|
|
6
|
+
import { Button } from './ui/button/index.js';
|
|
3
7
|
import { cn } from '../utils.js';
|
|
4
8
|
|
|
5
9
|
export interface VirtualTableColumn {
|
|
@@ -16,6 +20,11 @@
|
|
|
16
20
|
height?: number;
|
|
17
21
|
overscan?: number;
|
|
18
22
|
rowKey?: string;
|
|
23
|
+
scopeKey?: string | number;
|
|
24
|
+
ariaLabel?: string;
|
|
25
|
+
loading?: boolean;
|
|
26
|
+
error?: string | undefined;
|
|
27
|
+
onRetry?: () => void;
|
|
19
28
|
class?: string;
|
|
20
29
|
rowSnippet?: Snippet<[Record<string, unknown>, number]>;
|
|
21
30
|
}
|
|
@@ -27,34 +36,101 @@
|
|
|
27
36
|
height = 360,
|
|
28
37
|
overscan = 4,
|
|
29
38
|
rowKey = 'id',
|
|
39
|
+
scopeKey,
|
|
40
|
+
ariaLabel,
|
|
41
|
+
loading = false,
|
|
42
|
+
error,
|
|
43
|
+
onRetry,
|
|
30
44
|
class: className = '',
|
|
31
45
|
rowSnippet,
|
|
32
46
|
}: Props = $props();
|
|
33
47
|
|
|
34
48
|
let scrollTop = $state(0);
|
|
49
|
+
let viewport: HTMLDivElement | undefined = $state();
|
|
50
|
+
let observedScope = untrack(() => scopeKey);
|
|
51
|
+
const i18n = useTranslation();
|
|
35
52
|
|
|
36
53
|
function handleScroll(e: Event) {
|
|
37
|
-
const target = e.currentTarget
|
|
38
|
-
scrollTop = target.scrollTop;
|
|
54
|
+
const target = e.currentTarget;
|
|
55
|
+
if (target instanceof HTMLElement) scrollTop = target.scrollTop;
|
|
39
56
|
}
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
59
|
+
if (event.target !== event.currentTarget || event.altKey || event.metaKey || event.ctrlKey) return;
|
|
60
|
+
let next: number;
|
|
61
|
+
switch (event.key) {
|
|
62
|
+
case 'ArrowDown': next = boundedScrollTop + normalizedItemHeight; break;
|
|
63
|
+
case 'ArrowUp': next = boundedScrollTop - normalizedItemHeight; break;
|
|
64
|
+
case 'PageDown': next = boundedScrollTop + normalizedHeight; break;
|
|
65
|
+
case 'PageUp': next = boundedScrollTop - normalizedHeight; break;
|
|
66
|
+
case 'Home': next = 0; break;
|
|
67
|
+
case 'End': next = totalHeight - normalizedHeight; break;
|
|
68
|
+
default: return;
|
|
69
|
+
}
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
scrollTop = Math.min(Math.max(0, next), Math.max(0, totalHeight - normalizedHeight));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 固定高度窗口有明确尺寸上限,防止异常配置退化成全量 DOM。
|
|
75
|
+
const normalizedItemHeight = $derived(Number.isFinite(itemHeight) && itemHeight > 0
|
|
76
|
+
? Math.min(1000, Math.max(16, Math.floor(itemHeight))) : 44);
|
|
77
|
+
const normalizedHeight = $derived(Number.isFinite(height) && height > 0
|
|
78
|
+
? Math.min(4000, Math.max(16, Math.floor(height))) : 360);
|
|
79
|
+
const normalizedOverscan = $derived(Number.isFinite(overscan) && overscan >= 0
|
|
80
|
+
? Math.min(100, Math.floor(overscan)) : 4);
|
|
81
|
+
const identities = $derived.by(() => {
|
|
82
|
+
const keys: string[] = [];
|
|
83
|
+
const seen = new Set<string>();
|
|
84
|
+
for (const [index, item] of items.entries()) {
|
|
85
|
+
const id = item[rowKey];
|
|
86
|
+
if (id != null && typeof id !== 'string' && (typeof id !== 'number' || !Number.isFinite(id))) return undefined;
|
|
87
|
+
const key = id == null ? `index:${index}` : `${typeof id}:${id}`;
|
|
88
|
+
if (seen.has(key)) return undefined;
|
|
89
|
+
seen.add(key);
|
|
90
|
+
keys.push(key);
|
|
91
|
+
}
|
|
92
|
+
return keys;
|
|
93
|
+
});
|
|
94
|
+
const displayError = $derived(error || (identities === undefined ? i18n.t('virtualTable.invalidKeys') : ''));
|
|
95
|
+
const available = $derived(!loading && !displayError);
|
|
96
|
+
const totalHeight = $derived(available ? items.length * normalizedItemHeight : 0);
|
|
97
|
+
const boundedScrollTop = $derived(Math.min(
|
|
98
|
+
Math.max(0, Number.isFinite(scrollTop) ? scrollTop : 0),
|
|
99
|
+
Math.max(0, totalHeight - normalizedHeight),
|
|
100
|
+
));
|
|
101
|
+
const startIndex = $derived(Math.max(0, Math.floor(boundedScrollTop / normalizedItemHeight) - normalizedOverscan));
|
|
102
|
+
const endIndex = $derived(Math.min(items.length, Math.ceil((boundedScrollTop + normalizedHeight) / normalizedItemHeight) + normalizedOverscan));
|
|
44
103
|
const visibleSlice = $derived(
|
|
45
|
-
items.slice(startIndex, endIndex).map((item, idx) => ({
|
|
104
|
+
(available ? items.slice(startIndex, endIndex) : []).map((item, idx) => ({
|
|
46
105
|
item,
|
|
47
106
|
index: startIndex + idx,
|
|
48
|
-
top: (startIndex + idx) *
|
|
107
|
+
top: (startIndex + idx) * normalizedItemHeight,
|
|
108
|
+
key: identities?.[startIndex + idx] ?? `index:${startIndex + idx}`,
|
|
49
109
|
}))
|
|
50
110
|
);
|
|
111
|
+
|
|
112
|
+
$effect.pre(() => {
|
|
113
|
+
if (scopeKey !== observedScope) {
|
|
114
|
+
observedScope = scopeKey;
|
|
115
|
+
scrollTop = 0;
|
|
116
|
+
if (viewport) viewport.scrollTop = 0;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
$effect(() => {
|
|
120
|
+
const top = boundedScrollTop;
|
|
121
|
+
if (viewport && viewport.scrollTop !== top) viewport.scrollTop = top;
|
|
122
|
+
if (scrollTop !== top) scrollTop = top;
|
|
123
|
+
});
|
|
51
124
|
</script>
|
|
52
125
|
|
|
53
|
-
<div
|
|
126
|
+
<div role="table" aria-label={ariaLabel ?? i18n.t('virtualTable.label')}
|
|
127
|
+
aria-rowcount={(available ? items.length : 0) + 1} aria-colcount={columns.length} aria-busy={loading}
|
|
128
|
+
class={cn('svadmin-u-6da6a3c3f741 svadmin-u-a217b4eaa918 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558 svadmin-u-cef5b893cf23 svadmin-u-2cd02d11d1af svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-359090c2d529', className)}>
|
|
54
129
|
<!-- Sticky Header -->
|
|
55
|
-
<div class="svadmin-u-65fdbade2025 svadmin-u-c9ed8c5f79ae svadmin-u-b00f43c30c2b svadmin-u-e83a7042bc91 svadmin-u-bfa603190748 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-426b8b75185b svadmin-u-f0faeb26d656 svadmin-u-7f6912283f11 svadmin-u-012fbd121f37">
|
|
130
|
+
<div role="row" aria-rowindex="1" class="svadmin-u-65fdbade2025 svadmin-u-c9ed8c5f79ae svadmin-u-b00f43c30c2b svadmin-u-e83a7042bc91 svadmin-u-bfa603190748 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-426b8b75185b svadmin-u-f0faeb26d656 svadmin-u-7f6912283f11 svadmin-u-012fbd121f37">
|
|
56
131
|
{#each columns as col (col.key)}
|
|
57
132
|
<div
|
|
133
|
+
role="columnheader"
|
|
58
134
|
style={col.width ? `width: ${col.width}; flex-shrink: 0;` : 'flex: 1; min-width: 0;'}
|
|
59
135
|
class={cn('svadmin-u-f283ea9bea0e svadmin-u-d5eab218aa34', col.align === 'right' ? 'svadmin-u-308fc069e46e' : col.align === 'center' ? 'svadmin-u-ca6bf63030aa' : 'svadmin-u-2eba0d65d059')}
|
|
60
136
|
>
|
|
@@ -64,22 +140,35 @@
|
|
|
64
140
|
</div>
|
|
65
141
|
|
|
66
142
|
<!-- Virtual Scroll Viewport -->
|
|
143
|
+
<!-- 只读表格的滚动区域需要独立焦点;方向键只滚动,不伪装成可编辑网格。 -->
|
|
144
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
145
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
67
146
|
<div
|
|
147
|
+
bind:this={viewport}
|
|
148
|
+
role="region"
|
|
149
|
+
tabindex="0"
|
|
150
|
+
aria-label={ariaLabel ?? i18n.t('virtualTable.label')}
|
|
151
|
+
data-testid="virtual-table-viewport"
|
|
68
152
|
class="svadmin-u-92bf82f493b1 svadmin-u-d89972fe17d6 svadmin-u-6da6a3c3f741"
|
|
69
|
-
style="height: {
|
|
153
|
+
style="height: {normalizedHeight}px; min-height: 0; flex-shrink: 0; overflow: auto;"
|
|
70
154
|
onscroll={handleScroll}
|
|
155
|
+
onkeydown={handleKeydown}
|
|
71
156
|
>
|
|
72
|
-
<div style="height: {totalHeight}px; width: 100%; position: relative;">
|
|
73
|
-
{#
|
|
157
|
+
<div role="rowgroup" style="height: {totalHeight}px; width: 100%; position: relative;">
|
|
158
|
+
{#key scopeKey}
|
|
159
|
+
{#each visibleSlice as { item, index, top, key } (key)}
|
|
74
160
|
<div
|
|
161
|
+
role="row"
|
|
162
|
+
aria-rowindex={index + 2}
|
|
75
163
|
class="svadmin-u-da4dbfbc4fdc svadmin-u-c78facc7a0a6 svadmin-u-d8cdcad240d1 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-f0faeb26d656 svadmin-u-65fdbade2025 svadmin-u-6ee2d41e2d2d svadmin-u-12251b8f1749 svadmin-u-ceb69a6b0e5f"
|
|
76
|
-
style="top: {top}px; height: {
|
|
164
|
+
style="top: {top}px; height: {normalizedItemHeight}px; overflow: hidden;"
|
|
77
165
|
>
|
|
78
166
|
{#if rowSnippet}
|
|
79
167
|
{@render rowSnippet(item, index)}
|
|
80
168
|
{:else}
|
|
81
169
|
{#each columns as col (col.key)}
|
|
82
170
|
<div
|
|
171
|
+
role="cell"
|
|
83
172
|
style={col.width ? `width: ${col.width}; flex-shrink: 0;` : 'flex: 1; min-width: 0;'}
|
|
84
173
|
class={cn('svadmin-u-f283ea9bea0e svadmin-u-d5eab218aa34 svadmin-u-2689f3958069 svadmin-u-d4108abe6359', col.align === 'right' ? 'svadmin-u-308fc069e46e' : col.align === 'center' ? 'svadmin-u-ca6bf63030aa' : 'svadmin-u-2eba0d65d059')}
|
|
85
174
|
>
|
|
@@ -89,18 +178,25 @@
|
|
|
89
178
|
{/if}
|
|
90
179
|
</div>
|
|
91
180
|
{/each}
|
|
181
|
+
{/key}
|
|
92
182
|
|
|
93
|
-
{#if items.length === 0}
|
|
94
|
-
<div class="svadmin-u-b5f3ff77f4f9 svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-bfa603190748 svadmin-u-359090c2d529">
|
|
95
|
-
No records to display
|
|
96
|
-
</div>
|
|
97
|
-
{/if}
|
|
98
183
|
</div>
|
|
184
|
+
{#if loading}
|
|
185
|
+
<div role="status">{i18n.t('common.loading')}</div>
|
|
186
|
+
{:else if displayError}
|
|
187
|
+
<div role="alert">{displayError}</div>
|
|
188
|
+
{#if error && onRetry}
|
|
189
|
+
<Button type="button" variant="ghost" size="sm" onclick={onRetry}>
|
|
190
|
+
<RotateCw aria-hidden="true" />{i18n.t('common.retry')}
|
|
191
|
+
</Button>
|
|
192
|
+
{/if}
|
|
193
|
+
{:else if items.length === 0}
|
|
194
|
+
<div role="status">{i18n.t('common.noData')}</div>
|
|
195
|
+
{/if}
|
|
99
196
|
</div>
|
|
100
197
|
|
|
101
198
|
<!-- Footer Info -->
|
|
102
199
|
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-f0faeb26d656 svadmin-u-03b4dd7f172b svadmin-u-b950dda299d3 svadmin-u-05faf5c801ff svadmin-u-967d113a1451 svadmin-u-d058ca6de60f svadmin-u-bfa603190748 svadmin-u-012fbd121f37">
|
|
103
|
-
<span>
|
|
104
|
-
<span>Rendered {visibleSlice.length} in viewport</span>
|
|
200
|
+
<span>{i18n.t('virtualTable.totalRows', { count: String(available ? items.length : 0) })}</span>
|
|
105
201
|
</div>
|
|
106
202
|
</div>
|
|
@@ -12,6 +12,11 @@ interface Props {
|
|
|
12
12
|
height?: number;
|
|
13
13
|
overscan?: number;
|
|
14
14
|
rowKey?: string;
|
|
15
|
+
scopeKey?: string | number;
|
|
16
|
+
ariaLabel?: string;
|
|
17
|
+
loading?: boolean;
|
|
18
|
+
error?: string | undefined;
|
|
19
|
+
onRetry?: () => void;
|
|
15
20
|
class?: string;
|
|
16
21
|
rowSnippet?: Snippet<[Record<string, unknown>, number]>;
|
|
17
22
|
}
|
|
@@ -1,47 +1,83 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { captureAdminContext, type Organization } from '@svadmin/core';
|
|
2
3
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
3
|
-
import { Loader2 } from '@lucide/svelte';
|
|
4
|
-
import { Button } from '../ui/button/index.js';
|
|
5
|
-
import { Input } from '../ui/input/index.js';
|
|
6
|
-
import { Label } from '../ui/label/index.js';
|
|
7
|
-
import { Textarea } from '../ui/textarea/index.js';
|
|
8
4
|
import ContentPageShell from '../content/ContentPageShell.svelte';
|
|
9
5
|
import ContentPageHeader from '../content/ContentPageHeader.svelte';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
import DataState from '../content/DataState.svelte';
|
|
7
|
+
import FeedbackNotice from '../content/FeedbackNotice.svelte';
|
|
8
|
+
import { Input } from '../ui/input/index.js';
|
|
9
|
+
import { Button } from '../ui/button/index.js';
|
|
10
|
+
|
|
11
|
+
const context = captureAdminContext();
|
|
16
12
|
const i18n = useTranslation();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let
|
|
20
|
-
let
|
|
13
|
+
const isZh = $derived(i18n.locale === 'zh-CN');
|
|
14
|
+
const provider = $derived(context.organizationProvider);
|
|
15
|
+
let organization = $state<Organization | null>(null);
|
|
16
|
+
let name = $state('');
|
|
17
|
+
let loading = $state(false);
|
|
21
18
|
let saving = $state(false);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
let error = $state('');
|
|
20
|
+
let loadError = $state('');
|
|
21
|
+
let status = $state('');
|
|
22
|
+
let epoch = 0;
|
|
23
|
+
async function load() {
|
|
24
|
+
const current = provider;
|
|
25
|
+
const request = ++epoch;
|
|
26
|
+
organization = null; name = ''; error = ''; loadError = ''; status = ''; saving = false;
|
|
27
|
+
loading = Boolean(current);
|
|
28
|
+
if (!current) return;
|
|
29
|
+
try {
|
|
30
|
+
const result = await current.getCurrentOrganization(context.enterpriseRequestContext);
|
|
31
|
+
if (request !== epoch) return;
|
|
32
|
+
organization = result; name = result?.name ?? '';
|
|
33
|
+
} catch (caught) {
|
|
34
|
+
if (request === epoch) loadError = caught instanceof Error ? caught.message : String(caught);
|
|
35
|
+
} finally {
|
|
36
|
+
if (request === epoch) loading = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
$effect(() => {
|
|
40
|
+
void provider; void context.authProvider; void context.tenantCacheKey?.__svadminTenant;
|
|
41
|
+
void load();
|
|
42
|
+
return () => { epoch += 1; };
|
|
43
|
+
});
|
|
44
|
+
async function save(event: SubmitEvent) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
const current = provider;
|
|
47
|
+
if (!current?.updateCurrentOrganization || !organization || !name.trim() || saving) return;
|
|
48
|
+
const request = epoch;
|
|
49
|
+
saving = true; error = ''; status = '';
|
|
50
|
+
try {
|
|
51
|
+
const result = await current.updateCurrentOrganization({ name: name.trim() }, context.enterpriseRequestContext);
|
|
52
|
+
if (request !== epoch) return;
|
|
53
|
+
organization = result; name = result.name;
|
|
54
|
+
status = isZh ? '组织信息已保存' : 'Organization saved';
|
|
55
|
+
} catch (caught) {
|
|
56
|
+
if (request === epoch) error = caught instanceof Error ? caught.message : String(caught);
|
|
57
|
+
} finally {
|
|
58
|
+
if (request === epoch) saving = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
29
61
|
</script>
|
|
30
62
|
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
<ContentPageShell pageId="account-company-profile" width="narrow">
|
|
64
|
+
<ContentPageHeader title={i18n.t('account.companyProfile')} />
|
|
65
|
+
{#if !provider}
|
|
66
|
+
<FeedbackNotice tone="warning" message={isZh ? '未配置组织资料服务,无法加载或保存公司资料。' : 'OrganizationProvider is not configured. Company details cannot be loaded or saved.'} />
|
|
67
|
+
{:else if loading}
|
|
68
|
+
<DataState state="loading" />
|
|
69
|
+
{:else if loadError}
|
|
70
|
+
<DataState state="error" description={loadError} retry={load} />
|
|
71
|
+
{:else if !organization}
|
|
72
|
+
<DataState state="empty" title={isZh ? '暂无组织资料' : 'No organization details'} />
|
|
73
|
+
{:else}
|
|
74
|
+
<form onsubmit={save}>
|
|
75
|
+
<label for="company-name">{isZh ? '组织名称' : 'Organization name'}</label>
|
|
76
|
+
<Input id="company-name" bind:value={name} required disabled={saving || !provider.updateCurrentOrganization} />
|
|
77
|
+
{#if !provider.updateCurrentOrganization}<p>{isZh ? '当前组织资料为只读。' : 'Organization details are read-only.'}</p>{/if}
|
|
78
|
+
<Button type="submit" disabled={saving || !provider.updateCurrentOrganization || !name.trim() || name.trim() === organization.name}>{saving ? i18n.t('common.loading') : i18n.t('common.save')}</Button>
|
|
79
|
+
{#if error}<p role="alert">{error}</p>{/if}
|
|
80
|
+
{#if status}<p role="status">{status}</p>{/if}
|
|
81
|
+
</form>
|
|
82
|
+
{/if}
|
|
47
83
|
</ContentPageShell>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import ContentPageHeader from '../content/ContentPageHeader.svelte';
|
|
9
9
|
|
|
10
10
|
const i18n = useTranslation();
|
|
11
|
-
let completed = $state<string[]>([
|
|
11
|
+
let completed = $state<string[]>([]);
|
|
12
12
|
const isZh = $derived(i18n.locale === 'zh-CN');
|
|
13
13
|
const entries = $derived([
|
|
14
14
|
{ id: 'profile', title: isZh ? '个人资料' : 'User profile', description: isZh ? '维护联系人、工作状态、文件和日历连接。' : 'Maintain contact details, work status, files, and calendar connections.', href: '#/account/home/user-profile', icon: UserRound },
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
</script>
|
|
27
27
|
|
|
28
28
|
<ContentPageShell pageId="account-get-started" width="wide">
|
|
29
|
+
<p role="note">{isZh ? '本会话的个人检查清单;手动勾选不代表组织或安全配置已生效。' : 'A personal checklist for this session. Checking an item does not activate organization or security settings.'}</p>
|
|
29
30
|
<div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-020ba687fa12 svadmin-u-64cac80d2e9a svadmin-u-3b9871a0bf93">
|
|
30
31
|
<ContentPageHeader title={i18n.t('account.getStarted')} description={isZh ? '从账户功能入口快速完成组织、成员、安全和集成配置。' : 'Use the account hub to complete organization, member, security, and integration setup.'} />
|
|
31
32
|
<Badge variant="outline">{progress}% {isZh ? '已完成' : 'complete'}</Badge>
|