@questpie/admin 3.1.0 → 3.2.1
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/client/blocks/block-renderer.d.mts +12 -2
- package/dist/client/blocks/block-renderer.mjs +357 -49
- package/dist/client/builder/page/page.d.mts +29 -1
- package/dist/client/components/blocks/block-editor-context.mjs +11 -1
- package/dist/client/components/blocks/block-editor-provider.mjs +68 -26
- package/dist/client/components/blocks/block-item.mjs +181 -170
- package/dist/client/components/blocks/utils/tree-utils.mjs +13 -1
- package/dist/client/components/fields/array-field.mjs +177 -118
- package/dist/client/components/filter-builder/filter-builder-sheet.mjs +305 -310
- package/dist/client/components/filter-builder/filters-tab.mjs +1 -1
- package/dist/client/components/history-sidebar.mjs +121 -114
- package/dist/client/components/preview/live-preview-mode.mjs +140 -114
- package/dist/client/components/preview/preview-pane.mjs +288 -333
- package/dist/client/components/primitives/option-label.mjs +44 -0
- package/dist/client/components/primitives/select-multi.mjs +408 -383
- package/dist/client/components/primitives/select-single.mjs +387 -357
- package/dist/client/components/widgets/chart-widget.mjs +168 -143
- package/dist/client/contexts/focus-context.d.mts +11 -0
- package/dist/client/contexts/focus-context.mjs +51 -34
- package/dist/client/hooks/use-audit-history.mjs +10 -17
- package/dist/client/hooks/use-brand.mjs +2 -1
- package/dist/client/hooks/use-transition-stage.mjs +34 -41
- package/dist/client/preview/block-scope-context.d.mts +2 -2
- package/dist/client/preview/block-scope-context.mjs +10 -20
- package/dist/client/preview/index.d.mts +1 -1
- package/dist/client/preview/patch.mjs +100 -0
- package/dist/client/preview/preview-banner.d.mts +2 -2
- package/dist/client/preview/preview-field.d.mts +38 -9
- package/dist/client/preview/preview-field.mjs +385 -118
- package/dist/client/preview/types.d.mts +82 -3
- package/dist/client/preview/types.mjs +85 -6
- package/dist/client/preview/use-collection-preview.d.mts +19 -1
- package/dist/client/preview/use-collection-preview.mjs +182 -58
- package/dist/client/runtime/index.d.mts +2 -2
- package/dist/client/runtime/index.mjs +2 -2
- package/dist/client/runtime/provider.d.mts +5 -5
- package/dist/client/scope/picker.d.mts +2 -2
- package/dist/client/scope/provider.d.mts +2 -2
- package/dist/client/utils/build-field-definitions-from-schema.mjs +8 -3
- package/dist/client/views/auth/login-form.d.mts +2 -2
- package/dist/client/views/auth/reset-password-form.d.mts +2 -2
- package/dist/client/views/collection/bulk-action-toolbar.mjs +23 -25
- package/dist/client/views/collection/cells/primitive-cells.mjs +63 -13
- package/dist/client/views/collection/columns/build-columns.mjs +1 -0
- package/dist/client/views/collection/form-view.mjs +262 -33
- package/dist/client/views/collection/table-view.mjs +16 -11
- package/dist/client/views/layout/admin-layout-provider.d.mts +5 -5
- package/dist/client/views/layout/admin-layout-provider.mjs +107 -16
- package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
- package/dist/client/views/pages/dashboard-page.d.mts +2 -2
- package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
- package/dist/client/views/pages/reset-password-page.d.mts +2 -2
- package/dist/client/views/pages/setup-page.d.mts +2 -2
- package/dist/client.d.mts +3 -2
- package/dist/client.mjs +3 -2
- package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +3 -2
- package/dist/server/augmentation/common.d.mts +8 -4
- package/dist/server/augmentation/form-layout.d.mts +1 -1
- package/dist/server/i18n/messages/cs.mjs +11 -0
- package/dist/server/i18n/messages/de.mjs +11 -0
- package/dist/server/i18n/messages/en.mjs +11 -0
- package/dist/server/i18n/messages/es.mjs +11 -0
- package/dist/server/i18n/messages/fr.mjs +11 -0
- package/dist/server/i18n/messages/pl.mjs +11 -0
- package/dist/server/i18n/messages/pt.mjs +11 -0
- package/dist/server/i18n/messages/sk.mjs +11 -0
- package/dist/server/modules/admin/block/block-builder.d.mts +7 -10
- package/dist/server/modules/admin/block/block-builder.mjs +7 -10
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +49 -49
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
- package/dist/server/modules/admin/collections/apikey.d.mts +64 -64
- package/dist/server/modules/admin/collections/assets.d.mts +57 -20
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +100 -34
- package/dist/server/modules/admin/collections/user.mjs +4 -4
- package/dist/server/modules/admin/collections/verification.d.mts +32 -32
- package/dist/server/modules/admin/index.d.mts +3 -3
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
- package/dist/server/modules/admin/routes/admin-config.mjs +9 -12
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +11 -11
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/setup.d.mts +7 -7
- package/dist/server/modules/admin/routes/translations.d.mts +4 -4
- package/dist/server/modules/admin/routes/translations.mjs +1 -1
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin-preferences/collections/admin-preferences.mjs +4 -6
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -29
- package/dist/server/modules/admin-preferences/collections/saved-views.mjs +4 -6
- package/package.json +4 -4
|
@@ -22,6 +22,13 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
22
22
|
*
|
|
23
23
|
* Combines both functionalities into a single non-jumping UI element.
|
|
24
24
|
*/
|
|
25
|
+
function getSelectedIds(items) {
|
|
26
|
+
return items.flatMap((item) => {
|
|
27
|
+
if (typeof item !== "object" || item === null || !("id" in item)) return [];
|
|
28
|
+
const id = item.id;
|
|
29
|
+
return id == null ? [] : [String(id)];
|
|
30
|
+
});
|
|
31
|
+
}
|
|
25
32
|
/**
|
|
26
33
|
* BulkActionToolbar - Shows selection count and bulk actions
|
|
27
34
|
*
|
|
@@ -142,12 +149,12 @@ function BulkActionToolbar(t0) {
|
|
|
142
149
|
const { handler } = action_0;
|
|
143
150
|
if (action_0.id === "deleteMany" && onBulkDelete) {
|
|
144
151
|
setIsLoading(true);
|
|
145
|
-
const ids = selectedItems
|
|
152
|
+
const ids = getSelectedIds(selectedItems);
|
|
146
153
|
try {
|
|
147
154
|
await onBulkDelete(ids);
|
|
148
155
|
table.resetRowSelection();
|
|
149
156
|
setIsLoading(false);
|
|
150
|
-
} catch
|
|
157
|
+
} catch {
|
|
151
158
|
helpers.toast.error(t("collection.bulkDeleteError"));
|
|
152
159
|
setIsLoading(false);
|
|
153
160
|
}
|
|
@@ -155,32 +162,32 @@ function BulkActionToolbar(t0) {
|
|
|
155
162
|
}
|
|
156
163
|
if (action_0.id === "restoreMany" && onBulkRestore) {
|
|
157
164
|
setIsLoading(true);
|
|
158
|
-
const ids_0 = selectedItems
|
|
165
|
+
const ids_0 = getSelectedIds(selectedItems);
|
|
159
166
|
try {
|
|
160
167
|
await onBulkRestore(ids_0);
|
|
161
168
|
table.resetRowSelection();
|
|
162
169
|
setIsLoading(false);
|
|
163
|
-
} catch
|
|
170
|
+
} catch {
|
|
164
171
|
helpers.toast.error(t("collection.bulkRestoreError"));
|
|
165
172
|
setIsLoading(false);
|
|
166
173
|
}
|
|
167
174
|
return;
|
|
168
175
|
}
|
|
169
|
-
|
|
176
|
+
bb86: switch (handler.type) {
|
|
170
177
|
case "api": {
|
|
171
178
|
setIsLoading(true);
|
|
172
|
-
const ids_1 = selectedItems
|
|
179
|
+
const ids_1 = getSelectedIds(selectedItems);
|
|
173
180
|
const method = handler.method ? handler.method : "POST";
|
|
174
181
|
try {
|
|
175
182
|
helpers.toast.info(`Bulk API call: ${method} ${handler.endpoint} (${ids_1.length} items)`);
|
|
176
183
|
helpers.refresh();
|
|
177
184
|
table.resetRowSelection();
|
|
178
185
|
setIsLoading(false);
|
|
179
|
-
} catch
|
|
186
|
+
} catch {
|
|
180
187
|
helpers.toast.error(t("collection.bulkActionFailed"));
|
|
181
188
|
setIsLoading(false);
|
|
182
189
|
}
|
|
183
|
-
break
|
|
190
|
+
break bb86;
|
|
184
191
|
}
|
|
185
192
|
case "custom":
|
|
186
193
|
setIsLoading(true);
|
|
@@ -188,18 +195,18 @@ function BulkActionToolbar(t0) {
|
|
|
188
195
|
await handler.fn(ctx);
|
|
189
196
|
table.resetRowSelection();
|
|
190
197
|
setIsLoading(false);
|
|
191
|
-
} catch
|
|
198
|
+
} catch {
|
|
192
199
|
helpers.toast.error(t("collection.bulkActionFailed"));
|
|
193
200
|
setIsLoading(false);
|
|
194
201
|
}
|
|
195
|
-
break
|
|
202
|
+
break bb86;
|
|
196
203
|
case "dialog":
|
|
197
204
|
case "form":
|
|
198
205
|
onOpenDialog?.(action_0, selectedItems);
|
|
199
|
-
break
|
|
206
|
+
break bb86;
|
|
200
207
|
case "navigate":
|
|
201
208
|
helpers.toast.error("Navigate action not supported for bulk operations");
|
|
202
|
-
break
|
|
209
|
+
break bb86;
|
|
203
210
|
case "server": onOpenDialog?.(action_0, selectedItems);
|
|
204
211
|
}
|
|
205
212
|
};
|
|
@@ -272,8 +279,8 @@ function BulkActionToolbar(t0) {
|
|
|
272
279
|
let t17;
|
|
273
280
|
let t18;
|
|
274
281
|
if ($[38] !== filterCount || $[39] !== handleActionClick || $[40] !== handleSelectAllMatching || $[41] !== hasFilters || $[42] !== hasSelection || $[43] !== isDisabled || $[44] !== isLoading || $[45] !== isSelectingAll || $[46] !== isVisible || $[47] !== onClearFilters || $[48] !== onOpenFilters || $[49] !== onSelectAllMatching || $[50] !== pageCount || $[51] !== resolveText || $[52] !== selectedCount || $[53] !== t || $[54] !== table || $[55] !== totalCount || $[56] !== visibleActions) {
|
|
275
|
-
const regularActions = visibleActions.filter(
|
|
276
|
-
const destructiveActions = visibleActions.filter(
|
|
282
|
+
const regularActions = visibleActions.filter(_temp2);
|
|
283
|
+
const destructiveActions = visibleActions.filter(_temp3);
|
|
277
284
|
t17 = isVisible ? "open" : "closed";
|
|
278
285
|
t18 = "qa-bulk-toolbar fixed bottom-6 left-1/2 z-50 max-w-[calc(100%-2rem)] -translate-x-1/2 transition-[opacity,translate,scale] duration-[var(--motion-duration-slow)] ease-[var(--motion-ease-enter)] data-[state=closed]:pointer-events-none data-[state=closed]:translate-y-2 data-[state=closed]:scale-[0.98] data-[state=closed]:opacity-0 data-[state=open]:translate-y-0 data-[state=open]:scale-100 data-[state=open]:opacity-100 motion-reduce:transition-none sm:max-w-none";
|
|
279
286
|
t14 = "qa-bulk-toolbar__bar bg-background border-border flex items-center gap-2 overflow-x-auto rounded-full border px-3 py-2 shadow-lg transition-[gap,padding] duration-[var(--motion-duration-base)] ease-[var(--motion-ease-standard)] sm:gap-3 sm:px-4 sm:py-2.5";
|
|
@@ -492,21 +499,12 @@ function BulkActionToolbar(t0) {
|
|
|
492
499
|
} else t22 = $[85];
|
|
493
500
|
return t22;
|
|
494
501
|
}
|
|
495
|
-
function
|
|
502
|
+
function _temp3(a_0) {
|
|
496
503
|
return a_0.variant === "destructive";
|
|
497
504
|
}
|
|
498
|
-
function
|
|
505
|
+
function _temp2(a) {
|
|
499
506
|
return a.variant !== "destructive";
|
|
500
507
|
}
|
|
501
|
-
function _temp4(item_1) {
|
|
502
|
-
if (item_1 != null) return item_1.id;
|
|
503
|
-
}
|
|
504
|
-
function _temp3(item_0) {
|
|
505
|
-
if (item_0 != null) return item_0.id;
|
|
506
|
-
}
|
|
507
|
-
function _temp2(item) {
|
|
508
|
-
if (item != null) return item.id;
|
|
509
|
-
}
|
|
510
508
|
function _temp(row) {
|
|
511
509
|
return row.original;
|
|
512
510
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useTranslation } from "../../../i18n/hooks.mjs";
|
|
1
|
+
import { useResolveText, useTranslation } from "../../../i18n/hooks.mjs";
|
|
2
|
+
import { resolveOptionLabelForValue } from "../../../components/primitives/option-label.mjs";
|
|
2
3
|
import { Badge } from "../../../components/ui/badge.mjs";
|
|
3
4
|
import { c } from "react/compiler-runtime";
|
|
4
5
|
import "react";
|
|
@@ -364,10 +365,25 @@ function EmailCell(t0) {
|
|
|
364
365
|
/**
|
|
365
366
|
* Select/Status cell - badge display
|
|
366
367
|
*/
|
|
368
|
+
function getSelectOptions(fieldDef) {
|
|
369
|
+
const options = fieldDef?.["~options"]?.options;
|
|
370
|
+
return Array.isArray(options) ? options : void 0;
|
|
371
|
+
}
|
|
372
|
+
function resolveSelectCellLabel({ value, fieldDef, resolveText, t, locale }) {
|
|
373
|
+
return resolveOptionLabelForValue({
|
|
374
|
+
value,
|
|
375
|
+
options: getSelectOptions(fieldDef),
|
|
376
|
+
resolveText,
|
|
377
|
+
t,
|
|
378
|
+
locale
|
|
379
|
+
});
|
|
380
|
+
}
|
|
367
381
|
function SelectCell(t0) {
|
|
368
|
-
const $ = c(
|
|
369
|
-
const { value } = t0;
|
|
370
|
-
|
|
382
|
+
const $ = c(16);
|
|
383
|
+
const { value, fieldDef } = t0;
|
|
384
|
+
const { t, locale } = useTranslation();
|
|
385
|
+
const resolveText = useResolveText();
|
|
386
|
+
if (value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0) {
|
|
371
387
|
let t1$1;
|
|
372
388
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
373
389
|
t1$1 = /* @__PURE__ */ jsx("span", {
|
|
@@ -378,17 +394,51 @@ function SelectCell(t0) {
|
|
|
378
394
|
} else t1$1 = $[0];
|
|
379
395
|
return t1$1;
|
|
380
396
|
}
|
|
381
|
-
|
|
397
|
+
let t1;
|
|
398
|
+
if ($[1] !== value) {
|
|
399
|
+
t1 = Array.isArray(value) ? value : [value];
|
|
400
|
+
$[1] = value;
|
|
401
|
+
$[2] = t1;
|
|
402
|
+
} else t1 = $[2];
|
|
403
|
+
const values = t1;
|
|
382
404
|
let t2;
|
|
383
|
-
if ($[
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
405
|
+
if ($[3] !== fieldDef || $[4] !== locale || $[5] !== resolveText || $[6] !== t || $[7] !== values) {
|
|
406
|
+
let t3$1;
|
|
407
|
+
if ($[9] !== fieldDef || $[10] !== locale || $[11] !== resolveText || $[12] !== t) {
|
|
408
|
+
t3$1 = (item, index) => /* @__PURE__ */ jsx(Badge, {
|
|
409
|
+
variant: "outline",
|
|
410
|
+
children: resolveSelectCellLabel({
|
|
411
|
+
value: item,
|
|
412
|
+
fieldDef,
|
|
413
|
+
resolveText,
|
|
414
|
+
t,
|
|
415
|
+
locale
|
|
416
|
+
})
|
|
417
|
+
}, `${String(item)}-${index}`);
|
|
418
|
+
$[9] = fieldDef;
|
|
419
|
+
$[10] = locale;
|
|
420
|
+
$[11] = resolveText;
|
|
421
|
+
$[12] = t;
|
|
422
|
+
$[13] = t3$1;
|
|
423
|
+
} else t3$1 = $[13];
|
|
424
|
+
t2 = values.map(t3$1);
|
|
425
|
+
$[3] = fieldDef;
|
|
426
|
+
$[4] = locale;
|
|
427
|
+
$[5] = resolveText;
|
|
428
|
+
$[6] = t;
|
|
429
|
+
$[7] = values;
|
|
430
|
+
$[8] = t2;
|
|
431
|
+
} else t2 = $[8];
|
|
432
|
+
let t3;
|
|
433
|
+
if ($[14] !== t2) {
|
|
434
|
+
t3 = /* @__PURE__ */ jsx("span", {
|
|
435
|
+
className: "inline-flex max-w-[300px] flex-wrap gap-1",
|
|
436
|
+
children: t2
|
|
387
437
|
});
|
|
388
|
-
$[
|
|
389
|
-
$[
|
|
390
|
-
} else
|
|
391
|
-
return
|
|
438
|
+
$[14] = t2;
|
|
439
|
+
$[15] = t3;
|
|
440
|
+
} else t3 = $[15];
|
|
441
|
+
return t3;
|
|
392
442
|
}
|
|
393
443
|
|
|
394
444
|
//#endregion
|
|
@@ -10,6 +10,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSepara
|
|
|
10
10
|
import { LocaleSwitcher } from "../../components/locale-switcher.mjs";
|
|
11
11
|
import { Label } from "../../components/ui/label.mjs";
|
|
12
12
|
import { RenderProfiler } from "../../lib/render-profiler.mjs";
|
|
13
|
+
import { scrollFieldIntoView } from "../../contexts/focus-context.mjs";
|
|
13
14
|
import { Checkbox } from "../../components/ui/checkbox.mjs";
|
|
14
15
|
import { DateTimeInput } from "../../components/primitives/date-input.mjs";
|
|
15
16
|
import { getDefaultFormActions } from "../../builder/types/action-registry.mjs";
|
|
@@ -63,6 +64,139 @@ function extractReactiveConfigs(schema) {
|
|
|
63
64
|
for (const [fieldName, fieldDef] of Object.entries(schema.fields)) if (fieldDef.reactive) configs[fieldName] = fieldDef.reactive;
|
|
64
65
|
return configs;
|
|
65
66
|
}
|
|
67
|
+
function isPlainObject(value) {
|
|
68
|
+
if (!value || typeof value !== "object") return false;
|
|
69
|
+
const prototype = Object.getPrototypeOf(value);
|
|
70
|
+
return prototype === Object.prototype || prototype === null;
|
|
71
|
+
}
|
|
72
|
+
function clonePreviewSnapshot(value) {
|
|
73
|
+
if (typeof structuredClone === "function") try {
|
|
74
|
+
return structuredClone(value);
|
|
75
|
+
} catch {}
|
|
76
|
+
const json = JSON.stringify(value);
|
|
77
|
+
return json === void 0 ? value : JSON.parse(json);
|
|
78
|
+
}
|
|
79
|
+
function arePreviewValuesEqual(a, b) {
|
|
80
|
+
if (Object.is(a, b)) return true;
|
|
81
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
82
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
83
|
+
return a.every((value, index) => arePreviewValuesEqual(value, b[index]));
|
|
84
|
+
}
|
|
85
|
+
if (isPlainObject(a) || isPlainObject(b)) {
|
|
86
|
+
if (!isPlainObject(a) || !isPlainObject(b)) return false;
|
|
87
|
+
const aKeys = Object.keys(a);
|
|
88
|
+
const bKeys = Object.keys(b);
|
|
89
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
90
|
+
return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && arePreviewValuesEqual(a[key], b[key]));
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
function joinPreviewPath(basePath, key) {
|
|
95
|
+
return basePath ? `${basePath}.${key}` : key;
|
|
96
|
+
}
|
|
97
|
+
function diffPreviewSnapshots(previous, current, basePath = "") {
|
|
98
|
+
if (arePreviewValuesEqual(previous, current)) return [];
|
|
99
|
+
if (isPlainObject(previous) && isPlainObject(current)) {
|
|
100
|
+
const ops = [];
|
|
101
|
+
const keys = new Set([...Object.keys(previous), ...Object.keys(current)]);
|
|
102
|
+
for (const key of keys) {
|
|
103
|
+
const path = joinPreviewPath(basePath, key);
|
|
104
|
+
const previousHasKey = Object.prototype.hasOwnProperty.call(previous, key);
|
|
105
|
+
if (!Object.prototype.hasOwnProperty.call(current, key)) {
|
|
106
|
+
ops.push({
|
|
107
|
+
op: "remove",
|
|
108
|
+
path
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!previousHasKey) {
|
|
113
|
+
ops.push({
|
|
114
|
+
op: "set",
|
|
115
|
+
path,
|
|
116
|
+
value: current[key]
|
|
117
|
+
});
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
ops.push(...diffPreviewSnapshots(previous[key], current[key], path));
|
|
121
|
+
}
|
|
122
|
+
return ops;
|
|
123
|
+
}
|
|
124
|
+
if (!basePath) return [{
|
|
125
|
+
op: "set",
|
|
126
|
+
path: "",
|
|
127
|
+
value: current
|
|
128
|
+
}];
|
|
129
|
+
return [{
|
|
130
|
+
op: "set",
|
|
131
|
+
path: basePath,
|
|
132
|
+
value: current
|
|
133
|
+
}];
|
|
134
|
+
}
|
|
135
|
+
function isSafePreviewEditPath(path) {
|
|
136
|
+
if (!path || path.length > 512) return false;
|
|
137
|
+
return path.split(".").every((segment) => segment.length > 0 && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor");
|
|
138
|
+
}
|
|
139
|
+
function hasValueAtPath(value, path) {
|
|
140
|
+
let current = value;
|
|
141
|
+
for (const segment of path.split(".")) {
|
|
142
|
+
if (!current || typeof current !== "object") return false;
|
|
143
|
+
if (!Object.prototype.hasOwnProperty.call(current, segment)) return false;
|
|
144
|
+
current = current[segment];
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
function getTopLevelFieldSchema(schema, path) {
|
|
149
|
+
const topLevelField = path.split(".")[0];
|
|
150
|
+
if (!topLevelField) return void 0;
|
|
151
|
+
return schema?.fields?.[topLevelField];
|
|
152
|
+
}
|
|
153
|
+
function fieldSupportsInlineEdit(field, path, inputKind) {
|
|
154
|
+
const fieldType = field.metadata?.type;
|
|
155
|
+
const isNestedPath = path.includes(".");
|
|
156
|
+
if (fieldType === "object" || fieldType === "json") return isNestedPath;
|
|
157
|
+
switch (inputKind) {
|
|
158
|
+
case "text": return fieldType === "text" || fieldType === "email" || fieldType === "url" || fieldType === "select";
|
|
159
|
+
case "textarea": return fieldType === "textarea" || fieldType === "richText" || fieldType === "text";
|
|
160
|
+
case "number": return fieldType === "number";
|
|
161
|
+
case "boolean": return fieldType === "boolean";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function isAllowedPreviewEditPath({ message, schema, values }) {
|
|
165
|
+
if (!schema || !isSafePreviewEditPath(message.path)) return false;
|
|
166
|
+
if (message.fieldType === "relation") return false;
|
|
167
|
+
const blockMatch = message.path.match(/^([^.]+)\._values\.([^.]+)\.(.+)$/);
|
|
168
|
+
if (blockMatch) {
|
|
169
|
+
const [, blocksFieldName, blockId] = blockMatch;
|
|
170
|
+
if ((schema.fields?.[blocksFieldName])?.metadata?.type !== "blocks") return false;
|
|
171
|
+
if (message.blockId && message.blockId !== blockId) return false;
|
|
172
|
+
return hasValueAtPath(values, message.path);
|
|
173
|
+
}
|
|
174
|
+
const field = getTopLevelFieldSchema(schema, message.path);
|
|
175
|
+
if (!field) return false;
|
|
176
|
+
if (field.metadata?.readOnly || field.access?.update?.allowed === false) return false;
|
|
177
|
+
if (message.path.includes(".") && !hasValueAtPath(values, message.path)) return false;
|
|
178
|
+
return fieldSupportsInlineEdit(field, message.path, message.inputKind);
|
|
179
|
+
}
|
|
180
|
+
function normalizeInlineEditValue(message) {
|
|
181
|
+
switch (message.inputKind) {
|
|
182
|
+
case "text":
|
|
183
|
+
case "textarea": return typeof message.value === "string" ? {
|
|
184
|
+
ok: true,
|
|
185
|
+
value: message.value
|
|
186
|
+
} : { ok: false };
|
|
187
|
+
case "number": {
|
|
188
|
+
const value = typeof message.value === "number" ? message.value : typeof message.value === "string" ? Number(message.value) : NaN;
|
|
189
|
+
return Number.isFinite(value) ? {
|
|
190
|
+
ok: true,
|
|
191
|
+
value
|
|
192
|
+
} : { ok: false };
|
|
193
|
+
}
|
|
194
|
+
case "boolean": return typeof message.value === "boolean" ? {
|
|
195
|
+
ok: true,
|
|
196
|
+
value: message.value
|
|
197
|
+
} : { ok: false };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
66
200
|
/**
|
|
67
201
|
* Component that manages reactive field states.
|
|
68
202
|
* Must be rendered inside FormProvider to access form context.
|
|
@@ -174,13 +308,64 @@ const FormStateRefBridge = React.memo(function FormStateRefBridge$1(t0) {
|
|
|
174
308
|
React.useEffect(t4, t5);
|
|
175
309
|
return null;
|
|
176
310
|
});
|
|
311
|
+
const PreviewPatchBridge = React.memo(function PreviewPatchBridge$1(t0) {
|
|
312
|
+
const $ = c(5);
|
|
313
|
+
const { form, previewRef, enabled } = t0;
|
|
314
|
+
const previousSnapshotRef = React.useRef(null);
|
|
315
|
+
const snapshotVersionRef = React.useRef(void 0);
|
|
316
|
+
let t1;
|
|
317
|
+
let t2;
|
|
318
|
+
if ($[0] !== enabled || $[1] !== form || $[2] !== previewRef) {
|
|
319
|
+
t1 = () => {
|
|
320
|
+
if (!enabled) {
|
|
321
|
+
previousSnapshotRef.current = null;
|
|
322
|
+
snapshotVersionRef.current = void 0;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const initialSnapshot = clonePreviewSnapshot(form.getValues());
|
|
326
|
+
previousSnapshotRef.current = initialSnapshot;
|
|
327
|
+
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(initialSnapshot);
|
|
328
|
+
const subscription = form.watch((values) => {
|
|
329
|
+
const previousSnapshot = previousSnapshotRef.current;
|
|
330
|
+
const nextSnapshot = clonePreviewSnapshot(values);
|
|
331
|
+
if (!previousSnapshot) {
|
|
332
|
+
previousSnapshotRef.current = nextSnapshot;
|
|
333
|
+
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(nextSnapshot);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const ops = diffPreviewSnapshots(previousSnapshot, nextSnapshot).filter(_temp);
|
|
337
|
+
previousSnapshotRef.current = nextSnapshot;
|
|
338
|
+
if (ops.length === 0) return;
|
|
339
|
+
previewRef.current?.sendPatchBatch(ops, snapshotVersionRef.current);
|
|
340
|
+
});
|
|
341
|
+
return () => {
|
|
342
|
+
subscription.unsubscribe();
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
t2 = [
|
|
346
|
+
enabled,
|
|
347
|
+
form,
|
|
348
|
+
previewRef
|
|
349
|
+
];
|
|
350
|
+
$[0] = enabled;
|
|
351
|
+
$[1] = form;
|
|
352
|
+
$[2] = previewRef;
|
|
353
|
+
$[3] = t1;
|
|
354
|
+
$[4] = t2;
|
|
355
|
+
} else {
|
|
356
|
+
t1 = $[3];
|
|
357
|
+
t2 = $[4];
|
|
358
|
+
}
|
|
359
|
+
React.useEffect(t1, t2);
|
|
360
|
+
return null;
|
|
361
|
+
});
|
|
177
362
|
const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
|
|
178
|
-
const $ = c(
|
|
179
|
-
const { form, formElementRef, isEditMode, id, enabled, debounce, isDirtyRef, isSubmittingRef, updateMutation, onPreviewRefresh, onSavingChange, onSaved } = t0;
|
|
363
|
+
const $ = c(19);
|
|
364
|
+
const { form, formElementRef, isEditMode, id, enabled, debounce, isDirtyRef, isSubmittingRef, updateMutation, onPreviewRefresh, onPreviewCommit, onSavingChange, onSaved } = t0;
|
|
180
365
|
const { t } = useTranslation();
|
|
181
366
|
const timerRef = React.useRef(null);
|
|
182
367
|
let t1;
|
|
183
|
-
if ($[0] !== form || $[1] !== id || $[2] !== isDirtyRef || $[3] !== isSubmittingRef || $[4] !==
|
|
368
|
+
if ($[0] !== form || $[1] !== id || $[2] !== isDirtyRef || $[3] !== isSubmittingRef || $[4] !== onPreviewCommit || $[5] !== onPreviewRefresh || $[6] !== onSaved || $[7] !== onSavingChange || $[8] !== t || $[9] !== updateMutation) {
|
|
184
369
|
t1 = async () => {
|
|
185
370
|
if (!id || !isDirtyRef.current || isSubmittingRef.current) return;
|
|
186
371
|
try {
|
|
@@ -191,6 +376,7 @@ const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
|
|
|
191
376
|
data
|
|
192
377
|
});
|
|
193
378
|
form.reset(result, { keepTouched: true });
|
|
379
|
+
onPreviewCommit?.(result);
|
|
194
380
|
onPreviewRefresh?.();
|
|
195
381
|
onSaved(/* @__PURE__ */ new Date());
|
|
196
382
|
onSavingChange(false);
|
|
@@ -208,17 +394,18 @@ const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
|
|
|
208
394
|
$[1] = id;
|
|
209
395
|
$[2] = isDirtyRef;
|
|
210
396
|
$[3] = isSubmittingRef;
|
|
211
|
-
$[4] =
|
|
212
|
-
$[5] =
|
|
213
|
-
$[6] =
|
|
214
|
-
$[7] =
|
|
215
|
-
$[8] =
|
|
216
|
-
$[9] =
|
|
217
|
-
|
|
397
|
+
$[4] = onPreviewCommit;
|
|
398
|
+
$[5] = onPreviewRefresh;
|
|
399
|
+
$[6] = onSaved;
|
|
400
|
+
$[7] = onSavingChange;
|
|
401
|
+
$[8] = t;
|
|
402
|
+
$[9] = updateMutation;
|
|
403
|
+
$[10] = t1;
|
|
404
|
+
} else t1 = $[10];
|
|
218
405
|
const runAutosave = t1;
|
|
219
406
|
let t2;
|
|
220
407
|
let t3;
|
|
221
|
-
if ($[
|
|
408
|
+
if ($[11] !== debounce || $[12] !== enabled || $[13] !== formElementRef || $[14] !== id || $[15] !== isEditMode || $[16] !== runAutosave) {
|
|
222
409
|
t2 = () => {
|
|
223
410
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
224
411
|
if (!enabled || !isEditMode || !id) return;
|
|
@@ -246,17 +433,17 @@ const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
|
|
|
246
433
|
isEditMode,
|
|
247
434
|
runAutosave
|
|
248
435
|
];
|
|
249
|
-
$[
|
|
250
|
-
$[
|
|
251
|
-
$[
|
|
252
|
-
$[
|
|
253
|
-
$[
|
|
254
|
-
$[
|
|
255
|
-
$[
|
|
256
|
-
$[
|
|
436
|
+
$[11] = debounce;
|
|
437
|
+
$[12] = enabled;
|
|
438
|
+
$[13] = formElementRef;
|
|
439
|
+
$[14] = id;
|
|
440
|
+
$[15] = isEditMode;
|
|
441
|
+
$[16] = runAutosave;
|
|
442
|
+
$[17] = t2;
|
|
443
|
+
$[18] = t3;
|
|
257
444
|
} else {
|
|
258
|
-
t2 = $[
|
|
259
|
-
t3 = $[
|
|
445
|
+
t2 = $[17];
|
|
446
|
+
t3 = $[18];
|
|
260
447
|
}
|
|
261
448
|
React.useEffect(t2, t3);
|
|
262
449
|
return null;
|
|
@@ -271,7 +458,7 @@ const AutosaveIndicator = React.memo(function AutosaveIndicator$1(t0) {
|
|
|
271
458
|
$[1] = t1;
|
|
272
459
|
} else t1 = $[1];
|
|
273
460
|
const { isDirty } = useFormState(t1);
|
|
274
|
-
const [, forceUpdate] = React.useReducer(
|
|
461
|
+
const [, forceUpdate] = React.useReducer(_temp2, 0);
|
|
275
462
|
let t2;
|
|
276
463
|
if ($[2] !== forceUpdate || $[3] !== lastSaved) {
|
|
277
464
|
t2 = () => {
|
|
@@ -571,6 +758,33 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
571
758
|
resolver,
|
|
572
759
|
mode: "onBlur"
|
|
573
760
|
});
|
|
761
|
+
const commitPreviewSnapshot = React.useCallback((data) => {
|
|
762
|
+
if (!isLivePreviewOpen) return;
|
|
763
|
+
previewRef.current?.sendCommit(clonePreviewSnapshot(data));
|
|
764
|
+
}, [isLivePreviewOpen]);
|
|
765
|
+
const triggerPreviewFullResync = React.useCallback((reason) => {
|
|
766
|
+
if (!isLivePreviewOpen) return;
|
|
767
|
+
previewRef.current?.sendFullResync(reason);
|
|
768
|
+
previewRef.current?.triggerRefresh();
|
|
769
|
+
}, [isLivePreviewOpen]);
|
|
770
|
+
const handlePreviewFieldValueEdited = React.useCallback((message) => {
|
|
771
|
+
if (!isAllowedPreviewEditPath({
|
|
772
|
+
message,
|
|
773
|
+
schema,
|
|
774
|
+
values: form.getValues()
|
|
775
|
+
})) return;
|
|
776
|
+
const normalized = normalizeInlineEditValue(message);
|
|
777
|
+
if (!normalized.ok) return;
|
|
778
|
+
form.setValue(message.path, normalized.value, {
|
|
779
|
+
shouldDirty: true,
|
|
780
|
+
shouldTouch: true,
|
|
781
|
+
shouldValidate: true
|
|
782
|
+
});
|
|
783
|
+
setTimeout(() => scrollFieldIntoView(message.path), 0);
|
|
784
|
+
}, [form, schema]);
|
|
785
|
+
const handlePreviewResyncRequest = React.useCallback((reason_0) => {
|
|
786
|
+
triggerPreviewFullResync(reason_0 ?? "preview-request");
|
|
787
|
+
}, [triggerPreviewFullResync]);
|
|
574
788
|
/**
|
|
575
789
|
* Execute the confirmed workflow transition (immediate or scheduled).
|
|
576
790
|
*/
|
|
@@ -595,6 +809,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
595
809
|
if ("id" in result_0) form.reset(result_0);
|
|
596
810
|
}
|
|
597
811
|
}
|
|
812
|
+
triggerPreviewFullResync("workflow-transition");
|
|
598
813
|
if (transitionSchedule) if (transitionScheduledAt) toast.success(t("workflow.scheduledSuccess", {
|
|
599
814
|
stage: stageLabel,
|
|
600
815
|
date: transitionScheduledAt.toLocaleString()
|
|
@@ -664,7 +879,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
664
879
|
}
|
|
665
880
|
}, [
|
|
666
881
|
contentLocale,
|
|
667
|
-
form
|
|
882
|
+
form,
|
|
668
883
|
setContentLocale,
|
|
669
884
|
localeChangeDialog.open,
|
|
670
885
|
isEditMode
|
|
@@ -688,6 +903,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
688
903
|
prevLocaleRef.current = localeChangeDialog.pendingLocale;
|
|
689
904
|
setContentLocale(localeChangeDialog.pendingLocale);
|
|
690
905
|
localeQueryClient.invalidateQueries({ queryKey: ["collections", collection] });
|
|
906
|
+
triggerPreviewFullResync("locale-change");
|
|
691
907
|
}
|
|
692
908
|
setLocaleChangeDialog({
|
|
693
909
|
open: false,
|
|
@@ -697,7 +913,8 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
697
913
|
localeChangeDialog.pendingLocale,
|
|
698
914
|
setContentLocale,
|
|
699
915
|
localeQueryClient,
|
|
700
|
-
collection
|
|
916
|
+
collection,
|
|
917
|
+
triggerPreviewFullResync
|
|
701
918
|
]);
|
|
702
919
|
const handleLocaleChangeCancel = React.useCallback(() => {
|
|
703
920
|
skipItemResetRef.current = false;
|
|
@@ -716,13 +933,13 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
716
933
|
const handleLocaleDialogOpenChange = React.useCallback((open) => {
|
|
717
934
|
if (!open) handleLocaleChangeCancel();
|
|
718
935
|
}, [handleLocaleChangeCancel]);
|
|
719
|
-
const onSubmit = React.useEffectEvent(async (
|
|
936
|
+
const onSubmit = React.useEffectEvent(async (data_0) => {
|
|
720
937
|
const savePromise = async () => {
|
|
721
938
|
if (isEditMode && id) return await updateMutation.mutateAsync({
|
|
722
939
|
id,
|
|
723
|
-
data
|
|
940
|
+
data: data_0
|
|
724
941
|
});
|
|
725
|
-
else return await createMutation.mutateAsync(
|
|
942
|
+
else return await createMutation.mutateAsync(data_0);
|
|
726
943
|
};
|
|
727
944
|
toast.promise(savePromise(), {
|
|
728
945
|
loading: isEditMode ? t("toast.saving") : t("toast.creating"),
|
|
@@ -730,6 +947,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
730
947
|
if (onSuccess) onSuccess(result_1);
|
|
731
948
|
else if (isEditMode) {
|
|
732
949
|
form.reset(result_1);
|
|
950
|
+
commitPreviewSnapshot(result_1);
|
|
733
951
|
triggerPreviewRefresh();
|
|
734
952
|
} else if (result_1?.id) navigate(`${basePath}/collections/${collection}/${result_1.id}`);
|
|
735
953
|
else navigate(`${basePath}/collections/${collection}`);
|
|
@@ -743,9 +961,9 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
743
961
|
});
|
|
744
962
|
return t("toast.validationFailed");
|
|
745
963
|
}
|
|
746
|
-
const
|
|
747
|
-
onError?.(error instanceof Error ? error : new Error(
|
|
748
|
-
return `${t("toast.saveFailed")}: ${
|
|
964
|
+
const message_0 = error instanceof Error ? error.message : t("error.unknown");
|
|
965
|
+
onError?.(error instanceof Error ? error : new Error(message_0));
|
|
966
|
+
return `${t("toast.saveFailed")}: ${message_0}`;
|
|
749
967
|
}
|
|
750
968
|
});
|
|
751
969
|
});
|
|
@@ -988,7 +1206,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
988
1206
|
let errorBody = {};
|
|
989
1207
|
try {
|
|
990
1208
|
errorBody = await response_0.json();
|
|
991
|
-
} catch
|
|
1209
|
+
} catch {}
|
|
992
1210
|
let errorMessage;
|
|
993
1211
|
if (errorBody.message) if (typeof errorBody.message === "string") errorMessage = errorBody.message;
|
|
994
1212
|
else errorMessage = t("toast.actionFailed");
|
|
@@ -1062,7 +1280,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
1062
1280
|
else if (typeof pendingRevertVersion.versionNumber === "number") payload.version = pendingRevertVersion.versionNumber;
|
|
1063
1281
|
const result_3 = await revertVersionMutation.mutateAsync(payload);
|
|
1064
1282
|
form.reset(result_3);
|
|
1065
|
-
|
|
1283
|
+
triggerPreviewFullResync("version-revert");
|
|
1066
1284
|
toast.success(t("version.revertSuccess"));
|
|
1067
1285
|
setPendingRevertVersion(null);
|
|
1068
1286
|
};
|
|
@@ -1252,10 +1470,16 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
1252
1470
|
isDirtyRef: formIsDirtyRef,
|
|
1253
1471
|
isSubmittingRef: formIsSubmittingRef,
|
|
1254
1472
|
updateMutation,
|
|
1473
|
+
onPreviewCommit: commitPreviewSnapshot,
|
|
1255
1474
|
onPreviewRefresh: triggerPreviewRefresh,
|
|
1256
1475
|
onSavingChange: setIsSaving,
|
|
1257
1476
|
onSaved: setLastSaved
|
|
1258
1477
|
}),
|
|
1478
|
+
/* @__PURE__ */ jsx(PreviewPatchBridge, {
|
|
1479
|
+
form,
|
|
1480
|
+
previewRef,
|
|
1481
|
+
enabled: isLivePreviewOpen && !!previewUrl && !isBlocked
|
|
1482
|
+
}),
|
|
1259
1483
|
/* @__PURE__ */ jsx(ReactiveFieldsManager, {
|
|
1260
1484
|
collection,
|
|
1261
1485
|
reactiveConfigs,
|
|
@@ -1591,12 +1815,17 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
1591
1815
|
onClose: () => setIsLivePreviewOpen(false),
|
|
1592
1816
|
previewUrl,
|
|
1593
1817
|
previewRef,
|
|
1818
|
+
onFieldValueEdited: handlePreviewFieldValueEdited,
|
|
1819
|
+
onResyncRequest: handlePreviewResyncRequest,
|
|
1594
1820
|
defaultSize: schemaPreview?.defaultSize,
|
|
1595
1821
|
minSize: schemaPreview?.minSize,
|
|
1596
1822
|
children: formShell
|
|
1597
1823
|
});
|
|
1598
1824
|
}
|
|
1599
|
-
function _temp(
|
|
1825
|
+
function _temp(op) {
|
|
1826
|
+
return op.path;
|
|
1827
|
+
}
|
|
1828
|
+
function _temp2(x) {
|
|
1600
1829
|
return x + 1;
|
|
1601
1830
|
}
|
|
1602
1831
|
|