@svadmin/ui 0.73.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 +53 -28
- package/dist/app.css +3733 -4119
- package/dist/app.theme.css +3733 -4119
- 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 +33 -3
- package/dist/components/AutoForm.svelte.d.ts +1 -0
- package/dist/components/AutoTable.svelte +610 -47
- package/dist/components/AutoTable.svelte.d.ts +17 -1
- package/dist/components/BoundRecordDetailDrawer.svelte +9 -0
- package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +2 -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 +114 -27
- package/dist/components/ComboboxField.svelte.d.ts +1 -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 +85 -5
- package/dist/components/DateRangeInput.svelte.d.ts +10 -0
- package/dist/components/DateRangePicker.svelte +10 -0
- package/dist/components/DateRangePicker.svelte.d.ts +6 -0
- package/dist/components/DateTimeInput.svelte +74 -5
- package/dist/components/DateTimeInput.svelte.d.ts +8 -0
- package/dist/components/DateTimePicker.svelte +10 -0
- package/dist/components/DateTimePicker.svelte.d.ts +6 -0
- package/dist/components/DevTools.svelte +34 -26
- package/dist/components/DevframePanel.svelte +123 -0
- package/dist/components/DevframePanel.svelte.d.ts +3 -0
- package/dist/components/ErrorSummary.svelte +52 -0
- package/dist/components/ErrorSummary.svelte.d.ts +14 -0
- package/dist/components/FieldRenderer.svelte +82 -81
- package/dist/components/FileUpload.svelte +214 -52
- package/dist/components/FileUpload.svelte.d.ts +11 -0
- package/dist/components/FilterBuilder.svelte +88 -66
- 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 +339 -17
- package/dist/components/ImportWizard.svelte.d.ts +11 -0
- package/dist/components/JsonSchemaForm.svelte +120 -52
- package/dist/components/JsonSchemaForm.svelte.d.ts +7 -1
- package/dist/components/KanbanBoard.svelte +195 -40
- package/dist/components/KanbanBoard.svelte.d.ts +9 -2
- package/dist/components/Layout.svelte +4 -2
- 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/ListPage.svelte +5 -1
- package/dist/components/ListPage.svelte.d.ts +3 -0
- package/dist/components/LoginPage.svelte +1 -1
- 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/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/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 +4 -2
- package/dist/components/RecordDetailDrawer.svelte.d.ts +2 -0
- package/dist/components/RelationPicker.svelte +341 -0
- package/dist/components/RelationPicker.svelte.d.ts +26 -0
- package/dist/components/ResourceOperationsPage.svelte +18 -18
- package/dist/components/ResourceOperationsPage.svelte.d.ts +2 -2
- 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/ShowPage.svelte +18 -3
- package/dist/components/ShowPage.svelte.d.ts +3 -0
- package/dist/components/Sidebar.svelte +2 -2
- package/dist/components/Slider.svelte +100 -0
- package/dist/components/Slider.svelte.d.ts +19 -0
- package/dist/components/SpreadsheetView.svelte +1213 -95
- package/dist/components/SpreadsheetView.svelte.d.ts +19 -0
- package/dist/components/TagsInput.svelte +178 -0
- package/dist/components/TagsInput.svelte.d.ts +17 -0
- package/dist/components/TimePicker.svelte +10 -0
- package/dist/components/TimePicker.svelte.d.ts +6 -0
- package/dist/components/TreeSelect.svelte +267 -53
- package/dist/components/TreeSelect.svelte.d.ts +3 -0
- 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/buttons/ExportButton.svelte +56 -12
- package/dist/components/buttons/ExportButton.svelte.d.ts +11 -0
- package/dist/components/content/ContentPageHeader.svelte +1 -1
- package/dist/components/content/ContentPageShell.svelte +1 -1
- package/dist/components/content/MetricBlock.svelte +1 -1
- package/dist/components/content/PageToolbar.svelte +1 -1
- package/dist/components/content/ReferenceNotice.svelte +5 -0
- package/dist/components/content/ReferenceNotice.svelte.d.ts +18 -0
- package/dist/components/content/SectionHeader.svelte +1 -1
- package/dist/components/content/SettingsFieldRow.svelte +1 -1
- package/dist/components/content/SettingsGroup.svelte +1 -1
- package/dist/components/content/StatusBadge.svelte +1 -1
- package/dist/components/content/WorkspaceLayout.svelte +1 -1
- package/dist/components/export-reference.d.ts +2 -0
- package/dist/components/export-reference.js +9 -0
- package/dist/components/fieldComponentMap.js +2 -5
- package/dist/components/fields/DateField.svelte +12 -13
- package/dist/components/fields/date-display.d.ts +6 -0
- package/dist/components/fields/date-display.js +24 -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/saved-list-views.d.ts +74 -1
- package/dist/components/saved-list-views.js +191 -3
- 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 -3
- 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 -5
- 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 +7 -2
- 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 -2
- package/dist/components.css +33 -2
- package/dist/date-input-policy.d.ts +4 -0
- package/dist/date-input-policy.js +35 -0
- package/dist/design-system-components.d.ts +100 -0
- package/dist/design-system-components.js +101 -0
- package/dist/design-system.js +1 -1
- package/dist/devtools-bridge.d.ts +80 -0
- package/dist/devtools-bridge.js +92 -0
- package/dist/index.d.ts +37 -2
- package/dist/index.js +23 -2
- package/dist/recipes.d.ts +960 -4
- package/dist/recipes.js +264 -4
- package/dist/tree-table-model.d.ts +24 -0
- package/dist/tree-table-model.js +111 -0
- package/package.json +22 -10
- package/dist/styled-system/css/conditions.js +0 -36
- package/dist/styled-system/css/css.d.ts +0 -22
- package/dist/styled-system/css/css.js +0 -45
- package/dist/styled-system/css/cva.d.ts +0 -6
- package/dist/styled-system/css/cva.js +0 -87
- package/dist/styled-system/css/cx.d.ts +0 -5
- package/dist/styled-system/css/cx.js +0 -15
- package/dist/styled-system/css/index.d.ts +0 -5
- package/dist/styled-system/css/index.js +0 -4
- package/dist/styled-system/css/sva.d.ts +0 -4
- package/dist/styled-system/css/sva.js +0 -46
- package/dist/styled-system/helpers.js +0 -316
- package/dist/styled-system/patterns/aspect-ratio.d.ts +0 -20
- package/dist/styled-system/patterns/aspect-ratio.js +0 -38
- package/dist/styled-system/patterns/bleed.d.ts +0 -21
- package/dist/styled-system/patterns/bleed.js +0 -24
- package/dist/styled-system/patterns/box.d.ts +0 -20
- package/dist/styled-system/patterns/box.js +0 -15
- package/dist/styled-system/patterns/center.d.ts +0 -20
- package/dist/styled-system/patterns/center.js +0 -21
- package/dist/styled-system/patterns/circle.d.ts +0 -20
- package/dist/styled-system/patterns/circle.js +0 -25
- package/dist/styled-system/patterns/container.d.ts +0 -20
- package/dist/styled-system/patterns/container.js +0 -21
- package/dist/styled-system/patterns/cq.d.ts +0 -21
- package/dist/styled-system/patterns/cq.js +0 -21
- package/dist/styled-system/patterns/divider.d.ts +0 -22
- package/dist/styled-system/patterns/divider.js +0 -25
- package/dist/styled-system/patterns/flex.d.ts +0 -26
- package/dist/styled-system/patterns/flex.js +0 -26
- package/dist/styled-system/patterns/float.d.ts +0 -23
- package/dist/styled-system/patterns/float.js +0 -52
- package/dist/styled-system/patterns/grid-item.d.ts +0 -25
- package/dist/styled-system/patterns/grid-item.js +0 -25
- package/dist/styled-system/patterns/grid.d.ts +0 -24
- package/dist/styled-system/patterns/grid.js +0 -27
- package/dist/styled-system/patterns/hstack.d.ts +0 -21
- package/dist/styled-system/patterns/hstack.js +0 -24
- package/dist/styled-system/patterns/index.d.ts +0 -21
- package/dist/styled-system/patterns/index.js +0 -20
- package/dist/styled-system/patterns/link-overlay.d.ts +0 -20
- package/dist/styled-system/patterns/link-overlay.js +0 -24
- package/dist/styled-system/patterns/spacer.d.ts +0 -20
- package/dist/styled-system/patterns/spacer.js +0 -25
- package/dist/styled-system/patterns/square.d.ts +0 -20
- package/dist/styled-system/patterns/square.js +0 -24
- package/dist/styled-system/patterns/stack.d.ts +0 -23
- package/dist/styled-system/patterns/stack.js +0 -24
- package/dist/styled-system/patterns/visually-hidden.d.ts +0 -20
- package/dist/styled-system/patterns/visually-hidden.js +0 -18
- package/dist/styled-system/patterns/vstack.d.ts +0 -21
- package/dist/styled-system/patterns/vstack.js +0 -24
- package/dist/styled-system/patterns/wrap.d.ts +0 -24
- package/dist/styled-system/patterns/wrap.js +0 -25
- package/dist/styled-system/recipes/content-header.d.ts +0 -31
- package/dist/styled-system/recipes/content-header.js +0 -65
- package/dist/styled-system/recipes/content-page.d.ts +0 -34
- package/dist/styled-system/recipes/content-page.js +0 -43
- package/dist/styled-system/recipes/create-recipe.js +0 -82
- package/dist/styled-system/recipes/index.d.ts +0 -17
- package/dist/styled-system/recipes/index.js +0 -16
- package/dist/styled-system/recipes/metric-block.d.ts +0 -34
- package/dist/styled-system/recipes/metric-block.js +0 -76
- package/dist/styled-system/recipes/product-list.d.ts +0 -31
- package/dist/styled-system/recipes/product-list.js +0 -73
- package/dist/styled-system/recipes/product-section.d.ts +0 -31
- package/dist/styled-system/recipes/product-section.js +0 -49
- package/dist/styled-system/recipes/product-settings-row.d.ts +0 -34
- package/dist/styled-system/recipes/product-settings-row.js +0 -58
- package/dist/styled-system/recipes/product-settings.d.ts +0 -31
- package/dist/styled-system/recipes/product-settings.js +0 -57
- package/dist/styled-system/recipes/product-status.d.ts +0 -34
- package/dist/styled-system/recipes/product-status.js +0 -45
- package/dist/styled-system/recipes/product-toolbar.d.ts +0 -31
- package/dist/styled-system/recipes/product-toolbar.js +0 -41
- package/dist/styled-system/recipes/product-workspace.d.ts +0 -34
- package/dist/styled-system/recipes/product-workspace.js +0 -58
- package/dist/styled-system/recipes/surface-metric.d.ts +0 -38
- package/dist/styled-system/recipes/surface-metric.js +0 -63
- package/dist/styled-system/recipes/surface-table.d.ts +0 -34
- package/dist/styled-system/recipes/surface-table.js +0 -62
- package/dist/styled-system/recipes/ui-badge.d.ts +0 -31
- package/dist/styled-system/recipes/ui-badge.js +0 -38
- package/dist/styled-system/recipes/ui-button.d.ts +0 -32
- package/dist/styled-system/recipes/ui-button.js +0 -43
- package/dist/styled-system/recipes/ui-input.d.ts +0 -31
- package/dist/styled-system/recipes/ui-input.js +0 -49
- package/dist/styled-system/recipes/ui-textarea.d.ts +0 -31
- package/dist/styled-system/recipes/ui-textarea.js +0 -24
- package/dist/styled-system/styles.css +0 -1257
- package/dist/styled-system/tokens/index.d.ts +0 -9
- package/dist/styled-system/tokens/index.js +0 -268
- package/dist/styled-system/tokens/tokens.d.ts +0 -36
- package/dist/styled-system/types/composition.d.ts +0 -227
- package/dist/styled-system/types/conditions.d.ts +0 -236
- package/dist/styled-system/types/csstype.d.ts +0 -22570
- package/dist/styled-system/types/index.d.ts +0 -6
- package/dist/styled-system/types/parts.d.ts +0 -8
- package/dist/styled-system/types/pattern.d.ts +0 -78
- package/dist/styled-system/types/prop-type.d.ts +0 -223
- package/dist/styled-system/types/recipe.d.ts +0 -181
- package/dist/styled-system/types/selectors.d.ts +0 -59
- package/dist/styled-system/types/static-css.d.ts +0 -56
- package/dist/styled-system/types/style-props.d.ts +0 -8088
- package/dist/styled-system/types/system-types.d.ts +0 -151
- package/dist/styles/recipes.css +0 -1262
|
@@ -40,9 +40,16 @@
|
|
|
40
40
|
cell_getValue,
|
|
41
41
|
} from '@tanstack/table-core/static-functions';
|
|
42
42
|
|
|
43
|
-
import { captureAdminContext, DeleteManyPartialError, getAdminOptions, getContractFormFields, useNavigation, useParsed, useResourceContract, useList, useDeleteMany, downloadData } from '@svadmin/core';
|
|
43
|
+
import { captureAdminContext, captureAuthSession, useGetIdentity, DeleteManyPartialError, getAdminOptions, getContractFormFields, useNavigation, useParsed, useResourceContract, useList, useDeleteMany, downloadData, type ExportFormat, type TaskProvider } from '@svadmin/core';
|
|
44
44
|
import { decodeBaseRecord, snapshotPlainData, parseContractRouteId, formatContractRouteId } from '@svadmin/core/schema';
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
checkedTableRows,
|
|
47
|
+
copyTableRecord,
|
|
48
|
+
tableExportRows,
|
|
49
|
+
tableRowKey,
|
|
50
|
+
type BatchSelection,
|
|
51
|
+
type TableRecord,
|
|
52
|
+
} from './table-contract';
|
|
46
53
|
import type {
|
|
47
54
|
BaseRecord,
|
|
48
55
|
FieldDefinition,
|
|
@@ -60,6 +67,11 @@
|
|
|
60
67
|
cloneSavedListViewState,
|
|
61
68
|
columnOrderStorageKey,
|
|
62
69
|
columnVisibilityStorageKey,
|
|
70
|
+
decodeRemoteSavedListViews,
|
|
71
|
+
decodeSavedListViewMutationResult,
|
|
72
|
+
decodeSavedListViewRemoveResult,
|
|
73
|
+
decodeSavedListViewAccess,
|
|
74
|
+
decodeSavedListViewSubjects,
|
|
63
75
|
legacyActiveSavedListViewStorageKey,
|
|
64
76
|
legacyColumnOrderStorageKey,
|
|
65
77
|
legacyColumnVisibilityStorageKey,
|
|
@@ -70,7 +82,10 @@
|
|
|
70
82
|
serializeSavedListViews,
|
|
71
83
|
type ListPreferenceScope,
|
|
72
84
|
type SavedListView,
|
|
85
|
+
type SavedListViewAccess,
|
|
86
|
+
type SavedListViewSubject,
|
|
73
87
|
type SavedListViewState,
|
|
88
|
+
type SavedListViewProvider,
|
|
74
89
|
} from './saved-list-views.js';
|
|
75
90
|
|
|
76
91
|
import { fade } from 'svelte/transition';
|
|
@@ -86,7 +101,7 @@
|
|
|
86
101
|
import * as ContextMenu from './ui/context-menu/index.js';
|
|
87
102
|
import * as Select from './ui/select/index.js';
|
|
88
103
|
import {
|
|
89
|
-
Plus, Pencil, Trash2,
|
|
104
|
+
Plus, Pencil, Trash2, Star,
|
|
90
105
|
Search, Download, ChevronDown, ChevronUp, SlidersHorizontal, Filter as FilterIcon,
|
|
91
106
|
Eye, Copy, RefreshCw, Rows, X, Bookmark, Check
|
|
92
107
|
} from '@lucide/svelte';
|
|
@@ -97,6 +112,7 @@
|
|
|
97
112
|
import CanAccess from './CanAccess.svelte';
|
|
98
113
|
import TooltipButton from './TooltipButton.svelte';
|
|
99
114
|
import InlineEdit from './InlineEdit.svelte';
|
|
115
|
+
import ExportButton from './buttons/ExportButton.svelte';
|
|
100
116
|
import FieldDisplay from './FieldDisplay.svelte';
|
|
101
117
|
import DraggableHeader from './DraggableHeader.svelte';
|
|
102
118
|
import DataState from './content/DataState.svelte';
|
|
@@ -111,6 +127,8 @@
|
|
|
111
127
|
/** 可选主体渲染器:不替换资源、权限、工具栏和持久化逻辑。 */
|
|
112
128
|
gridBody?: Snippet<[AutoTableGridState]>;
|
|
113
129
|
selectable?: boolean;
|
|
130
|
+
/** 仅在业务回调支持按查询执行服务端命令时启用。 */
|
|
131
|
+
allowSelectAllMatching?: boolean;
|
|
114
132
|
density?: 'compact' | 'comfortable';
|
|
115
133
|
showHeader?: boolean;
|
|
116
134
|
title?: string;
|
|
@@ -133,11 +151,21 @@
|
|
|
133
151
|
pagination?: { current: number; pageSize: number };
|
|
134
152
|
/** Externally controlled sorters */
|
|
135
153
|
sorters?: Sort[];
|
|
136
|
-
/**
|
|
137
|
-
batchActions?: Snippet<[{ selectedIds: (string | number)[] }]>;
|
|
154
|
+
/** 批量操作必须按 selection.scope 区分明确 ID 与全部查询结果。 */
|
|
155
|
+
batchActions?: Snippet<[{ selectedIds: (string | number)[]; selection: BatchSelection }]>;
|
|
138
156
|
/** Summary row rendered at table footer */
|
|
139
157
|
summary?: Snippet<[{ data: BaseRecord[]; total: number; visibleColumnsCount: number }]>;
|
|
140
158
|
deleteVariables?: unknown;
|
|
159
|
+
/** 团队/系统视图;写入需要 Provider 能力和显式可写标记。 */
|
|
160
|
+
savedViewProvider?: SavedListViewProvider;
|
|
161
|
+
/** 显式允许唯一的远程默认视图在无 URL/个人活动视图时初始化页面。 */
|
|
162
|
+
applyRemoteDefaultView?: boolean;
|
|
163
|
+
/** 提供任务名后按当前筛选/排序提交全量导出;未提供时仍导出当前已加载页。 */
|
|
164
|
+
exportTaskName?: string;
|
|
165
|
+
exportTaskProvider?: TaskProvider;
|
|
166
|
+
exportTaskIdempotencyKey?: string;
|
|
167
|
+
exportFormat?: ExportFormat;
|
|
168
|
+
exportMaxItemCount?: number;
|
|
141
169
|
}
|
|
142
170
|
|
|
143
171
|
let {
|
|
@@ -145,6 +173,7 @@
|
|
|
145
173
|
rendering,
|
|
146
174
|
gridBody,
|
|
147
175
|
selectable = true,
|
|
176
|
+
allowSelectAllMatching = false,
|
|
148
177
|
density = 'comfortable',
|
|
149
178
|
showHeader = true,
|
|
150
179
|
title,
|
|
@@ -162,9 +191,18 @@
|
|
|
162
191
|
pagination: externalPagination,
|
|
163
192
|
sorters: externalSorters,
|
|
164
193
|
deleteVariables,
|
|
194
|
+
savedViewProvider,
|
|
195
|
+
applyRemoteDefaultView = false,
|
|
196
|
+
exportTaskName,
|
|
197
|
+
exportTaskProvider,
|
|
198
|
+
exportTaskIdempotencyKey,
|
|
199
|
+
exportFormat = 'csv',
|
|
200
|
+
exportMaxItemCount,
|
|
165
201
|
}: Props = $props();
|
|
166
202
|
|
|
167
203
|
let densityOverride = $state<'compact' | 'comfortable' | undefined>(undefined);
|
|
204
|
+
let allMatchingSelected = $state(false);
|
|
205
|
+
const excludedMatchingIds = new SvelteMap<string, string | number>();
|
|
168
206
|
const currentDensity = $derived(densityOverride ?? density);
|
|
169
207
|
const adminContext = captureAdminContext();
|
|
170
208
|
const parsed = useParsed();
|
|
@@ -176,12 +214,20 @@
|
|
|
176
214
|
const primaryKey = $derived(resource.primaryKey ?? 'id');
|
|
177
215
|
const listPermission = useCan(() => ({ resource: resourceName, action: 'list' }));
|
|
178
216
|
const canRead = $derived(listPermission.allowed);
|
|
217
|
+
const preferenceIdentity = useGetIdentity();
|
|
218
|
+
const preferenceSession = $derived(captureAuthSession(adminContext.authProvider));
|
|
219
|
+
const preferenceIdentityReady = $derived(!adminContext.authProvider || (
|
|
220
|
+
preferenceSession.available && !preferenceIdentity.isLoading && !preferenceIdentity.error
|
|
221
|
+
&& typeof preferenceIdentity.data?.id === 'string' && !!preferenceIdentity.data.id.trim()
|
|
222
|
+
));
|
|
179
223
|
const listPreferenceScope = $derived.by(() => {
|
|
180
|
-
const matcher = adminContext.queryKeyMatcher(resourceName);
|
|
181
224
|
return definedOptions({
|
|
182
225
|
resourceName,
|
|
183
|
-
providerName:
|
|
184
|
-
tenantIdentity:
|
|
226
|
+
providerName: binding.dataProviderName,
|
|
227
|
+
tenantIdentity: adminContext.queryKeyMatcher(resourceName).tenant,
|
|
228
|
+
identityKey: adminContext.authProvider
|
|
229
|
+
? preferenceIdentityReady ? `id:${preferenceIdentity.data?.id}` : `unresolved:${preferenceSession.cacheKey}`
|
|
230
|
+
: undefined,
|
|
185
231
|
});
|
|
186
232
|
});
|
|
187
233
|
|
|
@@ -195,6 +241,7 @@
|
|
|
195
241
|
}
|
|
196
242
|
|
|
197
243
|
function readScopedPreference(scope: ListPreferenceScope, scopedKey: string, legacyKey: string): string | null {
|
|
244
|
+
if (!preferenceIdentityReady) return null;
|
|
198
245
|
const scoped = readLocalPreference(scopedKey);
|
|
199
246
|
if (scoped !== null || !canMigrateLegacyListPreferences(scope)) return scoped;
|
|
200
247
|
const legacy = readLocalPreference(legacyKey);
|
|
@@ -222,6 +269,19 @@
|
|
|
222
269
|
savedViewColumnIds,
|
|
223
270
|
));
|
|
224
271
|
let savedViews = $state<SavedListView[]>(storedSavedViews);
|
|
272
|
+
let remoteSavedViews = $state<SavedListView[]>([]);
|
|
273
|
+
let remoteViewsLoading = $state(false);
|
|
274
|
+
let remoteViewsFailed = $state(false);
|
|
275
|
+
let remoteViewsReload = $state(0);
|
|
276
|
+
let remoteDefaultEligible = true;
|
|
277
|
+
const availableSavedViews = $derived.by(() => {
|
|
278
|
+
const ids = new Set<string>();
|
|
279
|
+
return [...remoteSavedViews, ...savedViews].filter(view => {
|
|
280
|
+
if (ids.has(view.id)) return false;
|
|
281
|
+
ids.add(view.id);
|
|
282
|
+
return true;
|
|
283
|
+
});
|
|
284
|
+
});
|
|
225
285
|
const storedActiveSavedViewId = untrack(() => {
|
|
226
286
|
const candidate = readScopedPreference(
|
|
227
287
|
listPreferenceScope,
|
|
@@ -232,6 +292,16 @@
|
|
|
232
292
|
});
|
|
233
293
|
let savedViewName = $state('');
|
|
234
294
|
let savedViewsOpen = $state(false);
|
|
295
|
+
let savedViewSource = $state<'local' | 'team' | 'system'>('local');
|
|
296
|
+
let savedViewMutationPending = $state(false);
|
|
297
|
+
let savedViewMutationError = $state<'failure' | 'conflict' | undefined>();
|
|
298
|
+
const accessDrafts = new SvelteMap<string, SavedListViewAccess>();
|
|
299
|
+
const accessSubjects = new SvelteMap<string, SavedListViewSubject[]>();
|
|
300
|
+
const accessSubjectsLoading = new SvelteMap<string, boolean>();
|
|
301
|
+
const accessSubjectsFailed = new SvelteMap<string, boolean>();
|
|
302
|
+
const accessSubjectQueries = new SvelteMap<string, string>();
|
|
303
|
+
const accessSubjectRequests = new Map<string, object>();
|
|
304
|
+
let savedViewMutationEpoch = 0;
|
|
235
305
|
|
|
236
306
|
const hasExplicitURLState = untrack(() => (
|
|
237
307
|
urlState.page !== undefined
|
|
@@ -241,7 +311,7 @@
|
|
|
241
311
|
|| urlState.filters !== undefined
|
|
242
312
|
));
|
|
243
313
|
let activeSavedViewId = $state<string | undefined>(hasExplicitURLState ? undefined : storedActiveSavedViewId);
|
|
244
|
-
const activeSavedViewName = $derived(
|
|
314
|
+
const activeSavedViewName = $derived(availableSavedViews.find((view) => view.id === activeSavedViewId)?.name);
|
|
245
315
|
const initialSavedView = untrack(() => (
|
|
246
316
|
!hasExplicitURLState && storedActiveSavedViewId
|
|
247
317
|
? storedSavedViews.find((view) => view.id === storedActiveSavedViewId)
|
|
@@ -371,6 +441,7 @@
|
|
|
371
441
|
|
|
372
442
|
function scheduleSearch(event: Event) {
|
|
373
443
|
if (!(event.currentTarget instanceof HTMLInputElement)) return;
|
|
444
|
+
markSavedViewDirty();
|
|
374
445
|
searchText = event.currentTarget.value;
|
|
375
446
|
if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
|
|
376
447
|
searchDebounceTimer = setTimeout(() => {
|
|
@@ -622,7 +693,7 @@
|
|
|
622
693
|
const selectedIdValueByKey = new SvelteMap<string, string | number>();
|
|
623
694
|
|
|
624
695
|
function preferenceScopeIsLoaded(): boolean {
|
|
625
|
-
return loadedPreferenceScopeId === listPreferenceScopeId(listPreferenceScope);
|
|
696
|
+
return preferenceIdentityReady && loadedPreferenceScopeId === listPreferenceScopeId(listPreferenceScope);
|
|
626
697
|
}
|
|
627
698
|
|
|
628
699
|
function rowIsExpanded(rowId: string): boolean {
|
|
@@ -631,7 +702,7 @@
|
|
|
631
702
|
}
|
|
632
703
|
|
|
633
704
|
function rowIsSelected(rowId: string): boolean {
|
|
634
|
-
return rowSelection[rowId] === true;
|
|
705
|
+
return allMatchingSelected ? !excludedMatchingIds.has(rowId) : rowSelection[rowId] === true;
|
|
635
706
|
}
|
|
636
707
|
|
|
637
708
|
function rowIdValue(row: Row<TableFeatures, TableRecord>): string | number {
|
|
@@ -640,6 +711,11 @@
|
|
|
640
711
|
|
|
641
712
|
function toggleRowSelection(row: Row<TableFeatures, TableRecord>): void {
|
|
642
713
|
if (confirmPending || !canRead) return;
|
|
714
|
+
if (allMatchingSelected) {
|
|
715
|
+
if (excludedMatchingIds.has(row.id)) excludedMatchingIds.delete(row.id);
|
|
716
|
+
else excludedMatchingIds.set(row.id, rowIdValue(row));
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
643
719
|
const wasSelected = rowIsSelected(row.id);
|
|
644
720
|
if (!wasSelected) {
|
|
645
721
|
selectedIdValueByKey.set(row.id, rowIdValue(row));
|
|
@@ -694,6 +770,7 @@
|
|
|
694
770
|
}
|
|
695
771
|
|
|
696
772
|
function markSavedViewDirty(): void {
|
|
773
|
+
remoteDefaultEligible = false;
|
|
697
774
|
if (!activeSavedViewId) return;
|
|
698
775
|
activeSavedViewId = undefined;
|
|
699
776
|
persistActiveSavedView();
|
|
@@ -714,6 +791,7 @@
|
|
|
714
791
|
}
|
|
715
792
|
|
|
716
793
|
function applySavedView(view: SavedListView): void {
|
|
794
|
+
remoteDefaultEligible = false;
|
|
717
795
|
if (searchDebounceTimer) {
|
|
718
796
|
clearTimeout(searchDebounceTimer);
|
|
719
797
|
searchDebounceTimer = undefined;
|
|
@@ -748,6 +826,8 @@
|
|
|
748
826
|
columnOrderAtom.set(state.columnOrder);
|
|
749
827
|
persistColumnOrder(state.columnOrder);
|
|
750
828
|
rowSelection = {};
|
|
829
|
+
allMatchingSelected = false;
|
|
830
|
+
excludedMatchingIds.clear();
|
|
751
831
|
selectedIdValueByKey.clear();
|
|
752
832
|
}
|
|
753
833
|
|
|
@@ -775,8 +855,12 @@
|
|
|
775
855
|
const scopeId = listPreferenceScopeId(scope);
|
|
776
856
|
if (scopeId === loadedPreferenceScopeId) return;
|
|
777
857
|
|
|
858
|
+
remoteDefaultEligible = true;
|
|
778
859
|
const preferences = readScopedSavedViewPreferences(scope);
|
|
860
|
+
savedViewName = '';
|
|
861
|
+
savedViewsOpen = false;
|
|
779
862
|
savedViews = preferences.savedViews;
|
|
863
|
+
remoteSavedViews = [];
|
|
780
864
|
activeSavedViewId = preferences.activeSavedViewId;
|
|
781
865
|
|
|
782
866
|
if (!hasExplicitURLState && preferences.activeSavedView) {
|
|
@@ -786,13 +870,68 @@
|
|
|
786
870
|
columnVisibilityAtom.set(preferences.columnVisibility);
|
|
787
871
|
columnOrderAtom.set(preferences.columnOrder);
|
|
788
872
|
rowSelection = {};
|
|
873
|
+
allMatchingSelected = false;
|
|
874
|
+
excludedMatchingIds.clear();
|
|
789
875
|
selectedIdValueByKey.clear();
|
|
790
876
|
}
|
|
791
877
|
|
|
792
878
|
loadedPreferenceScopeId = scopeId;
|
|
793
879
|
});
|
|
794
880
|
|
|
881
|
+
let remoteViewEpoch = 0;
|
|
882
|
+
$effect(() => {
|
|
883
|
+
const provider = savedViewProvider;
|
|
884
|
+
const scope = listPreferenceScope;
|
|
885
|
+
const columns = savedViewColumnIds;
|
|
886
|
+
void remoteViewsReload;
|
|
887
|
+
remoteSavedViews = [];
|
|
888
|
+
remoteViewsLoading = false;
|
|
889
|
+
remoteViewsFailed = false;
|
|
890
|
+
accessDrafts.clear();
|
|
891
|
+
accessSubjects.clear();
|
|
892
|
+
accessSubjectsLoading.clear();
|
|
893
|
+
accessSubjectsFailed.clear();
|
|
894
|
+
accessSubjectQueries.clear();
|
|
895
|
+
accessSubjectRequests.clear();
|
|
896
|
+
savedViewMutationEpoch += 1;
|
|
897
|
+
savedViewMutationPending = false;
|
|
898
|
+
savedViewMutationError = undefined;
|
|
899
|
+
savedViewSource = 'local';
|
|
900
|
+
if (!provider || !preferenceScopeIsLoaded()) {
|
|
901
|
+
remoteSavedViews = [];
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
const epoch = ++remoteViewEpoch;
|
|
905
|
+
remoteViewsLoading = true;
|
|
906
|
+
void Promise.resolve().then(() => provider.list({ ...scope })).then(value => {
|
|
907
|
+
if (epoch !== remoteViewEpoch || !preferenceScopeIsLoaded()) return;
|
|
908
|
+
remoteSavedViews = decodeRemoteSavedListViews(value, columns, !!(provider.save || provider.remove || provider.setDefault || provider.updateAccess));
|
|
909
|
+
if (applyRemoteDefaultView && remoteDefaultEligible) {
|
|
910
|
+
const defaults = remoteSavedViews.filter(view => view.default === true);
|
|
911
|
+
if (!hasExplicitURLState && !activeSavedViewId && !externalPagination && !externalSorters
|
|
912
|
+
&& defaults.length === 1 && defaults[0]) {
|
|
913
|
+
applySavedView(defaults[0]);
|
|
914
|
+
remoteDefaultEligible = false;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}).catch(() => {
|
|
918
|
+
if (epoch === remoteViewEpoch) {
|
|
919
|
+
remoteSavedViews = [];
|
|
920
|
+
remoteViewsFailed = true;
|
|
921
|
+
}
|
|
922
|
+
}).finally(() => {
|
|
923
|
+
if (epoch === remoteViewEpoch) remoteViewsLoading = false;
|
|
924
|
+
});
|
|
925
|
+
return () => {
|
|
926
|
+
remoteViewEpoch += 1;
|
|
927
|
+
savedViewMutationEpoch += 1;
|
|
928
|
+
accessSubjectRequests.clear();
|
|
929
|
+
};
|
|
930
|
+
});
|
|
931
|
+
|
|
795
932
|
function saveCurrentView(): void {
|
|
933
|
+
if (!preferenceScopeIsLoaded() || savedViewMutationPending) return;
|
|
934
|
+
remoteDefaultEligible = false;
|
|
796
935
|
const name = savedViewName.trim().slice(0, 60);
|
|
797
936
|
if (!name) return;
|
|
798
937
|
if (searchDebounceTimer) {
|
|
@@ -800,6 +939,16 @@
|
|
|
800
939
|
searchDebounceTimer = undefined;
|
|
801
940
|
appliedSearchText = searchText;
|
|
802
941
|
}
|
|
942
|
+
if (savedViewSource !== 'local') {
|
|
943
|
+
const existing = remoteSavedViews.find(view => view.source === savedViewSource
|
|
944
|
+
&& view.name.toLocaleLowerCase() === name.toLocaleLowerCase());
|
|
945
|
+
if (!savedViewProvider?.save || (existing && (existing.readOnly || !existing.version))) return;
|
|
946
|
+
void mutateRemoteView({
|
|
947
|
+
id: existing?.id ?? crypto.randomUUID(), name, state: getCurrentSavedViewState(),
|
|
948
|
+
source: savedViewSource, expectedVersion: existing?.version ?? null,
|
|
949
|
+
});
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
803
952
|
const existing = savedViews.find((view) => view.name.toLocaleLowerCase() === name.toLocaleLowerCase());
|
|
804
953
|
const view: SavedListView = {
|
|
805
954
|
id: existing?.id ?? `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -816,12 +965,184 @@
|
|
|
816
965
|
}
|
|
817
966
|
|
|
818
967
|
function deleteSavedView(id: string): void {
|
|
968
|
+
if (!preferenceScopeIsLoaded() || savedViewMutationPending) return;
|
|
969
|
+
const remote = remoteSavedViews.find(view => view.id === id);
|
|
970
|
+
if (remote) {
|
|
971
|
+
if (!savedViewProvider?.remove || remote.readOnly || !remote.version) return;
|
|
972
|
+
void mutateRemoteView({
|
|
973
|
+
id, expectedVersion: remote.version, source: remote.source === 'system' ? 'system' : 'team',
|
|
974
|
+
name: remote.name, state: remote.state,
|
|
975
|
+
}, true);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
819
978
|
savedViews = savedViews.filter((view) => view.id !== id);
|
|
820
979
|
if (activeSavedViewId === id) activeSavedViewId = undefined;
|
|
821
980
|
persistSavedViews();
|
|
822
981
|
persistActiveSavedView();
|
|
823
982
|
}
|
|
824
983
|
|
|
984
|
+
async function mutateRemoteView(
|
|
985
|
+
mutation: import('./saved-list-views').SavedListViewMutation, remove = false,
|
|
986
|
+
): Promise<void> {
|
|
987
|
+
const provider = savedViewProvider;
|
|
988
|
+
if (!provider || savedViewMutationPending || !preferenceScopeIsLoaded()
|
|
989
|
+
|| (remove && (mutation.expectedVersion === null || !provider.remove))
|
|
990
|
+
|| (!remove && !provider.save)) return;
|
|
991
|
+
const scope = { ...listPreferenceScope };
|
|
992
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
993
|
+
const columns = savedViewColumnIds;
|
|
994
|
+
const epoch = ++savedViewMutationEpoch;
|
|
995
|
+
const current = () => epoch === savedViewMutationEpoch && provider === savedViewProvider
|
|
996
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope) && preferenceScopeIsLoaded();
|
|
997
|
+
savedViewMutationPending = true;
|
|
998
|
+
savedViewMutationError = undefined;
|
|
999
|
+
try {
|
|
1000
|
+
const result = remove
|
|
1001
|
+
? await provider.remove?.(scope, { id: mutation.id, expectedVersion: mutation.expectedVersion ?? 0 })
|
|
1002
|
+
: await provider.save?.(scope, { ...mutation, state: cloneSavedListViewState(mutation.state) });
|
|
1003
|
+
if (!current()) return;
|
|
1004
|
+
if (remove && mutation.expectedVersion !== null && decodeSavedListViewRemoveResult(result, {
|
|
1005
|
+
id: mutation.id, expectedVersion: mutation.expectedVersion,
|
|
1006
|
+
})) {
|
|
1007
|
+
remoteSavedViews = remoteSavedViews.filter(view => view.id !== mutation.id);
|
|
1008
|
+
if (activeSavedViewId === mutation.id) activeSavedViewId = undefined;
|
|
1009
|
+
persistActiveSavedView();
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const decoded = decodeSavedListViewMutationResult(result, columns, mutation);
|
|
1013
|
+
if (!decoded || (remove && decoded.ok)) {
|
|
1014
|
+
savedViewMutationError = 'failure';
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
if (!decoded.ok) {
|
|
1018
|
+
savedViewMutationError = 'conflict';
|
|
1019
|
+
// 保留本地查询;用户必须重新读取后再决定如何修改。
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const view = { ...decoded.view, readOnly: decoded.view.readOnly !== false };
|
|
1023
|
+
remoteSavedViews = [view, ...remoteSavedViews.filter(candidate => candidate.id !== view.id)];
|
|
1024
|
+
savedViewName = '';
|
|
1025
|
+
} catch {
|
|
1026
|
+
if (current()) savedViewMutationError = 'failure';
|
|
1027
|
+
} finally {
|
|
1028
|
+
if (current()) savedViewMutationPending = false;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function setRemoteDefault(view: SavedListView): void {
|
|
1033
|
+
const provider = savedViewProvider;
|
|
1034
|
+
if (!provider?.setDefault || savedViewMutationPending || view.readOnly || !view.version
|
|
1035
|
+
|| savedViewMutationError === 'conflict' || !preferenceScopeIsLoaded()
|
|
1036
|
+
|| (view.source !== 'team' && view.source !== 'system')) return;
|
|
1037
|
+
const source = view.source;
|
|
1038
|
+
const expectedVersion = view.version;
|
|
1039
|
+
const defaultValue = view.default !== true;
|
|
1040
|
+
remoteDefaultEligible = false;
|
|
1041
|
+
const scope = { ...listPreferenceScope };
|
|
1042
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
1043
|
+
const epoch = ++savedViewMutationEpoch;
|
|
1044
|
+
const current = () => epoch === savedViewMutationEpoch && provider === savedViewProvider
|
|
1045
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope) && preferenceScopeIsLoaded();
|
|
1046
|
+
savedViewMutationPending = true;
|
|
1047
|
+
savedViewMutationError = undefined;
|
|
1048
|
+
void Promise.resolve().then(() => provider.setDefault?.(scope, {
|
|
1049
|
+
id: view.id, source, expectedVersion, default: defaultValue,
|
|
1050
|
+
})).then(result => {
|
|
1051
|
+
if (!current()) return;
|
|
1052
|
+
const decoded = decodeSavedListViewMutationResult(result, savedViewColumnIds, {
|
|
1053
|
+
id: view.id, source, expectedVersion,
|
|
1054
|
+
});
|
|
1055
|
+
if (!decoded) {
|
|
1056
|
+
savedViewMutationError = 'failure';
|
|
1057
|
+
} else if (!decoded.ok) {
|
|
1058
|
+
savedViewMutationError = 'conflict';
|
|
1059
|
+
} else if (decoded.view.default !== defaultValue) {
|
|
1060
|
+
savedViewMutationError = 'failure';
|
|
1061
|
+
} else {
|
|
1062
|
+
// 默认标记会同时改变同一作用域的其他视图,成功后重新读取完整集合。
|
|
1063
|
+
remoteViewsReload += 1;
|
|
1064
|
+
}
|
|
1065
|
+
}).catch(() => {
|
|
1066
|
+
if (current()) savedViewMutationError = 'failure';
|
|
1067
|
+
}).finally(() => {
|
|
1068
|
+
if (current()) savedViewMutationPending = false;
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function saveRemoteAccess(view: SavedListView): void {
|
|
1073
|
+
const provider = savedViewProvider;
|
|
1074
|
+
const draft = accessDrafts.get(view.id);
|
|
1075
|
+
if (!provider?.updateAccess || !draft || view.readOnly !== false || !view.version
|
|
1076
|
+
|| savedViewMutationPending || savedViewMutationError === 'conflict' || !preferenceScopeIsLoaded()
|
|
1077
|
+
|| (view.source !== 'team' && view.source !== 'system')) return;
|
|
1078
|
+
const access = decodeSavedListViewAccess({
|
|
1079
|
+
mode: draft.mode,
|
|
1080
|
+
subjectIds: draft.mode === 'restricted' ? draft.subjectIds : [],
|
|
1081
|
+
});
|
|
1082
|
+
if (!access) { savedViewMutationError = 'failure'; return; }
|
|
1083
|
+
const source = view.source;
|
|
1084
|
+
const expectedVersion = view.version;
|
|
1085
|
+
const scope = { ...listPreferenceScope };
|
|
1086
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
1087
|
+
const epoch = ++savedViewMutationEpoch;
|
|
1088
|
+
const current = () => epoch === savedViewMutationEpoch && provider === savedViewProvider
|
|
1089
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope) && preferenceScopeIsLoaded();
|
|
1090
|
+
remoteDefaultEligible = false;
|
|
1091
|
+
savedViewMutationPending = true;
|
|
1092
|
+
savedViewMutationError = undefined;
|
|
1093
|
+
void Promise.resolve().then(() => {
|
|
1094
|
+
if (!current()) return;
|
|
1095
|
+
return provider.updateAccess?.(scope, {
|
|
1096
|
+
id: view.id, source, expectedVersion, access: { ...access, subjectIds: [...access.subjectIds] },
|
|
1097
|
+
});
|
|
1098
|
+
}).then(result => {
|
|
1099
|
+
if (!current()) return;
|
|
1100
|
+
const decoded = decodeSavedListViewMutationResult(result, savedViewColumnIds, {
|
|
1101
|
+
id: view.id, source, expectedVersion,
|
|
1102
|
+
});
|
|
1103
|
+
if (!decoded) savedViewMutationError = 'failure';
|
|
1104
|
+
else if (!decoded.ok) savedViewMutationError = 'conflict';
|
|
1105
|
+
else if (decoded.view.access?.mode !== access.mode
|
|
1106
|
+
|| decoded.view.access.subjectIds.length !== access.subjectIds.length
|
|
1107
|
+
|| !decoded.view.access.subjectIds.every(id => access.subjectIds.includes(id))) {
|
|
1108
|
+
savedViewMutationError = 'failure';
|
|
1109
|
+
} else remoteViewsReload += 1;
|
|
1110
|
+
}).catch(() => {
|
|
1111
|
+
if (current()) savedViewMutationError = 'failure';
|
|
1112
|
+
}).finally(() => {
|
|
1113
|
+
if (current()) savedViewMutationPending = false;
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
function loadAccessSubjects(view: SavedListView, query = ''): void {
|
|
1118
|
+
const provider = savedViewProvider;
|
|
1119
|
+
if (!provider?.listAccessSubjects || view.readOnly !== false
|
|
1120
|
+
|| view.source === 'local' || !preferenceScopeIsLoaded()) return;
|
|
1121
|
+
const scope = { ...listPreferenceScope };
|
|
1122
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
1123
|
+
const request = {};
|
|
1124
|
+
accessSubjectRequests.set(view.id, request);
|
|
1125
|
+
const current = () => accessSubjectRequests.get(view.id) === request
|
|
1126
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope)
|
|
1127
|
+
&& provider === savedViewProvider && preferenceScopeIsLoaded();
|
|
1128
|
+
accessSubjectsLoading.set(view.id, true);
|
|
1129
|
+
accessSubjectsFailed.delete(view.id);
|
|
1130
|
+
accessSubjects.delete(view.id);
|
|
1131
|
+
void Promise.resolve().then(() => {
|
|
1132
|
+
if (current()) return provider.listAccessSubjects?.(scope, { query: query.trim(), limit: 50 });
|
|
1133
|
+
})
|
|
1134
|
+
.then(result => {
|
|
1135
|
+
if (!current()) return;
|
|
1136
|
+
const subjects = decodeSavedListViewSubjects(result);
|
|
1137
|
+
if (!subjects) accessSubjectsFailed.set(view.id, true);
|
|
1138
|
+
else accessSubjects.set(view.id, subjects);
|
|
1139
|
+
})
|
|
1140
|
+
.catch(() => {
|
|
1141
|
+
if (current()) accessSubjectsFailed.set(view.id, true);
|
|
1142
|
+
})
|
|
1143
|
+
.finally(() => { if (current()) accessSubjectsLoading.delete(view.id); });
|
|
1144
|
+
}
|
|
1145
|
+
|
|
825
1146
|
function setColumnVisibility(columnId: string, visible: boolean): void {
|
|
826
1147
|
markSavedViewDirty();
|
|
827
1148
|
columnVisibilityAtom.set({ ...tableColumnVisibility.current, [columnId]: visible });
|
|
@@ -939,6 +1260,10 @@
|
|
|
939
1260
|
|
|
940
1261
|
function toggleAllRowsSelection(): void {
|
|
941
1262
|
if (confirmPending || !canRead) return;
|
|
1263
|
+
if (allMatchingSelected) {
|
|
1264
|
+
clearSelection();
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
942
1267
|
const willSelect = !table_getIsAllRowsSelected(tbl);
|
|
943
1268
|
for (const row of tableView.rows) {
|
|
944
1269
|
if (willSelect) {
|
|
@@ -957,7 +1282,58 @@
|
|
|
957
1282
|
return id === undefined ? [] : [id];
|
|
958
1283
|
})
|
|
959
1284
|
);
|
|
960
|
-
const
|
|
1285
|
+
const matchingTotal = $derived(query.data?.total ?? 0);
|
|
1286
|
+
const selectedCount = $derived(allMatchingSelected
|
|
1287
|
+
? Math.max(0, matchingTotal - excludedMatchingIds.size)
|
|
1288
|
+
: selectedIds.length);
|
|
1289
|
+
const selectionLabel = $derived(i18n.t(
|
|
1290
|
+
allMatchingSelected ? 'common.allMatchingSelected' : 'common.selectedCount',
|
|
1291
|
+
{ count: selectedCount },
|
|
1292
|
+
));
|
|
1293
|
+
const selectionReady = $derived(
|
|
1294
|
+
canRead && selectable && pageRecords.ok && !query.isError && !query.isFetching
|
|
1295
|
+
);
|
|
1296
|
+
const canSelectAllMatching = $derived(
|
|
1297
|
+
allowSelectAllMatching && !!batchActions && selectionReady
|
|
1298
|
+
&& Number.isSafeInteger(matchingTotal) && matchingTotal > 0
|
|
1299
|
+
);
|
|
1300
|
+
function batchSelectionSnapshot(): BatchSelection {
|
|
1301
|
+
if (allMatchingSelected) return {
|
|
1302
|
+
scope: 'all', filters: queryFilters.map(cloneFilter),
|
|
1303
|
+
sorters: querySorters.map(sorter => ({ ...sorter })), total: matchingTotal,
|
|
1304
|
+
excludedIds: [...excludedMatchingIds.values()],
|
|
1305
|
+
};
|
|
1306
|
+
return {
|
|
1307
|
+
scope: 'selected', ids: [...selectedIds],
|
|
1308
|
+
currentPageIds: tableView.rows.filter(row => rowSelection[row.id] === true).map(rowIdValue),
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
const selectionCriteria = $derived(JSON.stringify(queryFilters));
|
|
1312
|
+
let previousSelectionCriteria = untrack(() => selectionCriteria);
|
|
1313
|
+
$effect.pre(() => {
|
|
1314
|
+
const criteria = selectionCriteria;
|
|
1315
|
+
if (criteria !== previousSelectionCriteria) {
|
|
1316
|
+
allMatchingSelected = false;
|
|
1317
|
+
excludedMatchingIds.clear();
|
|
1318
|
+
rowSelection = {};
|
|
1319
|
+
selectedIdValueByKey.clear();
|
|
1320
|
+
if (deleteRequest?.batch && !confirmPending) {
|
|
1321
|
+
confirmOpen = false;
|
|
1322
|
+
deleteRequest = null;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
previousSelectionCriteria = criteria;
|
|
1326
|
+
});
|
|
1327
|
+
$effect.pre(() => {
|
|
1328
|
+
if (!selectable || !allowSelectAllMatching || !batchActions) {
|
|
1329
|
+
allMatchingSelected = false;
|
|
1330
|
+
excludedMatchingIds.clear();
|
|
1331
|
+
}
|
|
1332
|
+
if (!selectable) {
|
|
1333
|
+
rowSelection = {};
|
|
1334
|
+
selectedIdValueByKey.clear();
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
961
1337
|
const batchDeletePerm = useCan(() => ({
|
|
962
1338
|
resource: resourceName,
|
|
963
1339
|
action: 'delete',
|
|
@@ -965,16 +1341,26 @@
|
|
|
965
1341
|
queryOptions: { enabled: acEnabled && canDelete && selectedIds.length > 0 },
|
|
966
1342
|
}));
|
|
967
1343
|
const canBatchDelete = $derived(
|
|
968
|
-
canDelete && (!acEnabled || batchDeletePerm.isLoading || batchDeletePerm.allowed)
|
|
1344
|
+
!allMatchingSelected && canDelete && (!acEnabled || batchDeletePerm.isLoading || batchDeletePerm.allowed)
|
|
969
1345
|
);
|
|
970
1346
|
|
|
971
1347
|
function clearSelection(): void {
|
|
972
1348
|
if (!deleteManyMutation.isPending) {
|
|
1349
|
+
allMatchingSelected = false;
|
|
1350
|
+
excludedMatchingIds.clear();
|
|
973
1351
|
selectedIdValueByKey.clear();
|
|
974
1352
|
table_resetRowSelection(tbl, true);
|
|
975
1353
|
}
|
|
976
1354
|
}
|
|
977
1355
|
|
|
1356
|
+
function selectAllMatching(): void {
|
|
1357
|
+
if (!canSelectAllMatching || confirmPending || confirmOpen) return;
|
|
1358
|
+
selectedIdValueByKey.clear();
|
|
1359
|
+
excludedMatchingIds.clear();
|
|
1360
|
+
rowSelection = {};
|
|
1361
|
+
allMatchingSelected = true;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
978
1364
|
function isColumnVisible(columnId: string): boolean {
|
|
979
1365
|
return tableColumnVisibility.current[columnId] !== false;
|
|
980
1366
|
}
|
|
@@ -1049,6 +1435,19 @@
|
|
|
1049
1435
|
variables: deleteVariables,
|
|
1050
1436
|
});
|
|
1051
1437
|
let previousTableScope: typeof tableScope | undefined;
|
|
1438
|
+
function tableScopesEqual(left: typeof tableScope, right: typeof tableScope): boolean {
|
|
1439
|
+
return left.contract === right.contract
|
|
1440
|
+
&& left.resourceName === right.resourceName
|
|
1441
|
+
&& left.provider === right.provider
|
|
1442
|
+
&& left.meta === right.meta
|
|
1443
|
+
&& left.tenant === right.tenant
|
|
1444
|
+
&& left.auth === right.auth
|
|
1445
|
+
&& left.router === right.router
|
|
1446
|
+
&& left.permissionProvider === right.permissionProvider
|
|
1447
|
+
&& left.canRead === right.canRead
|
|
1448
|
+
&& left.canDelete === right.canDelete
|
|
1449
|
+
&& left.variables === right.variables;
|
|
1450
|
+
}
|
|
1052
1451
|
const detailState = $derived.by(() => {
|
|
1053
1452
|
const route = parsed.params['detail'];
|
|
1054
1453
|
if (route === undefined || !canRead) return { id: undefined, invalid: false };
|
|
@@ -1059,24 +1458,14 @@
|
|
|
1059
1458
|
const detailOpen = $derived(detailRecordId != null);
|
|
1060
1459
|
$effect.pre(() => {
|
|
1061
1460
|
const scope = tableScope;
|
|
1062
|
-
if (previousTableScope && (
|
|
1063
|
-
previousTableScope.contract !== scope.contract ||
|
|
1064
|
-
previousTableScope.resourceName !== scope.resourceName ||
|
|
1065
|
-
previousTableScope.provider !== scope.provider ||
|
|
1066
|
-
previousTableScope.meta !== scope.meta ||
|
|
1067
|
-
previousTableScope.tenant !== scope.tenant ||
|
|
1068
|
-
previousTableScope.auth !== scope.auth ||
|
|
1069
|
-
previousTableScope.router !== scope.router ||
|
|
1070
|
-
previousTableScope.permissionProvider !== scope.permissionProvider ||
|
|
1071
|
-
previousTableScope.canRead !== scope.canRead ||
|
|
1072
|
-
previousTableScope.canDelete !== scope.canDelete ||
|
|
1073
|
-
previousTableScope.variables !== scope.variables
|
|
1074
|
-
)) {
|
|
1461
|
+
if (previousTableScope && !tableScopesEqual(previousTableScope, scope)) {
|
|
1075
1462
|
activeDelete = undefined;
|
|
1076
1463
|
confirmOpen = false;
|
|
1077
1464
|
confirmPending = false;
|
|
1078
1465
|
deleteRequest = null;
|
|
1079
1466
|
operationError = null;
|
|
1467
|
+
allMatchingSelected = false;
|
|
1468
|
+
excludedMatchingIds.clear();
|
|
1080
1469
|
selectedIdValueByKey.clear();
|
|
1081
1470
|
rowSelection = {};
|
|
1082
1471
|
expandedAtom.set({});
|
|
@@ -1450,9 +1839,21 @@
|
|
|
1450
1839
|
<h1 class="svadmin-u-42536e69e639 svadmin-u-998e0b29fe9e svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{title ?? resource.label}</h1>
|
|
1451
1840
|
<div class="svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4">
|
|
1452
1841
|
{#if canExport}
|
|
1453
|
-
|
|
1454
|
-
<
|
|
1455
|
-
|
|
1842
|
+
{#if exportTaskName}
|
|
1843
|
+
<ExportButton
|
|
1844
|
+
resource={resourceName}
|
|
1845
|
+
taskName={exportTaskName}
|
|
1846
|
+
{...definedOptions({ taskProvider: exportTaskProvider, taskIdempotencyKey: exportTaskIdempotencyKey, maxItemCount: exportMaxItemCount })}
|
|
1847
|
+
format={exportFormat}
|
|
1848
|
+
filters={activeFilters}
|
|
1849
|
+
sorters={sorters}
|
|
1850
|
+
accessControl={{ enabled: acEnabled, hideIfUnauthorized: true }}
|
|
1851
|
+
/>
|
|
1852
|
+
{:else}
|
|
1853
|
+
<Button variant="outline" size="sm" onclick={exportCSV}>
|
|
1854
|
+
<Download class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" data-icon="inline-start" /> {i18n.t("common.export")}
|
|
1855
|
+
</Button>
|
|
1856
|
+
{/if}
|
|
1456
1857
|
{/if}
|
|
1457
1858
|
{#if headerActions}
|
|
1458
1859
|
{@render headerActions()}
|
|
@@ -1467,21 +1868,27 @@
|
|
|
1467
1868
|
{/if}
|
|
1468
1869
|
|
|
1469
1870
|
<!-- Selection Banner (Enterprise Batch Actions) -->
|
|
1470
|
-
{#if selectedCount > 0}
|
|
1871
|
+
{#if selectedCount > 0 || allMatchingSelected}
|
|
1471
1872
|
<div
|
|
1472
1873
|
class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-ca6bcd4b6f3f svadmin-u-a6afccfc915b svadmin-u-375dc44df6e9 svadmin-u-e0d9cc7f0647 svadmin-u-03b4dd7f172b svadmin-u-5f22e64f2282 svadmin-u-fc7473ca09eb svadmin-u-d4108abe6359 svadmin-u-40137e897961 fade-in svadmin-u-625a4c3fbeb2 svadmin-u-259ce51fc8f3 svadmin-u-020ba687fa12 svadmin-u-9f76a62f4f44 svadmin-u-3b9871a0bf93"
|
|
1473
|
-
aria-label={
|
|
1874
|
+
aria-label={selectionLabel}
|
|
1474
1875
|
data-svadmin-batch-toolbar
|
|
1475
1876
|
>
|
|
1476
1877
|
<div class="svadmin-u-60fbb7713999 svadmin-u-7e0b7cdf1a94 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4 svadmin-u-fc7473ca09eb">
|
|
1477
|
-
<span class="svadmin-u-2689f3958069 svadmin-u-d4108abe6359 svadmin-u-3032cae0badb">{
|
|
1878
|
+
<span class="svadmin-u-2689f3958069 svadmin-u-d4108abe6359 svadmin-u-3032cae0badb">{selectionLabel}</span>
|
|
1478
1879
|
{#if deleteManyMutation.isPending}
|
|
1479
1880
|
<span class="svadmin-u-bfa603190748" role="status">{i18n.t("common.processing")}</span>
|
|
1480
1881
|
{/if}
|
|
1481
1882
|
</div>
|
|
1482
1883
|
<div class="svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4">
|
|
1483
|
-
{#if batchActions}
|
|
1484
|
-
{@render batchActions({ selectedIds })}
|
|
1884
|
+
{#if batchActions && selectionReady && selectedCount > 0}
|
|
1885
|
+
{@render batchActions({ selectedIds: [...selectedIds], selection: batchSelectionSnapshot() })}
|
|
1886
|
+
{/if}
|
|
1887
|
+
{#if !allMatchingSelected && canSelectAllMatching && selectedCount < matchingTotal}
|
|
1888
|
+
<Button variant="outline" size="sm" disabled={confirmPending || confirmOpen} onclick={selectAllMatching}>
|
|
1889
|
+
<Rows class="svadmin-u-7fc7f732bf7e svadmin-u-bf600f8e029c" data-icon="inline-start" aria-hidden="true" />
|
|
1890
|
+
{i18n.t('common.selectAllMatching', { count: matchingTotal })}
|
|
1891
|
+
</Button>
|
|
1485
1892
|
{/if}
|
|
1486
1893
|
{#if canBatchDelete}
|
|
1487
1894
|
<Button
|
|
@@ -1584,9 +1991,22 @@
|
|
|
1584
1991
|
<!-- Right: Table Controls (Density, Columns, Saved Views, Refresh, Export if !showHeader) -->
|
|
1585
1992
|
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-58284b4ea568 svadmin-u-012fbd121f37">
|
|
1586
1993
|
{#if !showHeader && canExport}
|
|
1587
|
-
|
|
1588
|
-
<
|
|
1589
|
-
|
|
1994
|
+
{#if exportTaskName}
|
|
1995
|
+
<ExportButton
|
|
1996
|
+
resource={resourceName}
|
|
1997
|
+
taskName={exportTaskName}
|
|
1998
|
+
{...definedOptions({ taskProvider: exportTaskProvider, taskIdempotencyKey: exportTaskIdempotencyKey, maxItemCount: exportMaxItemCount })}
|
|
1999
|
+
format={exportFormat}
|
|
2000
|
+
filters={activeFilters}
|
|
2001
|
+
sorters={sorters}
|
|
2002
|
+
hideText
|
|
2003
|
+
accessControl={{ enabled: acEnabled, hideIfUnauthorized: true }}
|
|
2004
|
+
/>
|
|
2005
|
+
{:else}
|
|
2006
|
+
<TooltipButton tooltip={i18n.t("common.export")} variant="outline" size="sm" class="svadmin-u-e7a768f922d2 svadmin-u-0b91436debbd" onclick={exportCSV}>
|
|
2007
|
+
<Download class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" aria-hidden="true" />
|
|
2008
|
+
</TooltipButton>
|
|
2009
|
+
{/if}
|
|
1590
2010
|
{/if}
|
|
1591
2011
|
|
|
1592
2012
|
{#if showDensitySwitcher}
|
|
@@ -1634,7 +2054,7 @@
|
|
|
1634
2054
|
<Popover.Root bind:open={savedViewsOpen}>
|
|
1635
2055
|
<Popover.Trigger>
|
|
1636
2056
|
{#snippet child({ props })}
|
|
1637
|
-
<Button variant="outline" size="sm" class="svadmin-u-e7a768f922d2 svadmin-u-0b91436debbd" {...props} aria-label={i18n.t("common.savedViews")}>
|
|
2057
|
+
<Button variant="outline" size="sm" class="svadmin-u-e7a768f922d2 svadmin-u-0b91436debbd" {...props} disabled={!preferenceIdentityReady} aria-label={i18n.t("common.savedViews")}>
|
|
1638
2058
|
<Bookmark class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" data-icon="inline-start" /> {activeSavedViewName ?? i18n.t("common.savedViews")}
|
|
1639
2059
|
</Button>
|
|
1640
2060
|
{/snippet}
|
|
@@ -1645,6 +2065,34 @@
|
|
|
1645
2065
|
<h4 class="svadmin-u-2689f3958069 svadmin-u-fc7473ca09eb">{i18n.t("common.savedViews")}</h4>
|
|
1646
2066
|
<p class="svadmin-u-b6b02c0ebef6 svadmin-u-359090c2d529 svadmin-u-bfa603190748">{i18n.t("common.savedViewsHint")}</p>
|
|
1647
2067
|
</div>
|
|
2068
|
+
{#if remoteViewsLoading}
|
|
2069
|
+
<p role="status">{i18n.t('common.loading')}</p>
|
|
2070
|
+
{:else if remoteViewsFailed}
|
|
2071
|
+
<p role="alert">{i18n.t('common.operationFailed')}</p>
|
|
2072
|
+
<Button type="button" variant="outline" size="sm" onclick={() => remoteViewsReload += 1}>
|
|
2073
|
+
{i18n.t('common.retry')}
|
|
2074
|
+
</Button>
|
|
2075
|
+
{/if}
|
|
2076
|
+
{#if savedViewMutationPending}
|
|
2077
|
+
<p role="status">{i18n.t('common.loading')}</p>
|
|
2078
|
+
{:else if savedViewMutationError}
|
|
2079
|
+
<p role="alert">{i18n.t(savedViewMutationError === 'conflict' ? 'common.viewConflict' : 'common.operationFailed')}</p>
|
|
2080
|
+
<Button type="button" variant="outline" size="sm" onclick={() => remoteViewsReload += 1}>
|
|
2081
|
+
{i18n.t('common.refresh')}
|
|
2082
|
+
</Button>
|
|
2083
|
+
{/if}
|
|
2084
|
+
{#if savedViewProvider?.save}
|
|
2085
|
+
<Select.Root aria-label={i18n.t('common.viewSource')} value={savedViewSource} disabled={savedViewMutationPending}
|
|
2086
|
+
onchange={(event: Event) => {
|
|
2087
|
+
if (!(event.currentTarget instanceof HTMLSelectElement)) return;
|
|
2088
|
+
const value = event.currentTarget.value;
|
|
2089
|
+
if (value === 'local' || value === 'team' || value === 'system') savedViewSource = value;
|
|
2090
|
+
}}>
|
|
2091
|
+
<option value="local">{i18n.t('common.personalView')}</option>
|
|
2092
|
+
<option value="team">{i18n.t('common.teamView')}</option>
|
|
2093
|
+
<option value="system">{i18n.t('common.systemView')}</option>
|
|
2094
|
+
</Select.Root>
|
|
2095
|
+
{/if}
|
|
1648
2096
|
<div class="svadmin-u-da7c36cd8867">
|
|
1649
2097
|
<label class="svadmin-u-359090c2d529 svadmin-u-bfa603190748" for="saved-list-view">{i18n.t("common.currentView")}</label>
|
|
1650
2098
|
<Select.Root
|
|
@@ -1654,16 +2102,17 @@
|
|
|
1654
2102
|
onchange={(event: Event) => {
|
|
1655
2103
|
if (!(event.currentTarget instanceof HTMLSelectElement)) return;
|
|
1656
2104
|
const id = event.currentTarget.value;
|
|
1657
|
-
const view =
|
|
2105
|
+
const view = availableSavedViews.find((candidate) => candidate.id === id);
|
|
1658
2106
|
if (view) applySavedView(view);
|
|
1659
2107
|
else {
|
|
2108
|
+
remoteDefaultEligible = false;
|
|
1660
2109
|
activeSavedViewId = undefined;
|
|
1661
2110
|
persistActiveSavedView();
|
|
1662
2111
|
}
|
|
1663
2112
|
}}
|
|
1664
2113
|
>
|
|
1665
2114
|
<option value="">{i18n.t("common.currentView")}</option>
|
|
1666
|
-
{#each
|
|
2115
|
+
{#each availableSavedViews as view (view.id)}
|
|
1667
2116
|
<option value={view.id}>{view.name}</option>
|
|
1668
2117
|
{/each}
|
|
1669
2118
|
</Select.Root>
|
|
@@ -1674,22 +2123,132 @@
|
|
|
1674
2123
|
placeholder={i18n.t("common.viewName")}
|
|
1675
2124
|
maxlength={60}
|
|
1676
2125
|
bind:value={savedViewName}
|
|
2126
|
+
disabled={savedViewMutationPending}
|
|
1677
2127
|
class="svadmin-u-e7a768f922d2"
|
|
1678
2128
|
/>
|
|
1679
|
-
<Button size="sm" class="svadmin-u-012fbd121f37" disabled={!savedViewName.trim()} onclick={saveCurrentView}>
|
|
2129
|
+
<Button size="sm" class="svadmin-u-012fbd121f37" disabled={!savedViewName.trim() || savedViewMutationPending || savedViewMutationError === 'conflict'} onclick={saveCurrentView}>
|
|
1680
2130
|
<Check class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3" data-icon="inline-start" /> {i18n.t("common.saveView")}
|
|
1681
2131
|
</Button>
|
|
1682
2132
|
</div>
|
|
1683
|
-
{#if
|
|
2133
|
+
{#if availableSavedViews.length > 0}
|
|
1684
2134
|
<div class="svadmin-u-da7c36cd8867 svadmin-u-b950dda299d3 svadmin-u-18049387f0af svadmin-u-f46b61a9b310">
|
|
1685
|
-
{#each
|
|
2135
|
+
{#each availableSavedViews as view (view.id)}
|
|
1686
2136
|
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-8ef2268efbbc svadmin-u-77a2a20e90d4 svadmin-u-fc7473ca09eb">
|
|
1687
2137
|
<span class="svadmin-u-7e0b7cdf1a94 svadmin-u-f283ea9bea0e">{view.name}</span>
|
|
2138
|
+
{#if (view.source === 'team' || view.source === 'system') && savedViewProvider?.setDefault}
|
|
2139
|
+
<button
|
|
2140
|
+
type="button"
|
|
2141
|
+
class="svadmin-u-52083e7da442 svadmin-u-012fbd121f37 svadmin-u-bfa603190748"
|
|
2142
|
+
aria-label={`${i18n.t(view.default ? 'common.unsetDefaultView' : 'common.setDefaultView')} ${view.name}`}
|
|
2143
|
+
aria-pressed={view.default === true}
|
|
2144
|
+
title={i18n.t(view.default ? 'common.unsetDefaultView' : 'common.setDefaultView')}
|
|
2145
|
+
disabled={view.readOnly === true || !view.version || savedViewMutationPending || savedViewMutationError === 'conflict'}
|
|
2146
|
+
onclick={() => setRemoteDefault(view)}
|
|
2147
|
+
>
|
|
2148
|
+
<Star class={view.default ? "svadmin-u-e83a7042bc91" : ""} fill={view.default ? "currentColor" : "none"} />
|
|
2149
|
+
</button>
|
|
2150
|
+
{/if}
|
|
2151
|
+
{#if (view.source === 'team' || view.source === 'system') && savedViewProvider?.updateAccess}
|
|
2152
|
+
{@const draft = accessDrafts.get(view.id)}
|
|
2153
|
+
{@const accessMode = draft?.mode ?? view.access?.mode ?? ''}
|
|
2154
|
+
<Select.Root
|
|
2155
|
+
aria-label={`${i18n.t('common.viewAccess')} ${view.name}`}
|
|
2156
|
+
value={accessMode}
|
|
2157
|
+
disabled={view.readOnly !== false || !view.version || savedViewMutationPending || savedViewMutationError === 'conflict'}
|
|
2158
|
+
onchange={(event: Event) => {
|
|
2159
|
+
if (!(event.currentTarget instanceof HTMLSelectElement)) return;
|
|
2160
|
+
const mode = event.currentTarget.value;
|
|
2161
|
+
if (mode === 'team' || mode === 'organization' || mode === 'restricted') {
|
|
2162
|
+
accessDrafts.set(view.id, {
|
|
2163
|
+
mode,
|
|
2164
|
+
subjectIds: draft?.subjectIds ?? view.access?.subjectIds ?? [],
|
|
2165
|
+
});
|
|
2166
|
+
if (mode === 'restricted') loadAccessSubjects(view);
|
|
2167
|
+
}
|
|
2168
|
+
}}
|
|
2169
|
+
>
|
|
2170
|
+
<option value="" disabled>{i18n.t('common.selectOption')}</option>
|
|
2171
|
+
<option value="team">{i18n.t('common.teamView')}</option>
|
|
2172
|
+
<option value="organization">{i18n.t('common.accessOrganization')}</option>
|
|
2173
|
+
<option value="restricted">{i18n.t('common.accessRestricted')}</option>
|
|
2174
|
+
</Select.Root>
|
|
2175
|
+
{#if accessMode === 'restricted'}
|
|
2176
|
+
{#if savedViewProvider.listAccessSubjects}
|
|
2177
|
+
{@const selected = draft?.subjectIds ?? view.access?.subjectIds ?? []}
|
|
2178
|
+
<div>
|
|
2179
|
+
<Input
|
|
2180
|
+
aria-label={`${i18n.t('common.search')} ${view.name}`}
|
|
2181
|
+
value={accessSubjectQueries.get(view.id) ?? ''}
|
|
2182
|
+
disabled={view.readOnly !== false || savedViewMutationPending}
|
|
2183
|
+
oninput={(event: Event) => {
|
|
2184
|
+
if (!(event.currentTarget instanceof HTMLInputElement)) return;
|
|
2185
|
+
accessSubjectQueries.set(view.id, event.currentTarget.value);
|
|
2186
|
+
loadAccessSubjects(view, event.currentTarget.value);
|
|
2187
|
+
}}
|
|
2188
|
+
/>
|
|
2189
|
+
<Button type="button" size="sm" variant="outline"
|
|
2190
|
+
aria-label={`${i18n.t('common.refresh')} ${view.name}`}
|
|
2191
|
+
disabled={view.readOnly !== false || savedViewMutationPending}
|
|
2192
|
+
onclick={() => loadAccessSubjects(view, accessSubjectQueries.get(view.id) ?? '')}>
|
|
2193
|
+
<RefreshCw />
|
|
2194
|
+
</Button>
|
|
2195
|
+
{#if accessSubjectsLoading.get(view.id)}
|
|
2196
|
+
<span role="status">{i18n.t('common.loading')}</span>
|
|
2197
|
+
{:else if accessSubjectsFailed.get(view.id)}
|
|
2198
|
+
<span role="alert">{i18n.t('common.operationFailed')}</span>
|
|
2199
|
+
{:else if accessSubjects.has(view.id) && !accessSubjects.get(view.id)?.length}
|
|
2200
|
+
<span role="status">{i18n.t('common.noData')}</span>
|
|
2201
|
+
{/if}
|
|
2202
|
+
{#each selected as id (id)}
|
|
2203
|
+
<label>
|
|
2204
|
+
<input type="checkbox" checked
|
|
2205
|
+
disabled={view.readOnly !== false || savedViewMutationPending || savedViewMutationError === 'conflict'}
|
|
2206
|
+
onchange={() => accessDrafts.set(view.id, {
|
|
2207
|
+
mode: 'restricted', subjectIds: selected.filter(candidate => candidate !== id),
|
|
2208
|
+
})} />
|
|
2209
|
+
{accessSubjects.get(view.id)?.find(subject => subject.id === id)?.label ?? id}
|
|
2210
|
+
</label>
|
|
2211
|
+
{/each}
|
|
2212
|
+
{#each (accessSubjects.get(view.id) ?? []).filter(subject => !selected.includes(subject.id)) as subject (subject.id)}
|
|
2213
|
+
<label>
|
|
2214
|
+
<input type="checkbox"
|
|
2215
|
+
disabled={view.readOnly !== false || savedViewMutationPending || savedViewMutationError === 'conflict' || selected.length >= 200}
|
|
2216
|
+
onchange={() => accessDrafts.set(view.id, {
|
|
2217
|
+
mode: 'restricted', subjectIds: [...selected, subject.id],
|
|
2218
|
+
})} />
|
|
2219
|
+
{subject.label}
|
|
2220
|
+
</label>
|
|
2221
|
+
{/each}
|
|
2222
|
+
</div>
|
|
2223
|
+
{:else}
|
|
2224
|
+
<Input
|
|
2225
|
+
aria-label={`${i18n.t('common.accessSubjects')} ${view.name}`}
|
|
2226
|
+
value={(draft?.subjectIds ?? view.access?.subjectIds ?? []).join(', ')}
|
|
2227
|
+
disabled={view.readOnly !== false || !view.version || savedViewMutationPending || savedViewMutationError === 'conflict'}
|
|
2228
|
+
oninput={(event: Event) => {
|
|
2229
|
+
if (event.currentTarget instanceof HTMLInputElement) {
|
|
2230
|
+
accessDrafts.set(view.id, { mode: 'restricted', subjectIds: event.currentTarget.value.split(',').map(id => id.trim()) });
|
|
2231
|
+
}
|
|
2232
|
+
}}
|
|
2233
|
+
/>
|
|
2234
|
+
{/if}
|
|
2235
|
+
{/if}
|
|
2236
|
+
<Button type="button" size="sm"
|
|
2237
|
+
aria-label={`${i18n.t('common.saveAccess')} ${view.name}`}
|
|
2238
|
+
disabled={!draft || view.readOnly !== false || !view.version
|
|
2239
|
+
|| savedViewMutationPending || savedViewMutationError === 'conflict'
|
|
2240
|
+
|| (draft.mode === 'restricted' && !decodeSavedListViewAccess(draft))}
|
|
2241
|
+
onclick={() => saveRemoteAccess(view)}>
|
|
2242
|
+
{i18n.t('common.saveAccess')}
|
|
2243
|
+
</Button>
|
|
2244
|
+
{/if}
|
|
1688
2245
|
<button
|
|
1689
2246
|
type="button"
|
|
1690
2247
|
class="svadmin-u-52083e7da442 svadmin-u-cc46d0fa277d svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-421ac2be5045 svadmin-u-bfa603190748 svadmin-u-8e551981c8d7 svadmin-u-ea7b2e9e070e svadmin-u-f10f771f87e9 svadmin-u-793c80e97ffb svadmin-u-9c1295a6914a"
|
|
1691
2248
|
aria-label="{i18n.t("common.delete")} {view.name}"
|
|
1692
2249
|
title="{i18n.t("common.delete")} {view.name}"
|
|
2250
|
+
disabled={view.readOnly === true || savedViewMutationPending || savedViewMutationError === 'conflict'
|
|
2251
|
+
|| (!!view.source && !savedViewProvider?.remove)}
|
|
1693
2252
|
onclick={() => deleteSavedView(view.id)}
|
|
1694
2253
|
>
|
|
1695
2254
|
<Trash2 class="svadmin-u-783b0d9d1e2c" />
|
|
@@ -1850,14 +2409,14 @@
|
|
|
1850
2409
|
{#if header.id === '_select'}
|
|
1851
2410
|
{#if batchActions}
|
|
1852
2411
|
<Checkbox
|
|
1853
|
-
aria-label={i18n.t('common.
|
|
1854
|
-
checked={table_getIsAllRowsSelected(tbl)}
|
|
2412
|
+
aria-label={i18n.t(allMatchingSelected ? 'common.clearSelection' : 'common.selectAllOnPage')}
|
|
2413
|
+
checked={allMatchingSelected || table_getIsAllRowsSelected(tbl)}
|
|
1855
2414
|
onCheckedChange={toggleAllRowsSelection}
|
|
1856
2415
|
/>
|
|
1857
2416
|
{:else if !acEnabled || deleteResourcePermission.isLoading || deleteResourcePermission.allowed}
|
|
1858
2417
|
<Checkbox
|
|
1859
|
-
aria-label={i18n.t('common.
|
|
1860
|
-
checked={table_getIsAllRowsSelected(tbl)}
|
|
2418
|
+
aria-label={i18n.t(allMatchingSelected ? 'common.clearSelection' : 'common.selectAllOnPage')}
|
|
2419
|
+
checked={allMatchingSelected || table_getIsAllRowsSelected(tbl)}
|
|
1861
2420
|
onCheckedChange={toggleAllRowsSelection}
|
|
1862
2421
|
/>
|
|
1863
2422
|
{/if}
|
|
@@ -1904,6 +2463,7 @@
|
|
|
1904
2463
|
<Checkbox
|
|
1905
2464
|
aria-label={i18n.t('common.selectRow', { id })}
|
|
1906
2465
|
checked={rowIsSelected(row.id)}
|
|
2466
|
+
disabled={confirmPending}
|
|
1907
2467
|
onCheckedChange={() => toggleRowSelection(row)}
|
|
1908
2468
|
/>
|
|
1909
2469
|
{:else}
|
|
@@ -1911,6 +2471,7 @@
|
|
|
1911
2471
|
<Checkbox
|
|
1912
2472
|
aria-label={i18n.t('common.selectRow', { id })}
|
|
1913
2473
|
checked={rowIsSelected(row.id)}
|
|
2474
|
+
disabled={confirmPending}
|
|
1914
2475
|
onCheckedChange={() => toggleRowSelection(row)}
|
|
1915
2476
|
/>
|
|
1916
2477
|
</CanAccess>
|
|
@@ -2032,6 +2593,7 @@
|
|
|
2032
2593
|
<Checkbox
|
|
2033
2594
|
aria-label={i18n.t('common.selectRow', { id })}
|
|
2034
2595
|
checked={rowIsSelected(row.id)}
|
|
2596
|
+
disabled={confirmPending}
|
|
2035
2597
|
onCheckedChange={() => toggleRowSelection(row)}
|
|
2036
2598
|
/>
|
|
2037
2599
|
{:else}
|
|
@@ -2039,6 +2601,7 @@
|
|
|
2039
2601
|
<Checkbox
|
|
2040
2602
|
aria-label={i18n.t('common.selectRow', { id })}
|
|
2041
2603
|
checked={rowIsSelected(row.id)}
|
|
2604
|
+
disabled={confirmPending}
|
|
2042
2605
|
onCheckedChange={() => toggleRowSelection(row)}
|
|
2043
2606
|
/>
|
|
2044
2607
|
</CanAccess>
|