@svadmin/core 0.50.0 → 0.52.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/package.json +21 -4
- package/src/access-control-contract.ts +250 -0
- package/src/adapters/casbin.ts +20 -14
- package/src/adapters/casl.ts +22 -19
- package/src/admin-tool.ts +7 -7
- package/src/audit-contract.ts +105 -0
- package/src/audit.ts +88 -128
- package/src/auth-hooks.svelte.ts +593 -261
- package/src/auth-mutation-contract.ts +143 -0
- package/src/auth-query-contract.ts +97 -0
- package/src/captured-mutation.svelte.ts +45 -0
- package/src/chatProvider.svelte.ts +1 -1
- package/src/command-contract.ts +195 -0
- package/src/context.svelte.ts +217 -205
- package/src/contract-context.svelte.ts +37 -0
- package/src/contract-form.svelte.ts +566 -0
- package/src/create-contract.ts +56 -0
- package/src/create-hooks.svelte.ts +347 -0
- package/src/create-many-contract.ts +90 -0
- package/src/create-many-hooks.svelte.ts +396 -0
- package/src/data-transfer.svelte.ts +1 -215
- package/src/defined-options.ts +50 -0
- package/src/delete-contract.ts +50 -0
- package/src/delete-hooks.svelte.ts +403 -0
- package/src/delete-many-contract.ts +60 -0
- package/src/delete-many-hooks.svelte.ts +402 -0
- package/src/enterprise.ts +4 -4
- package/src/export-format.ts +16 -14
- package/src/export-hooks.svelte.ts +191 -0
- package/src/feature-gate-contract.ts +53 -0
- package/src/hook-utils.svelte.ts +53 -105
- package/src/hooks.svelte.ts +19 -776
- package/src/http.ts +88 -87
- package/src/i18n.svelte.ts +56 -2
- package/src/import-contract.ts +106 -0
- package/src/import-hooks.svelte.ts +310 -0
- package/src/index.ts +77 -30
- package/src/inferencer-graphql.ts +7 -6
- package/src/infinite-query.svelte.ts +228 -0
- package/src/invalidation-contract.ts +26 -0
- package/src/live-sse.ts +91 -127
- package/src/live-subscription.svelte.ts +130 -0
- package/src/live-transport.ts +127 -0
- package/src/live-websocket.ts +85 -118
- package/src/live.svelte.ts +66 -68
- package/src/notification.svelte.ts +17 -15
- package/src/permission-catalog.ts +121 -0
- package/src/permission-hints-contract.ts +48 -0
- package/src/permissions.svelte.ts +112 -59
- package/src/plain-data.ts +61 -0
- package/src/provider-bundle.ts +85 -79
- package/src/provider-response.ts +35 -0
- package/src/query-hooks.svelte.ts +241 -288
- package/src/query-invalidation.ts +172 -0
- package/src/query-keys.ts +221 -110
- package/src/query-session.svelte.ts +216 -0
- package/src/query-snapshot.ts +144 -0
- package/src/reactive-projection.ts +53 -0
- package/src/record-decoder.ts +63 -0
- package/src/resource-binding.svelte.ts +19 -0
- package/src/resource-contract.ts +595 -0
- package/src/resource-mutation-types.ts +30 -0
- package/src/resource-schemas.ts +371 -0
- package/src/resource-transport-config.ts +156 -0
- package/src/route-parsing.ts +11 -11
- package/src/router-provider.ts +64 -59
- package/src/router.ts +8 -3
- package/src/routing-hooks.svelte.ts +90 -62
- package/src/schema-validation.ts +92 -0
- package/src/schema.ts +13 -0
- package/src/select-options.ts +95 -0
- package/src/select-query.svelte.ts +191 -0
- package/src/session-query.svelte.ts +88 -0
- package/src/strict-command-hooks.svelte.ts +333 -0
- package/src/strict-hooks.svelte.ts +198 -0
- package/src/table-hooks.svelte.ts +180 -174
- package/src/table-state.svelte.ts +117 -117
- package/src/task-contract.ts +87 -0
- package/src/task-hooks.svelte.ts +193 -149
- package/src/task-provider.ts +115 -0
- package/src/task-subscription.ts +132 -0
- package/src/theme.svelte.ts +155 -154
- package/src/toast.svelte.ts +38 -37
- package/src/types.ts +55 -108
- package/src/unsafe.ts +7 -0
- package/src/update-contract.ts +65 -0
- package/src/update-hooks.svelte.ts +347 -0
- package/src/update-many-contract.ts +79 -0
- package/src/update-many-hooks.svelte.ts +373 -0
- package/src/url-sync.ts +11 -10
- package/src/useCan.ts +96 -31
- package/src/useParsed.svelte.ts +18 -14
- package/src/useStepsForm.svelte.ts +123 -63
- package/src/utility-hooks.svelte.ts +101 -158
- package/src/form-hooks.svelte.ts +0 -889
- package/src/mutation-hooks.svelte.ts +0 -561
- package/src/schema-form.ts +0 -31
package/src/hooks.svelte.ts
CHANGED
|
@@ -3,821 +3,64 @@
|
|
|
3
3
|
// ─── Re-exports from modular hook files ─────────────────────────────
|
|
4
4
|
|
|
5
5
|
export * from './query-hooks.svelte';
|
|
6
|
-
export * from './mutation-hooks.svelte';
|
|
7
|
-
export * from './form-hooks.svelte';
|
|
8
6
|
export * from './table-hooks.svelte';
|
|
9
7
|
export * from './routing-hooks.svelte';
|
|
10
8
|
export * from './utility-hooks.svelte';
|
|
11
9
|
|
|
12
10
|
// ─── Re-export types from options ─────────────────────────────────
|
|
13
|
-
export type { InvalidateScope,
|
|
11
|
+
export type { InvalidateScope,OvertimeConfig } from './options.svelte';
|
|
14
12
|
|
|
15
13
|
// ─── Export shared utilities ────────────────────────────────────────
|
|
16
14
|
|
|
17
|
-
export { createOvertimeTracker,
|
|
18
|
-
export type { OvertimeResult,
|
|
15
|
+
export { createOvertimeTracker,createLiveSubscription } from './hook-utils.svelte';
|
|
16
|
+
export type { OvertimeResult,OvertimeOptions,NotificationConfig } from './hook-utils.svelte';
|
|
19
17
|
|
|
20
18
|
// ─── Additional core hooks ─────────────────────────────────────────
|
|
21
19
|
|
|
22
|
-
import { createQuery, createInfiniteQuery, createMutation, useQueryClient } from '@tanstack/svelte-query';
|
|
23
|
-
import { getAdminOptions } from './options.svelte';
|
|
24
20
|
import { captureAdminContext } from './context.svelte';
|
|
25
|
-
import {
|
|
26
|
-
import type {
|
|
27
|
-
import {
|
|
28
|
-
import { createOvertimeTracker, createLiveSubscription, fireSuccessNotification, fireErrorNotification, checkError } from './hook-utils.svelte';
|
|
29
|
-
import { invalidateByScopes, publishLiveEvent } from './mutation-hooks.svelte';
|
|
30
|
-
import type { NotificationConfig, OvertimeOptions, LiveSubscriptionParams } from './hook-utils.svelte';
|
|
31
|
-
import { DeleteManyPartialError, UndoError } from './types';
|
|
32
|
-
import type { BaseRecord, HttpError, Pagination, Sort, Filter, DataProvider, KnownResources, MutationMode } from './types';
|
|
33
|
-
import type { LiveMode, LiveEvent } from './live.svelte';
|
|
34
|
-
import { auditWithProvider } from './audit';
|
|
35
|
-
import { useTranslation } from './i18n.svelte';
|
|
36
|
-
import { toast } from './toast.svelte';
|
|
37
|
-
import type { MutationCallbacks } from './mutation-hooks.svelte';
|
|
21
|
+
import { createOvertimeTracker } from './hook-utils.svelte';
|
|
22
|
+
import type { OvertimeOptions } from './hook-utils.svelte';
|
|
23
|
+
import type { DataProvider } from './types';
|
|
38
24
|
|
|
39
25
|
// ─── useInfiniteList ────────────────────────────────────────────────
|
|
40
26
|
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
pagination?: Pagination;
|
|
44
|
-
sorters?: Sort[];
|
|
45
|
-
filters?: Filter[];
|
|
46
|
-
meta?: Record<string, unknown>;
|
|
47
|
-
dataProviderName?: string;
|
|
48
|
-
queryOptions?: { staleTime?: number; enabled?: boolean };
|
|
49
|
-
successNotification?: NotificationConfig;
|
|
50
|
-
errorNotification?: NotificationConfig;
|
|
51
|
-
liveMode?: LiveMode;
|
|
52
|
-
onLiveEvent?: (event: LiveEvent) => void;
|
|
53
|
-
overtimeOptions?: OvertimeOptions;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function useInfiniteList<TData extends BaseRecord = BaseRecord, TError = HttpError>(options: UseInfiniteListOptions<TData, TError> = {}) {
|
|
57
|
-
const adminContext = captureAdminContext();
|
|
58
|
-
const parsed = useParsed();
|
|
59
|
-
const adminOptions = getAdminOptions();
|
|
60
|
-
|
|
61
|
-
const query = createInfiniteQuery<{ data: TData[]; total: number }, TError>(() => {
|
|
62
|
-
const resource = options.resource ?? parsed.resource ?? '';
|
|
63
|
-
const provider = adminContext.getDataProviderForResource(resource, options.dataProviderName);
|
|
64
|
-
return {
|
|
65
|
-
queryKey: adminContext.queryKeys(resource, options.dataProviderName).data.infiniteList(resource, {
|
|
66
|
-
pageSize: options.pagination?.pageSize,
|
|
67
|
-
sorters: options.sorters,
|
|
68
|
-
filters: options.filters,
|
|
69
|
-
meta: options.meta,
|
|
70
|
-
}),
|
|
71
|
-
queryFn: async ({ pageParam = 1, signal }) => {
|
|
72
|
-
const result = await provider.getList<TData>({
|
|
73
|
-
signal,
|
|
74
|
-
resource,
|
|
75
|
-
pagination: { current: pageParam as number, pageSize: options.pagination?.pageSize ?? 10 },
|
|
76
|
-
sorters: options.sorters,
|
|
77
|
-
filters: options.filters,
|
|
78
|
-
meta: options.meta,
|
|
79
|
-
});
|
|
80
|
-
return result;
|
|
81
|
-
},
|
|
82
|
-
initialPageParam: 1,
|
|
83
|
-
getNextPageParam: (lastPage: { data: TData[]; total: number }, allPages: { data: TData[]; total: number }[]) => {
|
|
84
|
-
const totalFetched = allPages.reduce((acc: number, p) => acc + (p.data?.length ?? 0), 0);
|
|
85
|
-
if (totalFetched >= (lastPage.total ?? 0)) return undefined;
|
|
86
|
-
return allPages.length + 1;
|
|
87
|
-
},
|
|
88
|
-
enabled: options.queryOptions?.enabled ?? true,
|
|
89
|
-
staleTime: options.queryOptions?.staleTime ?? adminOptions.reactQuery?.staleTime,
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const overtime = createOvertimeTracker(() => query.isLoading, options.overtimeOptions ?? adminOptions.overtime);
|
|
94
|
-
|
|
95
|
-
createLiveSubscription((): LiveSubscriptionParams => ({
|
|
96
|
-
resource: options.resource ?? parsed.resource ?? '',
|
|
97
|
-
liveProvider: adminContext.liveProvider,
|
|
98
|
-
liveMode: options.liveMode ?? adminOptions.liveMode,
|
|
99
|
-
onLiveEvent: (e: LiveEvent) => {
|
|
100
|
-
options.onLiveEvent?.(e);
|
|
101
|
-
adminOptions.onLiveEvent?.(e);
|
|
102
|
-
},
|
|
103
|
-
enabled: options.queryOptions?.enabled ?? true,
|
|
104
|
-
dataProviderName: options.dataProviderName,
|
|
105
|
-
}));
|
|
106
|
-
|
|
107
|
-
let lastSuccessAt = 0;
|
|
108
|
-
let lastErrorAt = 0;
|
|
109
|
-
$effect(() => {
|
|
110
|
-
if (query.isSuccess && query.dataUpdatedAt > lastSuccessAt && options.successNotification) {
|
|
111
|
-
lastSuccessAt = query.dataUpdatedAt;
|
|
112
|
-
fireSuccessNotification({
|
|
113
|
-
config: options.successNotification,
|
|
114
|
-
defaultMessage: '',
|
|
115
|
-
data: query.data,
|
|
116
|
-
resource: options.resource ?? parsed.resource ?? '',
|
|
117
|
-
provider: adminContext.notificationProvider,
|
|
118
|
-
});
|
|
119
|
-
} else if (query.isError && query.errorUpdatedAt > lastErrorAt) {
|
|
120
|
-
lastErrorAt = query.errorUpdatedAt;
|
|
121
|
-
checkError(query.error, adminContext);
|
|
122
|
-
fireErrorNotification({
|
|
123
|
-
config: options.errorNotification,
|
|
124
|
-
defaultMessage: 'Fetch failed',
|
|
125
|
-
error: query.error,
|
|
126
|
-
resource: options.resource ?? parsed.resource ?? '',
|
|
127
|
-
provider: adminContext.notificationProvider,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return { query, get overtime() { return overtime; } };
|
|
133
|
-
}
|
|
27
|
+
export { useInfiniteList, createInfiniteListQuery } from './infinite-query.svelte';
|
|
28
|
+
export type { UseInfiniteListOptions } from './infinite-query.svelte';
|
|
134
29
|
|
|
135
30
|
// ─── useSelect ──────────────────────────────────────────────────────
|
|
136
31
|
|
|
137
|
-
export
|
|
138
|
-
|
|
139
|
-
optionLabel?: string | ((item: TData) => string);
|
|
140
|
-
optionValue?: string | ((item: TData) => string | number);
|
|
141
|
-
sorters?: Sort[];
|
|
142
|
-
filters?: Filter[];
|
|
143
|
-
defaultValue?: (string | number)[];
|
|
144
|
-
fetchSize?: number;
|
|
145
|
-
pagination?: Pagination;
|
|
146
|
-
debounce?: number;
|
|
147
|
-
queryOptions?: { staleTime?: number; enabled?: boolean };
|
|
148
|
-
defaultValueQueryOptions?: { staleTime?: number; enabled?: boolean };
|
|
149
|
-
meta?: Record<string, unknown>;
|
|
150
|
-
dataProviderName?: string;
|
|
151
|
-
onSearch?: (value: string) => Filter[];
|
|
152
|
-
successNotification?: NotificationConfig;
|
|
153
|
-
errorNotification?: NotificationConfig;
|
|
154
|
-
overtimeOptions?: OvertimeOptions;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function useSelect<TData extends BaseRecord = BaseRecord, TOption = { label: string; value: string | number }>(options: UseSelectOptions<TData, TOption>) {
|
|
158
|
-
const adminContext = captureAdminContext();
|
|
159
|
-
const { resource, optionLabel = 'title', optionValue = 'id', sorters, filters, pagination, meta, dataProviderName, onSearch, debounce: debounceMs = 300 } = options;
|
|
160
|
-
const adminOptions = getAdminOptions();
|
|
161
|
-
|
|
162
|
-
let searchText = $state('');
|
|
163
|
-
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
164
|
-
|
|
165
|
-
const searchFilters = $derived.by<Filter[]>(() => {
|
|
166
|
-
if (!onSearch || !searchText) return [];
|
|
167
|
-
return onSearch(searchText);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const allFilters = $derived([...(filters ?? []), ...searchFilters]);
|
|
171
|
-
|
|
172
|
-
const effectivePageSize = options.fetchSize ?? pagination?.pageSize ?? 999;
|
|
173
|
-
|
|
174
|
-
const query = createQuery<{ data: TData[]; total: number }>(() => {
|
|
175
|
-
const provider = adminContext.getDataProviderForResource(resource, dataProviderName);
|
|
176
|
-
return {
|
|
177
|
-
queryKey: adminContext.queryKeys(resource, dataProviderName).data.select(resource, {
|
|
178
|
-
filters: allFilters,
|
|
179
|
-
sorters,
|
|
180
|
-
pagination,
|
|
181
|
-
meta,
|
|
182
|
-
}),
|
|
183
|
-
queryFn: ({ signal }) => provider.getList<TData>({ resource, sorters, filters: allFilters, pagination: { current: 1, pageSize: effectivePageSize }, meta, signal }),
|
|
184
|
-
enabled: options.queryOptions?.enabled ?? true,
|
|
185
|
-
staleTime: options.queryOptions?.staleTime ?? adminOptions.reactQuery?.staleTime,
|
|
186
|
-
};
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// Parallel query to ensure default values are always available in options
|
|
190
|
-
const defaultValueIds = options.defaultValue ?? [];
|
|
191
|
-
const defaultValueQuery = defaultValueIds.length > 0
|
|
192
|
-
? createQuery<{ data: TData[] }>(() => {
|
|
193
|
-
const provider = adminContext.getDataProviderForResource(resource, dataProviderName);
|
|
194
|
-
return {
|
|
195
|
-
queryKey: adminContext.queryKeys(resource, dataProviderName).data.selectDefaults(resource, {
|
|
196
|
-
ids: defaultValueIds,
|
|
197
|
-
meta,
|
|
198
|
-
}),
|
|
199
|
-
queryFn: async ({ signal }) => {
|
|
200
|
-
if (provider.getMany) return provider.getMany<TData>({ resource, ids: defaultValueIds, meta, signal });
|
|
201
|
-
const results = await Promise.all(defaultValueIds.map(id => provider.getOne<TData>({ resource, id, meta, signal })));
|
|
202
|
-
return { data: results.map(r => r.data) };
|
|
203
|
-
},
|
|
204
|
-
enabled: (options.defaultValueQueryOptions?.enabled ?? true) && defaultValueIds.length > 0,
|
|
205
|
-
staleTime: options.defaultValueQueryOptions?.staleTime ?? Infinity,
|
|
206
|
-
};
|
|
207
|
-
})
|
|
208
|
-
: null;
|
|
209
|
-
|
|
210
|
-
const selectOptions = $derived.by(() => {
|
|
211
|
-
const queryResult = query.data as { data: TData[]; total: number } | undefined;
|
|
212
|
-
const data: TData[] = queryResult?.data ?? [];
|
|
213
|
-
// Merge default value items if not already in data
|
|
214
|
-
const defaultQueryResult = defaultValueQuery as { data: { data: TData[] } | undefined } | null;
|
|
215
|
-
const defaultData: TData[] = defaultQueryResult?.data?.data ?? [];
|
|
216
|
-
const allData: TData[] = [...data];
|
|
217
|
-
const resolveValue = (item: TData) => typeof optionValue === 'function' ? optionValue(item) : (item as Record<string, unknown>)[optionValue];
|
|
218
|
-
const existingIds = new Set(data.map((d: TData) => String(resolveValue(d))));
|
|
219
|
-
for (const item of defaultData) {
|
|
220
|
-
if (!existingIds.has(String(resolveValue(item)))) allData.push(item);
|
|
221
|
-
}
|
|
222
|
-
return allData.map((item: TData) => {
|
|
223
|
-
const label = typeof optionLabel === 'function' ? optionLabel(item) : String((item as Record<string, unknown>)[optionLabel] ?? '');
|
|
224
|
-
const value = resolveValue(item);
|
|
225
|
-
return { label, value } as unknown as TOption;
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const overtime = createOvertimeTracker(() => query.isLoading, options.overtimeOptions ?? adminOptions.overtime);
|
|
230
|
-
|
|
231
|
-
let lastSuccessAt = 0;
|
|
232
|
-
let lastErrorAt = 0;
|
|
233
|
-
$effect(() => {
|
|
234
|
-
if (query.isSuccess && query.dataUpdatedAt > lastSuccessAt && options.successNotification) {
|
|
235
|
-
lastSuccessAt = query.dataUpdatedAt;
|
|
236
|
-
fireSuccessNotification({
|
|
237
|
-
config: options.successNotification,
|
|
238
|
-
defaultMessage: '',
|
|
239
|
-
data: query.data,
|
|
240
|
-
resource,
|
|
241
|
-
provider: adminContext.notificationProvider,
|
|
242
|
-
});
|
|
243
|
-
} else if (query.isError && query.errorUpdatedAt > lastErrorAt) {
|
|
244
|
-
lastErrorAt = query.errorUpdatedAt;
|
|
245
|
-
checkError(query.error, adminContext);
|
|
246
|
-
fireErrorNotification({
|
|
247
|
-
config: options.errorNotification,
|
|
248
|
-
defaultMessage: 'Fetch failed',
|
|
249
|
-
error: query.error,
|
|
250
|
-
resource,
|
|
251
|
-
provider: adminContext.notificationProvider,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
function onSearchChange(value: string) {
|
|
257
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
258
|
-
debounceTimer = setTimeout(() => { searchText = value; }, debounceMs);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Cleanup debounce timer on unmount
|
|
262
|
-
$effect(() => {
|
|
263
|
-
return () => { if (debounceTimer) clearTimeout(debounceTimer); };
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
return {
|
|
267
|
-
query,
|
|
268
|
-
get options() { return selectOptions; },
|
|
269
|
-
get overtime() { return overtime; },
|
|
270
|
-
onSearchChange,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ─── useCustom ──────────────────────────────────────────────────────
|
|
275
|
-
|
|
276
|
-
export interface UseCustomOptions<_TData = unknown, _TError = HttpError> {
|
|
277
|
-
url: string;
|
|
278
|
-
method: 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
279
|
-
config?: { payload?: unknown; query?: Record<string, unknown>; headers?: Record<string, string>; sorters?: Sort[]; filters?: Filter[] };
|
|
280
|
-
meta?: Record<string, unknown>;
|
|
281
|
-
dataProviderName?: string;
|
|
282
|
-
queryOptions?: { staleTime?: number; enabled?: boolean };
|
|
283
|
-
successNotification?: NotificationConfig;
|
|
284
|
-
errorNotification?: NotificationConfig;
|
|
285
|
-
overtimeOptions?: OvertimeOptions;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export function useCustom<TData = unknown, TError = HttpError>(options: UseCustomOptions<TData, TError>) {
|
|
289
|
-
const adminContext = captureAdminContext();
|
|
290
|
-
const adminOptions = getAdminOptions();
|
|
291
|
-
|
|
292
|
-
const query = createQuery<{ data: TData }, TError>(() => {
|
|
293
|
-
const provider = adminContext.getDataProvider(options.dataProviderName);
|
|
294
|
-
return {
|
|
295
|
-
queryKey: adminContext.queryKeys(undefined, options.dataProviderName).custom.call(
|
|
296
|
-
options.url,
|
|
297
|
-
options.url,
|
|
298
|
-
options.method,
|
|
299
|
-
{ config: options.config, meta: options.meta },
|
|
300
|
-
),
|
|
301
|
-
queryFn: async ({ signal }) => {
|
|
302
|
-
if (!provider.custom) throw new Error('DataProvider does not support custom method');
|
|
303
|
-
return provider.custom<TData>({
|
|
304
|
-
signal,
|
|
305
|
-
url: options.url,
|
|
306
|
-
method: options.method,
|
|
307
|
-
payload: options.config?.payload,
|
|
308
|
-
query: options.config?.query,
|
|
309
|
-
headers: options.config?.headers,
|
|
310
|
-
sorters: options.config?.sorters,
|
|
311
|
-
filters: options.config?.filters,
|
|
312
|
-
meta: options.meta,
|
|
313
|
-
});
|
|
314
|
-
},
|
|
315
|
-
enabled: options.queryOptions?.enabled ?? true,
|
|
316
|
-
staleTime: options.queryOptions?.staleTime ?? adminOptions.reactQuery?.staleTime,
|
|
317
|
-
};
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const overtime = createOvertimeTracker(() => query.isLoading, options.overtimeOptions ?? adminOptions.overtime);
|
|
321
|
-
|
|
322
|
-
let lastSuccessAt = 0;
|
|
323
|
-
let lastErrorAt = 0;
|
|
324
|
-
$effect(() => {
|
|
325
|
-
if (query.isSuccess && query.dataUpdatedAt > lastSuccessAt && options.successNotification) {
|
|
326
|
-
lastSuccessAt = query.dataUpdatedAt;
|
|
327
|
-
fireSuccessNotification({
|
|
328
|
-
config: options.successNotification,
|
|
329
|
-
defaultMessage: '',
|
|
330
|
-
data: query.data,
|
|
331
|
-
provider: adminContext.notificationProvider,
|
|
332
|
-
});
|
|
333
|
-
} else if (query.isError && query.errorUpdatedAt > lastErrorAt) {
|
|
334
|
-
lastErrorAt = query.errorUpdatedAt;
|
|
335
|
-
checkError(query.error, adminContext);
|
|
336
|
-
fireErrorNotification({
|
|
337
|
-
config: options.errorNotification,
|
|
338
|
-
defaultMessage: 'Custom request failed',
|
|
339
|
-
error: query.error,
|
|
340
|
-
resource: '',
|
|
341
|
-
provider: adminContext.notificationProvider,
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
return { query, get overtime() { return overtime; } };
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// ─── useCustomMutation ──────────────────────────────────────────────
|
|
350
|
-
|
|
351
|
-
export function useCustomMutation<TData = unknown, TError = HttpError, TVariables = unknown>(dataProviderName?: string) {
|
|
352
|
-
const adminContext = captureAdminContext();
|
|
353
|
-
const queryClient = useQueryClient();
|
|
354
|
-
|
|
355
|
-
const mutation = createMutation<{ data: TData }, TError, { url: string; method: 'get' | 'post' | 'put' | 'patch' | 'delete'; values?: TVariables; query?: Record<string, unknown>; headers?: Record<string, string>; sorters?: Sort[]; filters?: Filter[]; meta?: Record<string, unknown>; invalidates?: string[] | false; resource?: string }>(() => ({
|
|
356
|
-
mutationFn: async (params) => {
|
|
357
|
-
const provider = adminContext.getDataProvider(dataProviderName);
|
|
358
|
-
if (!provider.custom) throw new Error('DataProvider does not support custom method');
|
|
359
|
-
return provider.custom<TData>({ url: params.url, method: params.method, payload: params.values, query: params.query, headers: params.headers, sorters: params.sorters, filters: params.filters, meta: params.meta });
|
|
360
|
-
},
|
|
361
|
-
onSuccess: (data, params) => {
|
|
362
|
-
if (params.resource && params.invalidates !== false) {
|
|
363
|
-
invalidateByScopes({
|
|
364
|
-
queryClient,
|
|
365
|
-
resource: params.resource,
|
|
366
|
-
scopes: params.invalidates,
|
|
367
|
-
defaults: ['list', 'many'],
|
|
368
|
-
matcher: adminContext.queryKeyMatcher(params.resource, dataProviderName),
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
onError: (error) => {
|
|
373
|
-
checkError(error, adminContext);
|
|
374
|
-
},
|
|
375
|
-
}));
|
|
376
|
-
|
|
377
|
-
return { mutation };
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// ─── useCreateMany / useUpdateMany / useDeleteMany ──────────────────
|
|
381
|
-
|
|
382
|
-
export function useCreateMany<TData extends BaseRecord = BaseRecord, TError = HttpError, TVariables = Record<string, unknown>>(options: { resource?: KnownResources; overtimeOptions?: OvertimeOptions } = {}) {
|
|
383
|
-
const adminContext = captureAdminContext();
|
|
384
|
-
const parsed = useParsed();
|
|
385
|
-
const resource = options.resource ?? parsed.resource ?? '';
|
|
386
|
-
const adminOptions = getAdminOptions();
|
|
387
|
-
const queryClient = useQueryClient();
|
|
388
|
-
|
|
389
|
-
const mutation = createMutation<{ data: TData[] }, TError, { resource?: KnownResources; variables: TVariables[]; meta?: Record<string, unknown>; dataProviderName?: string }>(() => ({
|
|
390
|
-
mutationFn: async (params) => {
|
|
391
|
-
const resName = params.resource ?? resource;
|
|
392
|
-
const provider = adminContext.getDataProviderForResource(resName, params.dataProviderName);
|
|
393
|
-
if (provider.createMany) return await provider.createMany<TData, TVariables>({ resource: resName, variables: params.variables, meta: params.meta });
|
|
394
|
-
const results = await Promise.all(params.variables.map(v => provider.create<TData, TVariables>({ resource: resName, variables: v, meta: params.meta })));
|
|
395
|
-
return { data: results.map(r => r.data) };
|
|
396
|
-
},
|
|
397
|
-
onSuccess: (data, params) => {
|
|
398
|
-
const resName = params.resource ?? resource;
|
|
399
|
-
fireSuccessNotification({
|
|
400
|
-
config: undefined,
|
|
401
|
-
defaultMessage: 'Created successfully',
|
|
402
|
-
data: data.data,
|
|
403
|
-
values: params.variables,
|
|
404
|
-
resource: resName,
|
|
405
|
-
provider: adminContext.notificationProvider,
|
|
406
|
-
});
|
|
407
|
-
auditWithProvider(
|
|
408
|
-
{ action: 'create', resource: resName, meta: adminContext.getProviderMeta(resName) },
|
|
409
|
-
adminContext.auditLogProvider,
|
|
410
|
-
);
|
|
411
|
-
publishLiveEvent(resName, 'created', undefined, adminContext);
|
|
412
|
-
},
|
|
413
|
-
onError: (error, params) => {
|
|
414
|
-
checkError(error, adminContext);
|
|
415
|
-
fireErrorNotification({
|
|
416
|
-
config: undefined,
|
|
417
|
-
defaultMessage: 'Create many failed',
|
|
418
|
-
error,
|
|
419
|
-
resource: params.resource ?? resource,
|
|
420
|
-
provider: adminContext.notificationProvider,
|
|
421
|
-
});
|
|
422
|
-
},
|
|
423
|
-
onSettled: (_d, _e, params) => {
|
|
424
|
-
const resName = params.resource ?? resource;
|
|
425
|
-
invalidateByScopes({
|
|
426
|
-
queryClient,
|
|
427
|
-
resource: resName,
|
|
428
|
-
defaults: ['list', 'many'],
|
|
429
|
-
matcher: adminContext.queryKeyMatcher(resName, params.dataProviderName),
|
|
430
|
-
});
|
|
431
|
-
},
|
|
432
|
-
}));
|
|
433
|
-
|
|
434
|
-
return { mutation, get overtime() { return createOvertimeTracker(() => mutation.isPending, options.overtimeOptions ?? adminOptions.overtime); } };
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
export function useUpdateMany<TData extends BaseRecord = BaseRecord, TError = HttpError, TVariables = Record<string, unknown>>(options: { resource?: KnownResources; overtimeOptions?: OvertimeOptions } = {}) {
|
|
438
|
-
const adminContext = captureAdminContext();
|
|
439
|
-
const parsed = useParsed();
|
|
440
|
-
const resource = options.resource ?? parsed.resource ?? '';
|
|
441
|
-
const adminOptions = getAdminOptions();
|
|
442
|
-
const queryClient = useQueryClient();
|
|
443
|
-
|
|
444
|
-
const mutation = createMutation<{ data: TData[] }, TError, { resource?: KnownResources; ids: (string | number)[]; variables: TVariables; meta?: Record<string, unknown>; dataProviderName?: string }>(() => ({
|
|
445
|
-
mutationFn: async (params) => {
|
|
446
|
-
const resName = params.resource ?? resource;
|
|
447
|
-
const provider = adminContext.getDataProviderForResource(resName, params.dataProviderName);
|
|
448
|
-
if (provider.updateMany) return await provider.updateMany<TData, TVariables>({ resource: resName, ids: params.ids, variables: params.variables, meta: params.meta });
|
|
449
|
-
const results = await Promise.all(params.ids.map(id => provider.update<TData, TVariables>({ resource: resName, id, variables: params.variables, meta: params.meta })));
|
|
450
|
-
return { data: results.map(r => r.data) };
|
|
451
|
-
},
|
|
452
|
-
onSuccess: (data, params) => {
|
|
453
|
-
const resName = params.resource ?? resource;
|
|
454
|
-
fireSuccessNotification({
|
|
455
|
-
config: undefined,
|
|
456
|
-
defaultMessage: 'Updated successfully',
|
|
457
|
-
data: data.data,
|
|
458
|
-
values: params.variables,
|
|
459
|
-
resource: resName,
|
|
460
|
-
provider: adminContext.notificationProvider,
|
|
461
|
-
});
|
|
462
|
-
auditWithProvider(
|
|
463
|
-
{ action: 'update', resource: resName, meta: adminContext.getProviderMeta(resName) },
|
|
464
|
-
adminContext.auditLogProvider,
|
|
465
|
-
);
|
|
466
|
-
publishLiveEvent(resName, 'updated', params.ids, adminContext);
|
|
467
|
-
},
|
|
468
|
-
onError: (error, params) => {
|
|
469
|
-
checkError(error, adminContext);
|
|
470
|
-
fireErrorNotification({
|
|
471
|
-
config: undefined,
|
|
472
|
-
defaultMessage: 'Update many failed',
|
|
473
|
-
error,
|
|
474
|
-
resource: params.resource ?? resource,
|
|
475
|
-
provider: adminContext.notificationProvider,
|
|
476
|
-
});
|
|
477
|
-
},
|
|
478
|
-
onSettled: (_d, _e, params) => {
|
|
479
|
-
const resName = params.resource ?? resource;
|
|
480
|
-
invalidateByScopes({
|
|
481
|
-
queryClient,
|
|
482
|
-
resource: resName,
|
|
483
|
-
defaults: ['list', 'many', 'detail'],
|
|
484
|
-
matcher: adminContext.queryKeyMatcher(resName, params.dataProviderName),
|
|
485
|
-
});
|
|
486
|
-
for (const id of params.ids) {
|
|
487
|
-
queryClient.invalidateQueries({
|
|
488
|
-
predicate: (q) => queryKeyMatches(q.queryKey, {
|
|
489
|
-
...adminContext.queryKeyMatcher(resName, params.dataProviderName),
|
|
490
|
-
kind: 'data',
|
|
491
|
-
resource: resName,
|
|
492
|
-
action: 'one',
|
|
493
|
-
id,
|
|
494
|
-
}),
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
}));
|
|
499
|
-
|
|
500
|
-
return { mutation, get overtime() { return createOvertimeTracker(() => mutation.isPending, options.overtimeOptions ?? adminOptions.overtime); } };
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
export interface UseDeleteManyOptions {
|
|
504
|
-
resource?: KnownResources;
|
|
505
|
-
mutationMode?: MutationMode;
|
|
506
|
-
undoableTimeout?: number;
|
|
507
|
-
mutationOptions?: MutationCallbacks;
|
|
508
|
-
overtimeOptions?: OvertimeOptions;
|
|
509
|
-
successNotification?: NotificationConfig;
|
|
510
|
-
errorNotification?: NotificationConfig;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
export interface UseDeleteManyMutateParams {
|
|
514
|
-
resource?: KnownResources;
|
|
515
|
-
ids: (string | number)[];
|
|
516
|
-
mutationMode?: MutationMode;
|
|
517
|
-
undoableTimeout?: number;
|
|
518
|
-
meta?: Record<string, unknown>;
|
|
519
|
-
dataProviderName?: string;
|
|
520
|
-
invalidates?: string[] | false;
|
|
521
|
-
onCancel?: () => void;
|
|
522
|
-
successNotification?: NotificationConfig;
|
|
523
|
-
errorNotification?: NotificationConfig;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
interface DeleteManyMutationContext {
|
|
527
|
-
_svadmin_ctx?: boolean;
|
|
528
|
-
userContext?: unknown;
|
|
529
|
-
previousQueries?: [readonly unknown[], unknown][];
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
export function useDeleteMany<TData extends BaseRecord = BaseRecord, TError = HttpError>(options: UseDeleteManyOptions = {}) {
|
|
533
|
-
const adminContext = captureAdminContext();
|
|
534
|
-
const parsed = useParsed();
|
|
535
|
-
const resource = options.resource ?? parsed.resource ?? '';
|
|
536
|
-
const adminOptions = getAdminOptions();
|
|
537
|
-
const queryClient = useQueryClient();
|
|
538
|
-
const i18n = useTranslation();
|
|
539
|
-
const mutationMode = options.mutationMode ?? adminOptions.mutationMode ?? 'pessimistic';
|
|
540
|
-
const undoableTimeout = options.undoableTimeout ?? adminOptions.undoableTimeout ?? 5000;
|
|
541
|
-
|
|
542
|
-
const removeIdsFromCache = (
|
|
543
|
-
resName: string,
|
|
544
|
-
ids: (string | number)[],
|
|
545
|
-
dataProviderName?: string,
|
|
546
|
-
) => {
|
|
547
|
-
const matcher = adminContext.queryKeyMatcher(resName, dataProviderName);
|
|
548
|
-
const dataMatch = (queryKey: readonly unknown[], fields: QueryMatcher = {}) => dataQueryMatches(queryKey, {
|
|
549
|
-
...matcher,
|
|
550
|
-
resource: resName,
|
|
551
|
-
...fields,
|
|
552
|
-
});
|
|
553
|
-
const primaryKey = adminContext.getResource(resName).primaryKey ?? 'id';
|
|
554
|
-
const deletedIds = new Set(ids.map(String));
|
|
555
|
-
const removeFromCollection = (old: unknown): unknown => {
|
|
556
|
-
if (!old || typeof old !== 'object' || !('data' in old)) return old;
|
|
557
|
-
const current = old as { data: Record<string, unknown>[]; total?: number };
|
|
558
|
-
if (!Array.isArray(current.data)) return old;
|
|
559
|
-
const filtered = current.data.filter((item) => !deletedIds.has(String(item[primaryKey])));
|
|
560
|
-
const removedCount = current.data.length - filtered.length;
|
|
561
|
-
return {
|
|
562
|
-
...current,
|
|
563
|
-
data: filtered,
|
|
564
|
-
total: current.total == null ? current.total : Math.max(0, current.total - removedCount),
|
|
565
|
-
};
|
|
566
|
-
};
|
|
567
|
-
const removeFromInfiniteCollection = (old: unknown): unknown => {
|
|
568
|
-
if (!old || typeof old !== 'object' || !('pages' in old)) return old;
|
|
569
|
-
const current = old as {
|
|
570
|
-
pages: { data: Record<string, unknown>[]; total?: number }[];
|
|
571
|
-
pageParams?: unknown[];
|
|
572
|
-
};
|
|
573
|
-
if (!Array.isArray(current.pages)) return old;
|
|
574
|
-
|
|
575
|
-
const removedIds = new Set<string>();
|
|
576
|
-
for (const page of current.pages) {
|
|
577
|
-
if (!Array.isArray(page.data)) continue;
|
|
578
|
-
for (const item of page.data) {
|
|
579
|
-
const itemId = String(item[primaryKey]);
|
|
580
|
-
if (deletedIds.has(itemId)) removedIds.add(itemId);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
return {
|
|
585
|
-
...current,
|
|
586
|
-
pages: current.pages.map((page) => ({
|
|
587
|
-
...page,
|
|
588
|
-
data: Array.isArray(page.data)
|
|
589
|
-
? page.data.filter((item) => !deletedIds.has(String(item[primaryKey])))
|
|
590
|
-
: page.data,
|
|
591
|
-
total: page.total == null ? page.total : Math.max(0, page.total - removedIds.size),
|
|
592
|
-
})),
|
|
593
|
-
};
|
|
594
|
-
};
|
|
595
|
-
|
|
596
|
-
queryClient.setQueriesData({
|
|
597
|
-
predicate: (query) => ['list', 'select', 'selectDefaults'].some((action) => dataMatch(query.queryKey, { action })),
|
|
598
|
-
}, removeFromCollection);
|
|
599
|
-
queryClient.setQueriesData({
|
|
600
|
-
predicate: (query) => dataMatch(query.queryKey, { action: 'infiniteList' }),
|
|
601
|
-
}, removeFromInfiniteCollection);
|
|
602
|
-
queryClient.setQueriesData({
|
|
603
|
-
predicate: (query) => dataMatch(query.queryKey, { action: 'many' }),
|
|
604
|
-
}, removeFromCollection);
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
const mutation = createMutation<{ data: TData[] }, TError | DeleteManyPartialError, UseDeleteManyMutateParams, DeleteManyMutationContext>(() => ({
|
|
608
|
-
mutationFn: async (params) => {
|
|
609
|
-
const resName = params.resource ?? resource;
|
|
610
|
-
const effectiveMutationMode = params.mutationMode ?? mutationMode;
|
|
611
|
-
const effectiveUndoableTimeout = params.undoableTimeout ?? undoableTimeout;
|
|
612
|
-
if (effectiveMutationMode === 'undoable') {
|
|
613
|
-
await new Promise<void>((resolve, reject) => {
|
|
614
|
-
toast.undoable(i18n.t('common.actionCanBeUndone'), effectiveUndoableTimeout, () => {
|
|
615
|
-
params.onCancel?.();
|
|
616
|
-
reject(new UndoError());
|
|
617
|
-
}, resolve);
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
const provider = adminContext.getDataProviderForResource(resName, params.dataProviderName);
|
|
621
|
-
if (provider.deleteMany) return await provider.deleteMany<TData>({ resource: resName, ids: params.ids, meta: params.meta });
|
|
622
|
-
const settled = await Promise.allSettled(
|
|
623
|
-
params.ids.map(id => provider.deleteOne<TData>({ resource: resName, id, meta: params.meta })),
|
|
624
|
-
);
|
|
625
|
-
const succeededIds: (string | number)[] = [];
|
|
626
|
-
const failedIds: (string | number)[] = [];
|
|
627
|
-
const causes: unknown[] = [];
|
|
628
|
-
const data: TData[] = [];
|
|
629
|
-
settled.forEach((result, index) => {
|
|
630
|
-
const id = params.ids[index];
|
|
631
|
-
if (result.status === 'fulfilled') {
|
|
632
|
-
succeededIds.push(id);
|
|
633
|
-
data.push(result.value.data);
|
|
634
|
-
} else {
|
|
635
|
-
failedIds.push(id);
|
|
636
|
-
causes.push(result.reason);
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
if (failedIds.length > 0) {
|
|
640
|
-
if (succeededIds.length > 0) {
|
|
641
|
-
throw new DeleteManyPartialError(succeededIds, failedIds, causes);
|
|
642
|
-
}
|
|
643
|
-
throw causes[0] ?? new Error('Delete many failed');
|
|
644
|
-
}
|
|
645
|
-
return { data };
|
|
646
|
-
},
|
|
647
|
-
onMutate: async (params) => {
|
|
648
|
-
const userContext = typeof options.mutationOptions?.onMutate === 'function'
|
|
649
|
-
? await options.mutationOptions.onMutate(params)
|
|
650
|
-
: undefined;
|
|
651
|
-
const effectiveMutationMode = params.mutationMode ?? mutationMode;
|
|
652
|
-
if (effectiveMutationMode === 'pessimistic') return { _svadmin_ctx: true, userContext };
|
|
653
|
-
const resName = params.resource ?? resource;
|
|
654
|
-
const matcher = adminContext.queryKeyMatcher(resName, params.dataProviderName);
|
|
655
|
-
const dataMatch = (queryKey: readonly unknown[], fields: QueryMatcher = {}) => dataQueryMatches(queryKey, {
|
|
656
|
-
...matcher,
|
|
657
|
-
resource: resName,
|
|
658
|
-
...fields,
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
await queryClient.cancelQueries({ predicate: (query) => dataMatch(query.queryKey) });
|
|
662
|
-
const previousQueries = queryClient.getQueriesData({ predicate: (query) => dataMatch(query.queryKey) });
|
|
663
|
-
removeIdsFromCache(resName, params.ids, params.dataProviderName);
|
|
664
|
-
|
|
665
|
-
return { _svadmin_ctx: true, userContext, previousQueries };
|
|
666
|
-
},
|
|
667
|
-
onSuccess: (data, params, context) => {
|
|
668
|
-
const resName = params.resource ?? resource;
|
|
669
|
-
const extractedContext = context?._svadmin_ctx ? context.userContext : context;
|
|
670
|
-
fireSuccessNotification({
|
|
671
|
-
config: params.successNotification ?? options.successNotification,
|
|
672
|
-
defaultMessage: 'Deleted successfully',
|
|
673
|
-
data: data.data,
|
|
674
|
-
resource: resName,
|
|
675
|
-
provider: adminContext.notificationProvider,
|
|
676
|
-
});
|
|
677
|
-
auditWithProvider(
|
|
678
|
-
{ action: 'delete', resource: resName, meta: adminContext.getProviderMeta(resName) },
|
|
679
|
-
adminContext.auditLogProvider,
|
|
680
|
-
);
|
|
681
|
-
publishLiveEvent(resName, 'deleted', params.ids, adminContext);
|
|
682
|
-
for (const id of params.ids) {
|
|
683
|
-
queryClient.removeQueries({
|
|
684
|
-
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
685
|
-
...adminContext.queryKeyMatcher(resName, params.dataProviderName),
|
|
686
|
-
kind: 'data',
|
|
687
|
-
resource: resName,
|
|
688
|
-
action: 'one',
|
|
689
|
-
id,
|
|
690
|
-
}),
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
if (typeof options.mutationOptions?.onSuccess === 'function') {
|
|
694
|
-
options.mutationOptions.onSuccess(data, params, extractedContext);
|
|
695
|
-
}
|
|
696
|
-
},
|
|
697
|
-
onError: (error, params, context) => {
|
|
698
|
-
if (context?.previousQueries) {
|
|
699
|
-
for (const [queryKey, data] of context.previousQueries) queryClient.setQueryData(queryKey, data);
|
|
700
|
-
}
|
|
701
|
-
if (error instanceof UndoError) return;
|
|
702
|
-
const resName = params.resource ?? resource;
|
|
703
|
-
if (error instanceof DeleteManyPartialError) {
|
|
704
|
-
removeIdsFromCache(resName, error.succeededIds, params.dataProviderName);
|
|
705
|
-
for (const id of error.succeededIds) {
|
|
706
|
-
queryClient.removeQueries({
|
|
707
|
-
predicate: (query) => queryKeyMatches(query.queryKey, {
|
|
708
|
-
...adminContext.queryKeyMatcher(resName, params.dataProviderName),
|
|
709
|
-
kind: 'data',
|
|
710
|
-
resource: resName,
|
|
711
|
-
action: 'one',
|
|
712
|
-
id,
|
|
713
|
-
}),
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
auditWithProvider(
|
|
717
|
-
{
|
|
718
|
-
action: 'delete',
|
|
719
|
-
resource: resName,
|
|
720
|
-
outcome: 'failure',
|
|
721
|
-
details: { succeededIds: error.succeededIds, failedIds: error.failedIds },
|
|
722
|
-
meta: adminContext.getProviderMeta(resName),
|
|
723
|
-
},
|
|
724
|
-
adminContext.auditLogProvider,
|
|
725
|
-
);
|
|
726
|
-
publishLiveEvent(resName, 'deleted', error.succeededIds, adminContext);
|
|
727
|
-
}
|
|
728
|
-
const authError = error instanceof DeleteManyPartialError
|
|
729
|
-
? (error.causes[0] ?? error)
|
|
730
|
-
: error;
|
|
731
|
-
checkError(authError, adminContext);
|
|
732
|
-
fireErrorNotification({
|
|
733
|
-
config: params.errorNotification ?? options.errorNotification,
|
|
734
|
-
defaultMessage: 'Delete many failed',
|
|
735
|
-
error,
|
|
736
|
-
resource: resName,
|
|
737
|
-
provider: adminContext.notificationProvider,
|
|
738
|
-
});
|
|
739
|
-
const extractedContext = context?._svadmin_ctx ? context.userContext : context;
|
|
740
|
-
if (typeof options.mutationOptions?.onError === 'function') {
|
|
741
|
-
options.mutationOptions.onError(error, params, extractedContext);
|
|
742
|
-
}
|
|
743
|
-
},
|
|
744
|
-
onSettled: (_data, error, params, context) => {
|
|
745
|
-
if (error instanceof UndoError) return;
|
|
746
|
-
const resName = params.resource ?? resource;
|
|
747
|
-
invalidateByScopes({
|
|
748
|
-
queryClient,
|
|
749
|
-
resource: resName,
|
|
750
|
-
scopes: params.invalidates,
|
|
751
|
-
defaults: ['list', 'many'],
|
|
752
|
-
matcher: adminContext.queryKeyMatcher(resName, params.dataProviderName),
|
|
753
|
-
});
|
|
754
|
-
const extractedContext = context?._svadmin_ctx ? context.userContext : context;
|
|
755
|
-
if (typeof options.mutationOptions?.onSettled === 'function') {
|
|
756
|
-
options.mutationOptions.onSettled(_data, error, params, extractedContext);
|
|
757
|
-
}
|
|
758
|
-
},
|
|
759
|
-
}));
|
|
760
|
-
|
|
761
|
-
return { mutation, get overtime() { return createOvertimeTracker(() => mutation.isPending, options.overtimeOptions ?? adminOptions.overtime); } };
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// ─── useInvalidate ──────────────────────────────────────────────────
|
|
765
|
-
|
|
766
|
-
export function useInvalidate() {
|
|
767
|
-
const queryClient = useQueryClient();
|
|
768
|
-
const adminContext = captureAdminContext();
|
|
769
|
-
return (params: { resource?: string; invalidates?: string[] | 'all' | false; id?: string | number; dataProviderName?: string }) => {
|
|
770
|
-
if (params.invalidates === false) return;
|
|
771
|
-
if (params.invalidates === 'all' || !params.resource) {
|
|
772
|
-
queryClient.invalidateQueries({
|
|
773
|
-
predicate: (q) => queryKeyMatches(q.queryKey, {
|
|
774
|
-
...adminContext.queryKeyMatcher(undefined, params.dataProviderName),
|
|
775
|
-
kind: 'data',
|
|
776
|
-
}),
|
|
777
|
-
});
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
const res = params.resource;
|
|
781
|
-
invalidateByScopes({
|
|
782
|
-
queryClient,
|
|
783
|
-
resource: res,
|
|
784
|
-
scopes: params.invalidates || ['resourceAll'],
|
|
785
|
-
defaults: ['resourceAll'],
|
|
786
|
-
id: params.id,
|
|
787
|
-
matcher: adminContext.queryKeyMatcher(res, params.dataProviderName),
|
|
788
|
-
});
|
|
789
|
-
};
|
|
790
|
-
}
|
|
32
|
+
export { createSelectQuery } from './select-query.svelte';
|
|
33
|
+
export type { UseSelectOptions } from './select-query.svelte';
|
|
791
34
|
|
|
792
35
|
// ─── useOvertime ────────────────────────────────────────────────────
|
|
793
36
|
|
|
794
37
|
export function useOvertime(options?: OvertimeOptions) {
|
|
795
|
-
let isLoading
|
|
796
|
-
const overtime
|
|
38
|
+
let isLoading=$state(false);
|
|
39
|
+
const overtime=createOvertimeTracker(() => isLoading,options);
|
|
797
40
|
return {
|
|
798
41
|
get elapsedTime() { return overtime.elapsedTime; },
|
|
799
|
-
start() { isLoading
|
|
800
|
-
stop() { isLoading
|
|
42
|
+
start() { isLoading=true; },
|
|
43
|
+
stop() { isLoading=false; },
|
|
801
44
|
};
|
|
802
45
|
}
|
|
803
46
|
|
|
804
47
|
// ─── useDataProvider ────────────────────────────────────────────────
|
|
805
48
|
|
|
806
49
|
export function useDataProvider(): (dataProviderName?: string) => DataProvider {
|
|
807
|
-
const adminContext
|
|
50
|
+
const adminContext=captureAdminContext();
|
|
808
51
|
return (name?: string) => adminContext.getDataProvider(name);
|
|
809
52
|
}
|
|
810
53
|
|
|
811
54
|
// ─── useThemedLayoutContext ─────────────────────────────────────────
|
|
812
55
|
|
|
813
|
-
let _sidebarCollapsed
|
|
56
|
+
let _sidebarCollapsed=$state(false);
|
|
814
57
|
|
|
815
|
-
export function resetSidebarCollapsed() { _sidebarCollapsed
|
|
58
|
+
export function resetSidebarCollapsed() { _sidebarCollapsed=false; }
|
|
816
59
|
|
|
817
60
|
export function useThemedLayoutContext() {
|
|
818
61
|
return {
|
|
819
62
|
get sidebarCollapsed() { return _sidebarCollapsed; },
|
|
820
|
-
setSidebarCollapsed(v: boolean) { _sidebarCollapsed
|
|
821
|
-
toggleSidebar() { _sidebarCollapsed
|
|
63
|
+
setSidebarCollapsed(v: boolean) { _sidebarCollapsed=v; },
|
|
64
|
+
toggleSidebar() { _sidebarCollapsed=!_sidebarCollapsed; },
|
|
822
65
|
};
|
|
823
66
|
}
|