@svadmin/ui 0.88.0 → 0.91.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/dist/app.css +4 -0
- package/dist/app.theme.css +4 -0
- package/dist/component-registry.svelte.d.ts +2 -2
- package/dist/components/AboutSettings.svelte +23 -4
- package/dist/components/AdminApp.svelte +6 -4
- package/dist/components/AdminApp.svelte.d.ts +3 -3
- package/dist/components/ApiSettings.svelte +43 -27
- package/dist/components/ApprovalActionCard.svelte +8 -9
- package/dist/components/ApprovalCenter.svelte +24 -5
- package/dist/components/AuditLogViewer.svelte +2 -2
- package/dist/components/AutoComplete.svelte +610 -0
- package/dist/components/AutoComplete.svelte.d.ts +25 -0
- package/dist/components/AutoForm.svelte +263 -18
- package/dist/components/AutoForm.svelte.d.ts +19 -2
- package/dist/components/AutoTable.svelte +135 -35
- package/dist/components/AutoTable.svelte.d.ts +8 -0
- package/dist/components/BatchActionBar.svelte +42 -14
- package/dist/components/BatchActionBar.svelte.d.ts +5 -0
- package/dist/components/BoundRecordDetailDrawer.svelte +5 -3
- package/dist/components/CancelTaskButton.svelte +14 -1
- package/dist/components/Cascader.svelte +412 -53
- package/dist/components/Cascader.svelte.d.ts +7 -1
- package/dist/components/ColorPicker.svelte +1 -1
- package/dist/components/ColorPicker.svelte.d.ts +1 -1
- package/dist/components/ComboboxField.svelte +270 -34
- package/dist/components/ComboboxField.svelte.d.ts +4 -0
- package/dist/components/CommandPalette.svelte +4 -4
- package/dist/components/DashboardView.svelte +73 -7
- package/dist/components/DashboardView.svelte.d.ts +3 -1
- package/dist/components/DateRangeInput.svelte +8 -1
- package/dist/components/DetailWorkspaceAsyncSection.svelte +107 -0
- package/dist/components/DetailWorkspaceAsyncSection.svelte.d.ts +36 -0
- package/dist/components/DetailWorkspaceSections.svelte +233 -0
- package/dist/components/DetailWorkspaceSections.svelte.d.ts +9 -0
- package/dist/components/DrawerForm.svelte +78 -9
- package/dist/components/DrawerForm.svelte.d.ts +3 -3
- package/dist/components/ErrorBoundary.svelte +1 -1
- package/dist/components/ErrorComponent.svelte +19 -6
- package/dist/components/ErrorComponent.svelte.d.ts +1 -0
- package/dist/components/ErrorSummary.svelte +9 -4
- package/dist/components/FieldRenderer.svelte +203 -90
- package/dist/components/FieldRenderer.svelte.d.ts +16 -1
- package/dist/components/FileUpload.svelte +116 -51
- package/dist/components/FileUpload.svelte.d.ts +7 -27
- package/dist/components/FileUploadField.svelte +170 -0
- package/dist/components/FileUploadField.svelte.d.ts +21 -0
- package/dist/components/FilterBuilder.svelte +13 -7
- package/dist/components/FilterCollectionInput.svelte +9 -7
- package/dist/components/FilterCollectionInput.svelte.d.ts +3 -0
- package/dist/components/ForgotPasswordPage.svelte +1 -1
- package/dist/components/Header.svelte +1 -1
- package/dist/components/ImportWizard.svelte +197 -28
- package/dist/components/ImportWizard.svelte.d.ts +5 -1
- package/dist/components/InferencerPanel.svelte +4 -2
- package/dist/components/InfiniteList.svelte +52 -9
- package/dist/components/JsonSchemaForm.svelte +237 -42
- package/dist/components/JsonSchemaForm.svelte.d.ts +10 -3
- package/dist/components/KanbanBoard.svelte +43 -7
- package/dist/components/KanbanBoard.svelte.d.ts +2 -2
- package/dist/components/Layout.svelte +1 -1
- package/dist/components/LoginPage.svelte +1 -1
- package/dist/components/MetricStrip.svelte +8 -3
- package/dist/components/MetricStrip.svelte.d.ts +1 -0
- package/dist/components/ModalForm.svelte +85 -9
- package/dist/components/ModalForm.svelte.d.ts +3 -3
- package/dist/components/MoneyInput.svelte +5 -3
- package/dist/components/MoneyInput.svelte.d.ts +4 -2
- package/dist/components/MultiTabKeepAlive.svelte +502 -43
- package/dist/components/MultiTabKeepAlive.svelte.d.ts +40 -6
- package/dist/components/NotificationsSettings.svelte +10 -10
- package/dist/components/NumberInput.svelte +5 -3
- package/dist/components/NumberInput.svelte.d.ts +4 -2
- package/dist/components/NumericInput.svelte +277 -25
- package/dist/components/NumericInput.svelte.d.ts +3 -1
- package/dist/components/PercentInput.svelte +5 -3
- package/dist/components/PercentInput.svelte.d.ts +4 -2
- package/dist/components/PermissionMatrix.svelte +44 -8
- package/dist/components/PermissionMatrix.svelte.d.ts +1 -0
- package/dist/components/ProfilePage.svelte +38 -13
- package/dist/components/RadioGroup.svelte +203 -4
- package/dist/components/RadioGroup.svelte.d.ts +1 -1
- package/dist/components/Rate.svelte +3 -3
- package/dist/components/Rate.svelte.d.ts +2 -2
- package/dist/components/RecordDetailDrawer.svelte +1 -1
- package/dist/components/RecordDetailDrawer.svelte.d.ts +1 -1
- package/dist/components/RegisterPage.svelte +1 -1
- package/dist/components/RelationPicker.svelte +39 -11
- package/dist/components/ResourceAccessGuard.svelte +7 -2
- package/dist/components/RetryTaskButton.svelte +14 -1
- package/dist/components/RolesSettings.svelte +74 -28
- package/dist/components/SecuritySettings.svelte +66 -51
- package/dist/components/SegmentedControl.svelte +4 -2
- package/dist/components/SegmentedControl.svelte.d.ts +1 -1
- package/dist/components/Sidebar.svelte +1 -1
- package/dist/components/StepsForm.svelte +60 -10
- package/dist/components/StepsForm.svelte.d.ts +4 -2
- package/dist/components/SvadminLogo.svelte +1 -0
- package/dist/components/SvarDataGrid.svelte +42 -24
- package/dist/components/SvarDataGrid.svelte.d.ts +10 -10
- package/dist/components/SvarResourceRead.svelte +2 -0
- package/dist/components/SvarResourceTableView.svelte +4 -2
- package/dist/components/TagsInput.svelte +3 -3
- package/dist/components/TagsInput.svelte.d.ts +2 -2
- package/dist/components/TaskDetails.svelte +22 -16
- package/dist/components/TaskDetails.svelte.d.ts +1 -1
- package/dist/components/TaskList.svelte +57 -15
- package/dist/components/TaskQueueDrawer.svelte +54 -34
- package/dist/components/Transfer.svelte +67 -5
- package/dist/components/Transfer.svelte.d.ts +4 -0
- package/dist/components/TreeSelect.svelte +337 -77
- package/dist/components/TreeSelect.svelte.d.ts +4 -0
- package/dist/components/TreeTable.svelte +6 -1
- package/dist/components/TwoFactorAuthPage.svelte +8 -4
- package/dist/components/UpdatePasswordPage.svelte +1 -1
- package/dist/components/VersionDiffViewer.svelte +9 -7
- package/dist/components/account/CompanyProfilePage.svelte +13 -12
- package/dist/components/account/MemberDirectory.svelte +14 -10
- package/dist/components/account/SettingsEnterprisePage.svelte +18 -18
- package/dist/components/content/AuditTimeline.svelte +8 -6
- package/dist/components/content/DataState.svelte +1 -1
- package/dist/components/content/FileList.svelte +44 -13
- package/dist/components/content/FileList.svelte.d.ts +4 -2
- package/dist/components/content/FileListRow.svelte +87 -0
- package/dist/components/content/FileListRow.svelte.d.ts +11 -0
- package/dist/components/content/ImportDropzone.svelte +109 -12
- package/dist/components/content/ImportDropzone.svelte.d.ts +1 -0
- package/dist/components/content/IntegrationCard.svelte +3 -3
- package/dist/components/content/IntegrationCard.svelte.d.ts +2 -0
- package/dist/components/content/MediaThumbnail.svelte +6 -2
- package/dist/components/detail-workspace.d.ts +60 -0
- package/dist/components/detail-workspace.js +3 -0
- package/dist/components/enterprise/json-schema-form.d.ts +1 -1
- package/dist/components/enterprise/recursive-schema-form.d.ts +15 -1
- package/dist/components/enterprise/recursive-schema-form.js +288 -14
- package/dist/components/enterprise/schema-form-references.d.ts +14 -0
- package/dist/components/enterprise/schema-form-references.js +316 -0
- package/dist/components/fields/ArrayField.svelte +178 -14
- package/dist/components/fields/ArrayField.svelte.d.ts +17 -3
- package/dist/components/fields/array-field-metadata.d.ts +4 -0
- package/dist/components/fields/array-field-metadata.js +36 -0
- package/dist/components/file-upload-reference.d.ts +11 -0
- package/dist/components/file-upload-reference.js +50 -0
- package/dist/components/file-upload-types.d.ts +32 -0
- package/dist/components/file-upload-types.js +1 -0
- package/dist/components/form-contract.upload-type-fixture.svelte +108 -0
- package/dist/components/form-contract.upload-type-fixture.svelte.d.ts +18 -0
- package/dist/components/json-schema-form-state.d.ts +19 -0
- package/dist/components/list-deletion.svelte.js +6 -1
- package/dist/components/list-state.svelte.d.ts +12 -1
- package/dist/components/list-state.svelte.js +56 -16
- package/dist/components/numeric-format.d.ts +44 -0
- package/dist/components/numeric-format.js +198 -0
- package/dist/components/record-detail.svelte.js +2 -0
- package/dist/components/saved-list-views.d.ts +9 -2
- package/dist/components/saved-list-views.js +42 -11
- package/dist/components/saved-list-views.svelte.d.ts +3 -0
- package/dist/components/saved-list-views.svelte.js +56 -7
- package/dist/components/task-action.svelte.d.ts +1 -0
- package/dist/components/task-action.svelte.js +55 -3
- package/dist/components/task-utils.d.ts +1 -0
- package/dist/components/task-utils.js +18 -0
- package/dist/components/ui/input/input.svelte +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/sheet/sheet.svelte +4 -1
- package/dist/components/version-history.d.ts +14 -0
- package/dist/components/version-history.js +32 -0
- package/dist/components/workspace-tab-state.d.ts +78 -0
- package/dist/components/workspace-tab-state.js +587 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +4 -0
- package/package.json +5 -5
|
@@ -4,11 +4,13 @@ interface StepDef {
|
|
|
4
4
|
}
|
|
5
5
|
interface Props {
|
|
6
6
|
resourceName: string;
|
|
7
|
-
id?: string | number;
|
|
7
|
+
id?: string | number | undefined;
|
|
8
8
|
mode?: 'create' | 'edit';
|
|
9
9
|
steps: StepDef[];
|
|
10
10
|
onSuccess?: () => void;
|
|
11
11
|
}
|
|
12
|
-
declare const StepsForm: import("svelte").Component<Props, {
|
|
12
|
+
declare const StepsForm: import("svelte").Component<Props, {
|
|
13
|
+
reset: () => void;
|
|
14
|
+
}, "">;
|
|
13
15
|
type StepsForm = ReturnType<typeof StepsForm>;
|
|
14
16
|
export default StepsForm;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<span
|
|
12
|
+
data-svadmin-logo
|
|
12
13
|
aria-hidden="true"
|
|
13
14
|
class="svadmin-u-bb0c4bfc52bd svadmin-u-012fbd121f37 svadmin-u-5f22e64f2282 svadmin-u-75b1bec3ea0e svadmin-u-438b2237b8d6 {onDark ? 'svadmin-u-3daca9af0861 svadmin-u-8b434b651a9e' : ''}"
|
|
14
15
|
style:width={`${size}px`}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
filters?: readonly SvarTextFilter[];
|
|
31
31
|
onSortChange?: (sorters: SvarSort[]) => void;
|
|
32
32
|
onFilterChange?: (filters: SvarTextFilter[]) => void;
|
|
33
|
-
onCellEdit?: (edit: SvarCellEdit) => Promise<void
|
|
33
|
+
onCellEdit?: ((edit: SvarCellEdit) => Promise<void>) | undefined;
|
|
34
34
|
selectable?: boolean;
|
|
35
35
|
selectedIds?: readonly SvarRecordId[];
|
|
36
36
|
onSelectionChange?: (ids: SvarRecordId[]) => void;
|
|
@@ -38,29 +38,35 @@
|
|
|
38
38
|
cellContent?: Snippet<[SvarCellContent]>;
|
|
39
39
|
/** 已加载记录的独立快照,含已加载后代,不包含尚未请求的行。 */
|
|
40
40
|
onRecordsChange?: (records: readonly Record<string, unknown>[]) => void;
|
|
41
|
-
windowSource?: SvarWindowSource;
|
|
41
|
+
windowSource?: SvarWindowSource | undefined;
|
|
42
42
|
loadChildren?: SvarChildrenLoader;
|
|
43
43
|
hasChildrenKey?: string;
|
|
44
44
|
onLoadMore?: (request: { signal: AbortSignal }) => Promise<void>;
|
|
45
45
|
hasMore?: boolean;
|
|
46
46
|
/** 查询、租户或会话切换必须改变此宿主版本;追加同一数据集则无需改变。 */
|
|
47
47
|
scopeKey?: string | number;
|
|
48
|
-
loading?: boolean;
|
|
49
|
-
error?: string;
|
|
50
|
-
disabled?: boolean;
|
|
48
|
+
loading?: boolean | undefined;
|
|
49
|
+
error?: string | undefined;
|
|
50
|
+
disabled?: boolean | undefined;
|
|
51
51
|
locale?: string;
|
|
52
|
-
label?: string;
|
|
53
|
-
emptyLabel?: string;
|
|
54
|
-
loadingLabel?: string;
|
|
55
|
-
disabledLabel?: string;
|
|
56
|
-
fallbackLabel?: string;
|
|
52
|
+
label?: string | undefined;
|
|
53
|
+
emptyLabel?: string | undefined;
|
|
54
|
+
loadingLabel?: string | undefined;
|
|
55
|
+
disabledLabel?: string | undefined;
|
|
56
|
+
fallbackLabel?: string | undefined;
|
|
57
57
|
}
|
|
58
58
|
let { Grid, Theme, items = [], columns, primaryKey = 'id', childrenKey, height = 420, density = 'comfortable', rowHeight: rowHeightProp, headerHeight: headerHeightProp, freezeLeft = 0, freezeRight = 0, queryMode = 'local',
|
|
59
59
|
sorters = [], filters = [], onSortChange, onFilterChange, onCellEdit, selectable = false, selectedIds = [], onSelectionChange, onRecordsChange, cellContent,
|
|
60
60
|
windowSource, loadChildren, hasChildrenKey = 'hasChildren', onLoadMore, hasMore = false, scopeKey = 0,
|
|
61
|
-
loading = false, error, disabled = false, locale = 'en-US', label
|
|
62
|
-
disabledLabel
|
|
61
|
+
loading = false, error, disabled = false, locale = 'en-US', label, emptyLabel, loadingLabel,
|
|
62
|
+
disabledLabel, fallbackLabel,
|
|
63
63
|
}: SvarDataGridProps = $props();
|
|
64
|
+
const isZh = $derived(locale.startsWith('zh'));
|
|
65
|
+
const resolvedLabel = $derived(label ?? (isZh ? '数据表格' : 'Data grid'));
|
|
66
|
+
const resolvedEmptyLabel = $derived(emptyLabel ?? (isZh ? '暂无记录' : 'No records'));
|
|
67
|
+
const resolvedLoadingLabel = $derived(loadingLabel ?? (isZh ? '加载中...' : 'Loading'));
|
|
68
|
+
const resolvedDisabledLabel = $derived(disabledLabel ?? (isZh ? '表格已禁用' : 'Grid is disabled'));
|
|
69
|
+
const resolvedFallbackLabel = $derived(fallbackLabel ?? (isZh ? '静态预览(最多 20 行)' : 'Static preview (up to 20 rows)'));
|
|
64
70
|
const treeKey = $derived(loadChildren ? childrenKey ?? 'children' : childrenKey);
|
|
65
71
|
const rowHeight = $derived(rowHeightProp ?? (density === 'compact' ? 32 : 44));
|
|
66
72
|
const headerHeight = $derived(headerHeightProp ?? (density === 'compact' ? 32 : 36));
|
|
@@ -117,7 +123,12 @@
|
|
|
117
123
|
center: engineColumns.map(column => ({ ...column, hidden: freezeRight > 0 && rightIds.has(column.id) })),
|
|
118
124
|
right: engineColumns.map(column => ({ ...column, hidden: !rightIds.has(column.id), width: column.width ?? 160, flexgrow: 0, resize: false })),
|
|
119
125
|
rightWidth: engineColumns.slice(engineColumns.length - freezeRight).reduce((sum, column) => sum + (column.width ?? 160), 0) + 2 };
|
|
120
|
-
} catch
|
|
126
|
+
} catch {
|
|
127
|
+
return {
|
|
128
|
+
ok: false as const,
|
|
129
|
+
message: locale.startsWith('zh') ? '表格配置无效,请检查后重试' : 'The grid configuration is invalid; check it and retry',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
121
132
|
});
|
|
122
133
|
const records = $derived.by(() => {
|
|
123
134
|
if (!model.ok) return { ok: false as const, message: model.message };
|
|
@@ -126,7 +137,12 @@
|
|
|
126
137
|
const rows = loadChildren && treeKey ? projectSvarLazyRows(value, model.columns, primaryKey, treeKey, hasChildrenKey, branches, opened)
|
|
127
138
|
: projectSvarRows(value, model.columns, primaryKey, treeKey);
|
|
128
139
|
return { ok: true as const, value, rows };
|
|
129
|
-
} catch
|
|
140
|
+
} catch {
|
|
141
|
+
return {
|
|
142
|
+
ok: false as const,
|
|
143
|
+
message: locale.startsWith('zh') ? '表格数据无效,请刷新后重试' : 'The grid data is invalid; refresh and retry',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
130
146
|
});
|
|
131
147
|
const cellRecords = $derived.by(() => cellContent && records.ok
|
|
132
148
|
? svarRecordIndex(snapshotSvarRecords(records.value, primaryKey, treeKey), primaryKey, treeKey) : new Map<string, Record<string, unknown>>());
|
|
@@ -169,7 +185,7 @@
|
|
|
169
185
|
if (!source || !current(owner)) return;
|
|
170
186
|
let range: SvarRange;
|
|
171
187
|
try { range = checkedSvarRange(event, windowTotal ?? source.total); }
|
|
172
|
-
catch { windowError = 'Invalid grid window'; return; }
|
|
188
|
+
catch { windowError = isZh ? '表格窗口无效' : 'Invalid grid window'; return; }
|
|
173
189
|
// 布局测量也会发出新窗口,不能因此自动重试失败或移除用户的重试按钮。
|
|
174
190
|
if (windowError && !force) { lastRange = range; return; }
|
|
175
191
|
const key = `${range.start}:${range.end}`;
|
|
@@ -186,7 +202,7 @@
|
|
|
186
202
|
if (!alive || dataOwner !== owner || desiredWindow !== key || failure instanceof SvarLoadCancelled) return;
|
|
187
203
|
windowRows = []; windowLoading = false;
|
|
188
204
|
if (!current(owner)) { desiredWindow = ''; return; }
|
|
189
|
-
windowError =
|
|
205
|
+
windowError = isZh ? '窗口加载失败,请重试' : 'Window load failed; retry';
|
|
190
206
|
});
|
|
191
207
|
}
|
|
192
208
|
$effect(() => {
|
|
@@ -244,7 +260,9 @@
|
|
|
244
260
|
server: () => queryMode === 'server', sorters: () => sorters, filters: () => filters,
|
|
245
261
|
sort: next => onSortChange?.(next), filter: next => onFilterChange?.(next),
|
|
246
262
|
editable: () => onCellEdit !== undefined && !pending, selectable: () => selectable && !pending,
|
|
247
|
-
selection: next => onSelectionChange?.(next), error:
|
|
263
|
+
selection: next => onSelectionChange?.(next), error: () => {
|
|
264
|
+
if (active()) editFailure = locale.startsWith('zh') ? '保存失败,请刷新确认后重试' : 'Save failed; refresh before retrying';
|
|
265
|
+
},
|
|
248
266
|
edit(edit) {
|
|
249
267
|
const write = onCellEdit;
|
|
250
268
|
if (!write || pending || !active()) return;
|
|
@@ -292,13 +310,13 @@
|
|
|
292
310
|
</script>
|
|
293
311
|
|
|
294
312
|
<Theme fonts={false}>
|
|
295
|
-
<div class="svadmin-svar-grid" data-svadmin-svar-grid data-density={density} role="group" aria-label={
|
|
313
|
+
<div class="svadmin-svar-grid" data-svadmin-svar-grid data-density={density} role="group" aria-label={resolvedLabel} aria-busy={isLoading || pending || morePending} aria-disabled={disabled}>
|
|
296
314
|
{#if !model.ok || !records.ok}<p role="alert">{!model.ok ? model.message : !records.ok ? records.message : ''}</p>
|
|
297
315
|
{:else}
|
|
298
316
|
{#if activeError}<p role="alert">{activeError}</p>
|
|
299
|
-
{:else if isLoading}<p role="status">{
|
|
300
|
-
{:else if disabled}<p role="status">{
|
|
301
|
-
{:else if records.rows.length === 0 && (!windowSource || (windowTotal ?? windowSource.total) === 0)}<p role="status">{
|
|
317
|
+
{:else if isLoading}<p role="status">{resolvedLoadingLabel}</p>
|
|
318
|
+
{:else if disabled}<p role="status">{resolvedDisabledLabel}</p>
|
|
319
|
+
{:else if records.rows.length === 0 && (!windowSource || (windowTotal ?? windowSource.total) === 0)}<p role="status">{resolvedEmptyLabel}</p>{/if}
|
|
302
320
|
{#if windowError}<button type="button" disabled={disabled} onclick={retryWindow}>{locale.startsWith('zh') ? '重试窗口' : 'Retry window'}</button>{/if}
|
|
303
321
|
{#if editFailure}<p role="alert">{editFailure}</p>{/if}
|
|
304
322
|
{#if pending}<p role="status">{locale.startsWith('zh') ? '正在保存' : 'Saving'}</p>{/if}
|
|
@@ -306,19 +324,19 @@
|
|
|
306
324
|
{#each [...branchErrors] as [key, failure] (key)}<p role="alert">{failure.message}: {failure.id} <button type="button" disabled={disabled} onclick={() => openBranch(key, true)}>{locale.startsWith('zh') ? '重试' : 'Retry children'}</button></p>{/each}
|
|
307
325
|
{#if mounted}
|
|
308
326
|
{#key dataOwner}<div class="grid-panes" inert={disabled}>
|
|
309
|
-
<div class="grid-viewport center-pane" data-svar-pane="center" style:height="{height}px" role="region" aria-label=
|
|
327
|
+
<div class="grid-viewport center-pane" data-svar-pane="center" style:height="{height}px" role="region" aria-label={isZh ? '可滚动列' : 'Scrollable columns'} onkeydowncapture={event => navigatePane(event, 0)}>
|
|
310
328
|
<Grid columns={model.center} data={activeError || isLoading ? [] : records.rows} tree={treeKey !== undefined}
|
|
311
329
|
split={{ left: freezeLeft }} sizes={{ rowHeight, headerHeight, columnWidth: 160 }} select={selectable} multiselect={selectable} {selectedRows}
|
|
312
330
|
reorder={false} draggableRows={false} undo={false} {...queryProps} {...dynamicProps} init={api => init(api, 0)} />
|
|
313
331
|
</div>
|
|
314
|
-
{#if freezeRight > 0}<div class="grid-viewport right-pane" data-svar-pane="right" style:height="{height}px" style:width="{model.rightWidth}px" role="region" aria-label=
|
|
332
|
+
{#if freezeRight > 0}<div class="grid-viewport right-pane" data-svar-pane="right" style:height="{height}px" style:width="{model.rightWidth}px" role="region" aria-label={isZh ? '右侧冻结列' : 'Frozen right columns'} onkeydowncapture={event => navigatePane(event, 1)}>
|
|
315
333
|
<Grid columns={model.right} data={activeError || isLoading ? [] : rightRows} tree={treeKey !== undefined}
|
|
316
334
|
split={{ left: 0 }} sizes={{ rowHeight, headerHeight, columnWidth: 160 }} select={selectable} multiselect={selectable} {selectedRows}
|
|
317
335
|
reorder={false} draggableRows={false} undo={false} {...queryProps} {...dynamicProps} init={api => init(api, 1)} />
|
|
318
336
|
</div>{/if}
|
|
319
337
|
</div>{/key}
|
|
320
338
|
{:else if !loading && !error}
|
|
321
|
-
<div class="static-preview"><table><caption>{
|
|
339
|
+
<div class="static-preview"><table><caption>{resolvedFallbackLabel}</caption>
|
|
322
340
|
<thead><tr>{#each model.engineColumns as column, index (column.id)}<th scope="col">{model.columns[index]?.label}</th>{/each}</tr></thead>
|
|
323
341
|
<tbody>{#each records.rows.slice(0, 20) as row (row.id)}<tr>{#each model.engineColumns as column (column.id)}<td>{row[column.id] ?? ''}</td>{/each}</tr>{/each}</tbody>
|
|
324
342
|
</table></div>
|
|
@@ -28,7 +28,7 @@ export interface SvarDataGridProps {
|
|
|
28
28
|
filters?: readonly SvarTextFilter[];
|
|
29
29
|
onSortChange?: (sorters: SvarSort[]) => void;
|
|
30
30
|
onFilterChange?: (filters: SvarTextFilter[]) => void;
|
|
31
|
-
onCellEdit?: (edit: SvarCellEdit) => Promise<void
|
|
31
|
+
onCellEdit?: ((edit: SvarCellEdit) => Promise<void>) | undefined;
|
|
32
32
|
selectable?: boolean;
|
|
33
33
|
selectedIds?: readonly SvarRecordId[];
|
|
34
34
|
onSelectionChange?: (ids: SvarRecordId[]) => void;
|
|
@@ -36,7 +36,7 @@ export interface SvarDataGridProps {
|
|
|
36
36
|
cellContent?: Snippet<[SvarCellContent]>;
|
|
37
37
|
/** 已加载记录的独立快照,含已加载后代,不包含尚未请求的行。 */
|
|
38
38
|
onRecordsChange?: (records: readonly Record<string, unknown>[]) => void;
|
|
39
|
-
windowSource?: SvarWindowSource;
|
|
39
|
+
windowSource?: SvarWindowSource | undefined;
|
|
40
40
|
loadChildren?: SvarChildrenLoader;
|
|
41
41
|
hasChildrenKey?: string;
|
|
42
42
|
onLoadMore?: (request: {
|
|
@@ -45,15 +45,15 @@ export interface SvarDataGridProps {
|
|
|
45
45
|
hasMore?: boolean;
|
|
46
46
|
/** 查询、租户或会话切换必须改变此宿主版本;追加同一数据集则无需改变。 */
|
|
47
47
|
scopeKey?: string | number;
|
|
48
|
-
loading?: boolean;
|
|
49
|
-
error?: string;
|
|
50
|
-
disabled?: boolean;
|
|
48
|
+
loading?: boolean | undefined;
|
|
49
|
+
error?: string | undefined;
|
|
50
|
+
disabled?: boolean | undefined;
|
|
51
51
|
locale?: string;
|
|
52
|
-
label?: string;
|
|
53
|
-
emptyLabel?: string;
|
|
54
|
-
loadingLabel?: string;
|
|
55
|
-
disabledLabel?: string;
|
|
56
|
-
fallbackLabel?: string;
|
|
52
|
+
label?: string | undefined;
|
|
53
|
+
emptyLabel?: string | undefined;
|
|
54
|
+
loadingLabel?: string | undefined;
|
|
55
|
+
disabledLabel?: string | undefined;
|
|
56
|
+
fallbackLabel?: string | undefined;
|
|
57
57
|
}
|
|
58
58
|
declare const SvarDataGrid: Component<SvarDataGridProps, {}, "">;
|
|
59
59
|
type SvarDataGrid = ReturnType<typeof SvarDataGrid>;
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
const binding = useResourceContract(() => request.resourceName);
|
|
21
21
|
const query = useList({
|
|
22
22
|
get resource() { return binding.resource; },
|
|
23
|
+
// 读取失败由 SVAR 窗口、追加加载或树节点的局部错误态承接。
|
|
24
|
+
errorNotification: false,
|
|
23
25
|
get dataProviderName() { return binding.dataProviderName; },
|
|
24
26
|
get pagination() { return request.pagination; },
|
|
25
27
|
get sorters() { return request.sorters; },
|
|
@@ -69,6 +69,8 @@
|
|
|
69
69
|
const queryPagination = $derived({ current, pageSize: effectivePageSize });
|
|
70
70
|
const query = useList({
|
|
71
71
|
get resource() { return binding.resource; }, get dataProviderName() { return binding.dataProviderName; },
|
|
72
|
+
// 表格提供刷新和局部加载错误反馈,避免与默认查询 Toast 重复。
|
|
73
|
+
errorNotification: false,
|
|
72
74
|
get meta() { return { svadminSvarScope: dataScopeKey }; },
|
|
73
75
|
get queryOptions() { return { enabled: permission.allowed && validPageSize && validLoading && !disabled }; },
|
|
74
76
|
get pagination() { return queryPagination; }, get sorters() { return sorters; }, get filters() { return rootFilters; },
|
|
@@ -85,7 +87,7 @@
|
|
|
85
87
|
const total = $derived(permission.allowed ? query.data?.total ?? 0 : 0);
|
|
86
88
|
const pageCount = $derived(validPageSize ? Math.max(1, Math.ceil(total / effectivePageSize)) : 1);
|
|
87
89
|
const queryIdentity = $derived(JSON.stringify([resourceName, dataScopeKey, queryPagination, rootFilters, sorters, loadingMode, lazyTree]));
|
|
88
|
-
const error = $derived(query.isError ? (
|
|
90
|
+
const error = $derived(query.isError ? (zh ? '数据加载失败' : 'Data request failed') : undefined);
|
|
89
91
|
let selectedIds = $state<SvarRecordId[]>([]);
|
|
90
92
|
let busy = $state(false);
|
|
91
93
|
let feedback = $state('');
|
|
@@ -296,7 +298,7 @@
|
|
|
296
298
|
{#if !validLoading}<p role="alert">{zh ? '高级加载配置无效;窗口和无限加载仅支持平面资源,异步树必须声明父字段' : 'Invalid loading configuration: window/infinite modes require flat resources; lazy trees require a declared parent field'}</p>
|
|
297
299
|
{:else if !validPageSize}<p role="alert">{zh ? '每页数量必须为 1–1000 的整数' : 'Page size must be an integer between 1 and 1000'}</p>
|
|
298
300
|
{:else if permission.isLoading}<p role="status">{zh ? '正在检查权限' : 'Checking access'}</p>
|
|
299
|
-
{:else if !permission.allowed}<p role="alert">{permission.
|
|
301
|
+
{:else if !permission.allowed}<p role="alert">{permission.isError ? i18n.t('common.permissionUnavailable') : i18n.t('common.permissionDenied')}</p>
|
|
300
302
|
{:else}
|
|
301
303
|
{#each [...reads.requests] as [id, request] (id)}<SvarResourceRead {request} />{/each}
|
|
302
304
|
{#if savedViews}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
value?: string[];
|
|
7
|
-
name?: string;
|
|
7
|
+
name?: string | undefined;
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
separator?: string;
|
|
10
10
|
maxItems?: number;
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
required?: boolean;
|
|
13
13
|
invalid?: boolean;
|
|
14
|
-
describedby?: string;
|
|
14
|
+
describedby?: string | undefined;
|
|
15
15
|
ariaLabel?: string;
|
|
16
16
|
class?: string;
|
|
17
17
|
onchange?: (value: string[]) => void;
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
>
|
|
79
79
|
{#if name}
|
|
80
80
|
{#each value as tag (tag)}
|
|
81
|
-
<input type="hidden" {name} value={tag} />
|
|
81
|
+
<input type="hidden" {name} value={tag} {disabled} />
|
|
82
82
|
{/each}
|
|
83
83
|
{/if}
|
|
84
84
|
{#each value as tag, index (tag)}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
interface Props {
|
|
2
2
|
value?: string[];
|
|
3
|
-
name?: string;
|
|
3
|
+
name?: string | undefined;
|
|
4
4
|
placeholder?: string;
|
|
5
5
|
separator?: string;
|
|
6
6
|
maxItems?: number;
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
required?: boolean;
|
|
9
9
|
invalid?: boolean;
|
|
10
|
-
describedby?: string;
|
|
10
|
+
describedby?: string | undefined;
|
|
11
11
|
ariaLabel?: string;
|
|
12
12
|
class?: string;
|
|
13
13
|
onchange?: (value: string[]) => void;
|
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
import { useTranslation } from '@svadmin/core/i18n';
|
|
7
7
|
|
|
8
8
|
import type { TaskProvider, TaskRecord } from '@svadmin/core';
|
|
9
|
-
import { Loader2 } from '@lucide/svelte';
|
|
10
9
|
import * as Card from './ui/card/index.js';
|
|
11
10
|
import { Badge } from './ui/badge/index.js';
|
|
12
11
|
import TaskStatusBadge from './TaskStatusBadge.svelte';
|
|
13
12
|
import TaskProgressBar from './TaskProgressBar.svelte';
|
|
14
13
|
import RetryTaskButton from './RetryTaskButton.svelte';
|
|
15
14
|
import CancelTaskButton from './CancelTaskButton.svelte';
|
|
15
|
+
import DataState from './content/DataState.svelte';
|
|
16
16
|
import {
|
|
17
17
|
canCancelTask,
|
|
18
18
|
canRetryTask,
|
|
19
|
+
formatTaskDate,
|
|
19
20
|
resolveTaskCreatedAt,
|
|
20
21
|
resolveTaskError,
|
|
21
22
|
resolveTaskMessage,
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
useProviderData = !task && !!taskId,
|
|
36
37
|
queryOptions,
|
|
37
38
|
} = $props<{
|
|
38
|
-
task?: TaskRecord;
|
|
39
|
+
task?: TaskRecord | undefined;
|
|
39
40
|
taskId?: string;
|
|
40
41
|
taskProvider?: TaskProvider<TaskRecord>;
|
|
41
42
|
title?: string;
|
|
@@ -50,7 +51,9 @@
|
|
|
50
51
|
};
|
|
51
52
|
}>();
|
|
52
53
|
|
|
54
|
+
const canRead = $derived(useProviderData && !!taskId && !!taskProvider && queryOptions?.enabled !== false);
|
|
53
55
|
const query = useTask({
|
|
56
|
+
errorNotification: false,
|
|
54
57
|
get taskId() {
|
|
55
58
|
return taskId;
|
|
56
59
|
},
|
|
@@ -58,7 +61,7 @@
|
|
|
58
61
|
return taskProvider;
|
|
59
62
|
},
|
|
60
63
|
get queryOptions() {
|
|
61
|
-
return { enabled:
|
|
64
|
+
return { ...queryOptions, enabled: canRead };
|
|
62
65
|
},
|
|
63
66
|
});
|
|
64
67
|
|
|
@@ -70,16 +73,15 @@
|
|
|
70
73
|
return { data: undefined, invalid: true };
|
|
71
74
|
}
|
|
72
75
|
});
|
|
73
|
-
const resolvedTask = $derived(view.data);
|
|
76
|
+
const resolvedTask = $derived(useProviderData && (query.isError || query.isLoading) ? undefined : view.data);
|
|
74
77
|
const resolvedTitle = $derived(title ?? i18n.t('task.detailsTitle'));
|
|
75
78
|
|
|
76
|
-
function
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
if (Number.isNaN(date.getTime())) return '—';
|
|
80
|
-
return date.toLocaleString();
|
|
79
|
+
function retryRead(): void {
|
|
80
|
+
if (!canRead || query.isFetching) return;
|
|
81
|
+
void query.refetch({ cancelRefetch: false });
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
|
|
83
85
|
function toPrettyJson(value: unknown) {
|
|
84
86
|
try {
|
|
85
87
|
return JSON.stringify(value, null, 2);
|
|
@@ -101,13 +103,17 @@
|
|
|
101
103
|
</Card.Description>
|
|
102
104
|
</Card.Header>
|
|
103
105
|
<Card.Content class="svadmin-u-b43b4c086d9a svadmin-u-9335c39f6eff">
|
|
104
|
-
{#if view.invalid
|
|
106
|
+
{#if view.invalid}
|
|
105
107
|
<p role="alert">{i18n.t('validation.invalidFormat')}</p>
|
|
108
|
+
{:else if useProviderData && query.isError}
|
|
109
|
+
<DataState
|
|
110
|
+
state="error"
|
|
111
|
+
title={i18n.t('common.error')}
|
|
112
|
+
description={i18n.t('task.fetchFailed')}
|
|
113
|
+
{...definedOptions({ retry: !canRead || query.isFetching ? undefined : retryRead })}
|
|
114
|
+
/>
|
|
106
115
|
{:else if useProviderData && taskProvider && query.isLoading}
|
|
107
|
-
<
|
|
108
|
-
<Loader2 class="svadmin-u-d2347e8497a9 svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-afbdd13a380e" />
|
|
109
|
-
{i18n.t('task.loadingDetail')}
|
|
110
|
-
</div>
|
|
116
|
+
<DataState state="loading" loadingLabel={i18n.t('task.loadingDetail')} />
|
|
111
117
|
{:else if resolvedTask}
|
|
112
118
|
<div class="svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4">
|
|
113
119
|
<TaskStatusBadge status={String(resolvedTask.status ?? 'pending')} />
|
|
@@ -121,11 +127,11 @@
|
|
|
121
127
|
<div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff">
|
|
122
128
|
<div class="svadmin-u-da7c36cd8867">
|
|
123
129
|
<div class="svadmin-u-359090c2d529 uppercase svadmin-u-8baf13a3e9d7 svadmin-u-bfa603190748">{i18n.t('task.createdLabel')}</div>
|
|
124
|
-
<div class="svadmin-u-fc7473ca09eb">{
|
|
130
|
+
<div class="svadmin-u-fc7473ca09eb">{formatTaskDate(resolveTaskCreatedAt(resolvedTask), i18n.locale)}</div>
|
|
125
131
|
</div>
|
|
126
132
|
<div class="svadmin-u-da7c36cd8867">
|
|
127
133
|
<div class="svadmin-u-359090c2d529 uppercase svadmin-u-8baf13a3e9d7 svadmin-u-bfa603190748">{i18n.t('task.updatedLabel')}</div>
|
|
128
|
-
<div class="svadmin-u-fc7473ca09eb">{
|
|
134
|
+
<div class="svadmin-u-fc7473ca09eb">{formatTaskDate(resolveTaskUpdatedAt(resolvedTask), i18n.locale)}</div>
|
|
129
135
|
</div>
|
|
130
136
|
</div>
|
|
131
137
|
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
import TaskProgressBar from './TaskProgressBar.svelte';
|
|
15
15
|
import RetryTaskButton from './RetryTaskButton.svelte';
|
|
16
16
|
import CancelTaskButton from './CancelTaskButton.svelte';
|
|
17
|
+
import DataState from './content/DataState.svelte';
|
|
17
18
|
import {
|
|
18
19
|
canCancelTask,
|
|
19
20
|
canRetryTask,
|
|
21
|
+
formatTaskDate,
|
|
20
22
|
resolveTaskMessage,
|
|
21
23
|
resolveTaskProgress,
|
|
22
24
|
resolveTaskTitle,
|
|
@@ -58,7 +60,11 @@
|
|
|
58
60
|
onSelect?: (task: TaskRecord) => void;
|
|
59
61
|
}>();
|
|
60
62
|
|
|
63
|
+
const hasReadCapability = $derived(typeof (dlq ? taskProvider?.listDlq : taskProvider?.list) === 'function');
|
|
64
|
+
const readUnavailable = $derived(useProviderData && !hasReadCapability);
|
|
65
|
+
const canRead = $derived(useProviderData && hasReadCapability && queryOptions?.enabled !== false);
|
|
61
66
|
const query = useTaskList({
|
|
67
|
+
errorNotification: false,
|
|
62
68
|
get params() {
|
|
63
69
|
return params;
|
|
64
70
|
},
|
|
@@ -69,7 +75,7 @@
|
|
|
69
75
|
return taskProvider;
|
|
70
76
|
},
|
|
71
77
|
get queryOptions() {
|
|
72
|
-
return { enabled:
|
|
78
|
+
return { ...queryOptions, enabled: canRead };
|
|
73
79
|
},
|
|
74
80
|
});
|
|
75
81
|
|
|
@@ -81,31 +87,59 @@
|
|
|
81
87
|
}
|
|
82
88
|
});
|
|
83
89
|
const resolvedTasks = $derived(view.data);
|
|
90
|
+
const keyedTasks = $derived.by(() => {
|
|
91
|
+
const occurrences = new Map<string, number>();
|
|
92
|
+
return resolvedTasks.map(task => {
|
|
93
|
+
const occurrence = occurrences.get(task.id) ?? 0;
|
|
94
|
+
occurrences.set(task.id, occurrence + 1);
|
|
95
|
+
return { task, key: JSON.stringify([task.id, occurrence]) };
|
|
96
|
+
});
|
|
97
|
+
});
|
|
84
98
|
const resolvedTitle = $derived(title ?? (dlq ? i18n.t('task.dlqTitle') : i18n.t('task.listTitle')));
|
|
85
99
|
const resolvedEmptyText = $derived(emptyText ?? (dlq ? i18n.t('task.noDlq') : i18n.t('task.noTasks')));
|
|
86
100
|
|
|
87
|
-
function
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
function retryRead(): void {
|
|
102
|
+
if (!canRead || query.isFetching) return;
|
|
103
|
+
void query.refetch({ cancelRefetch: false });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function selectRow(event: MouseEvent, task: TaskRecord): void {
|
|
107
|
+
if (event.target instanceof Element
|
|
108
|
+
&& event.target.closest('button, a, input, select, textarea, [role="button"]')) return;
|
|
109
|
+
onSelect?.(task);
|
|
92
110
|
}
|
|
111
|
+
|
|
93
112
|
</script>
|
|
94
113
|
|
|
95
114
|
<Card.Root class="svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
96
115
|
<Card.Header class="svadmin-u-7fcf9124b5df">
|
|
97
116
|
<Card.Title class="svadmin-u-4ee734926ff6">{resolvedTitle}</Card.Title>
|
|
117
|
+
{#if !readUnavailable}
|
|
98
118
|
<Card.Description>
|
|
99
119
|
{#if useProviderData && taskProvider}
|
|
100
120
|
{i18n.t('task.count', { count: (query.data?.total ?? resolvedTasks.length) || 0 })}
|
|
101
121
|
{:else}
|
|
102
122
|
{i18n.t('task.count', { count: resolvedTasks.length })}
|
|
103
123
|
{/if}
|
|
104
|
-
|
|
124
|
+
</Card.Description>
|
|
125
|
+
{/if}
|
|
105
126
|
</Card.Header>
|
|
106
127
|
<Card.Content class="svadmin-u-9335c39f6eff">
|
|
107
|
-
{#if view.invalid
|
|
128
|
+
{#if view.invalid}
|
|
108
129
|
<p role="alert">{i18n.t('validation.invalidFormat')}</p>
|
|
130
|
+
{:else if readUnavailable}
|
|
131
|
+
<DataState
|
|
132
|
+
state="error"
|
|
133
|
+
title={i18n.t('common.configRequired')}
|
|
134
|
+
description={i18n.t(dlq ? 'task.dlqUnavailable' : 'task.listUnavailable')}
|
|
135
|
+
/>
|
|
136
|
+
{:else if useProviderData && query.isError}
|
|
137
|
+
<DataState
|
|
138
|
+
state="error"
|
|
139
|
+
title={i18n.t('common.error')}
|
|
140
|
+
description={i18n.t('task.fetchListFailed')}
|
|
141
|
+
{...definedOptions({ retry: !canRead || query.isFetching ? undefined : retryRead })}
|
|
142
|
+
/>
|
|
109
143
|
{:else if useProviderData && taskProvider && query.isLoading}
|
|
110
144
|
<div class="svadmin-u-60fbb7713999 svadmin-u-aadad6871af8 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-bfa603190748">
|
|
111
145
|
<Loader2 class="svadmin-u-d2347e8497a9 svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-afbdd13a380e" />
|
|
@@ -125,11 +159,17 @@
|
|
|
125
159
|
</Table.Row>
|
|
126
160
|
</Table.Header>
|
|
127
161
|
<Table.Body>
|
|
128
|
-
{#each
|
|
129
|
-
<Table.Row class="svadmin-u-34516836730d svadmin-u-f6e31b39b8e4" onclick={() =>
|
|
162
|
+
{#each keyedTasks as { task, key } (key)}
|
|
163
|
+
<Table.Row class="svadmin-u-34516836730d svadmin-u-f6e31b39b8e4" onclick={(event) => selectRow(event, task)}>
|
|
130
164
|
<Table.Cell class="svadmin-u-00cde2e9f1cf">
|
|
131
165
|
<div class="svadmin-u-da7c36cd8867">
|
|
132
|
-
|
|
166
|
+
{#if onSelect && !showActions}
|
|
167
|
+
<Button type="button" variant="ghost" size="sm" onclick={() => onSelect?.(task)}>
|
|
168
|
+
{resolveTaskTitle(task)}
|
|
169
|
+
</Button>
|
|
170
|
+
{:else}
|
|
171
|
+
<div class="svadmin-u-2689f3958069 svadmin-u-d4108abe6359">{resolveTaskTitle(task)}</div>
|
|
172
|
+
{/if}
|
|
133
173
|
{#if resolveTaskMessage(task)}
|
|
134
174
|
<div class="svadmin-u-359090c2d529 svadmin-u-bfa603190748">{resolveTaskMessage(task)}</div>
|
|
135
175
|
{/if}
|
|
@@ -142,7 +182,7 @@
|
|
|
142
182
|
<TaskStatusBadge status={String(task.status ?? 'pending')} />
|
|
143
183
|
</Table.Cell>
|
|
144
184
|
<Table.Cell class="svadmin-u-359090c2d529 svadmin-u-bfa603190748">
|
|
145
|
-
{
|
|
185
|
+
{formatTaskDate(resolveTaskUpdatedAt(task), i18n.locale)}
|
|
146
186
|
</Table.Cell>
|
|
147
187
|
{#if showActions}
|
|
148
188
|
<Table.Cell class="svadmin-u-308fc069e46e">
|
|
@@ -153,9 +193,11 @@
|
|
|
153
193
|
{#if taskProvider?.cancel && canCancelTask(task)}
|
|
154
194
|
<CancelTaskButton taskId={task.id} {taskProvider} />
|
|
155
195
|
{/if}
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
|
|
196
|
+
{#if onSelect}
|
|
197
|
+
<Button type="button" variant="ghost" size="sm" onclick={() => onSelect?.(task)}>
|
|
198
|
+
{i18n.t('task.detailsAction')}
|
|
199
|
+
</Button>
|
|
200
|
+
{/if}
|
|
159
201
|
</div>
|
|
160
202
|
</Table.Cell>
|
|
161
203
|
{/if}
|