@firecms/core 3.0.0-rc.2 → 3.0.0-rc.4

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.
Files changed (52) hide show
  1. package/dist/components/HomePage/HomePageDnD.d.ts +2 -1
  2. package/dist/components/PropertyCollectionView.d.ts +23 -0
  3. package/dist/core/EntityEditView.d.ts +10 -4
  4. package/dist/form/EntityForm.d.ts +5 -2
  5. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  6. package/dist/form/components/LocalChangesMenu.d.ts +11 -0
  7. package/dist/form/index.d.ts +2 -1
  8. package/dist/index.es.js +1307 -384
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.umd.js +1306 -383
  11. package/dist/index.umd.js.map +1 -1
  12. package/dist/types/collections.d.ts +11 -0
  13. package/dist/types/fields.d.ts +8 -0
  14. package/dist/types/properties.d.ts +32 -6
  15. package/dist/util/collections.d.ts +1 -0
  16. package/dist/util/entity_cache.d.ts +6 -1
  17. package/dist/util/make_properties_editable.d.ts +1 -2
  18. package/dist/util/objects.d.ts +1 -0
  19. package/dist/util/useStorageUploadController.d.ts +1 -0
  20. package/package.json +6 -6
  21. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +47 -47
  22. package/src/components/EntityCollectionView/EntityCollectionView.tsx +6 -1
  23. package/src/components/EntityView.tsx +29 -40
  24. package/src/components/HomePage/DefaultHomePage.tsx +13 -9
  25. package/src/components/HomePage/HomePageDnD.tsx +140 -38
  26. package/src/components/PropertyCollectionView.tsx +329 -0
  27. package/src/components/SelectableTable/SelectableTable.tsx +0 -12
  28. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +2 -1
  29. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +0 -1
  30. package/src/core/EntityEditView.tsx +27 -14
  31. package/src/core/EntityEditViewFormActions.tsx +33 -18
  32. package/src/core/EntitySidePanel.tsx +9 -3
  33. package/src/form/EntityForm.tsx +173 -42
  34. package/src/form/EntityFormActions.tsx +30 -15
  35. package/src/form/PropertyFieldBinding.tsx +4 -4
  36. package/src/form/components/ErrorFocus.tsx +22 -29
  37. package/src/form/components/LocalChangesMenu.tsx +144 -0
  38. package/src/form/field_bindings/BlockFieldBinding.tsx +1 -0
  39. package/src/form/index.tsx +5 -1
  40. package/src/hooks/useBuildNavigationController.tsx +104 -31
  41. package/src/preview/property_previews/MapPropertyPreview.tsx +2 -2
  42. package/src/preview/property_previews/NumberPropertyPreview.tsx +2 -2
  43. package/src/types/collections.ts +12 -0
  44. package/src/types/fields.tsx +10 -0
  45. package/src/types/properties.ts +35 -6
  46. package/src/util/collections.ts +8 -0
  47. package/src/util/createFormexStub.tsx +4 -0
  48. package/src/util/entity_cache.ts +71 -52
  49. package/src/util/join_collections.ts +3 -3
  50. package/src/util/make_properties_editable.ts +0 -22
  51. package/src/util/objects.ts +40 -2
  52. package/src/util/useStorageUploadController.tsx +71 -34
package/dist/index.umd.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react-compiler-runtime"), require("react"), require("@firecms/ui"), require("notistack"), require("object-hash"), require("@firecms/formex"), require("react-router-dom"), require("fuse.js"), require("react-fast-compare"), require("date-fns"), require("date-fns/locale"), require("react-use-measure"), require("yup"), require("react-window"), require("@dnd-kit/core"), require("@dnd-kit/sortable"), require("@dnd-kit/utilities"), require("@dnd-kit/modifiers"), require("react-dropzone"), require("react-image-file-resizer"), require("@firecms/editor"), require("prism-react-renderer"), require("react-router"), require("@radix-ui/react-portal")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react-compiler-runtime", "react", "@firecms/ui", "notistack", "object-hash", "@firecms/formex", "react-router-dom", "fuse.js", "react-fast-compare", "date-fns", "date-fns/locale", "react-use-measure", "yup", "react-window", "@dnd-kit/core", "@dnd-kit/sortable", "@dnd-kit/utilities", "@dnd-kit/modifiers", "react-dropzone", "react-image-file-resizer", "@firecms/editor", "prism-react-renderer", "react-router", "@radix-ui/react-portal"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["FireCMS Core"] = {}, global.jsxRuntime, global.reactCompilerRuntime, global.React, global.ui, global.notistack, global.hash, global.formex, global.reactRouterDom, global.Fuse, global.equal, global.dateFns, global.locales, global.useMeasure, global.yup, global.reactWindow, global.core, global.sortable, global.utilities, global.modifiers, global.reactDropzone, global.Resizer, global.editor, global.prismReactRenderer, global.reactRouter, global.Portal));
3
- })(this, (function(exports2, jsxRuntime, reactCompilerRuntime, React, ui, notistack, hash, formex, reactRouterDom, Fuse, equal, dateFns, locales, useMeasure, yup, reactWindow, core, sortable, utilities, modifiers, reactDropzone, Resizer, editor, prismReactRenderer, reactRouter, Portal) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("react-compiler-runtime"), require("react"), require("@firecms/ui"), require("notistack"), require("object-hash"), require("@firecms/formex"), require("react-router-dom"), require("fuse.js"), require("react-fast-compare"), require("date-fns"), require("date-fns/locale"), require("react-use-measure"), require("yup"), require("react-window"), require("@dnd-kit/core"), require("@dnd-kit/sortable"), require("@dnd-kit/utilities"), require("@dnd-kit/modifiers"), require("react-dropzone"), require("compressorjs"), require("@firecms/editor"), require("prism-react-renderer"), require("react-router"), require("@radix-ui/react-portal")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "react-compiler-runtime", "react", "@firecms/ui", "notistack", "object-hash", "@firecms/formex", "react-router-dom", "fuse.js", "react-fast-compare", "date-fns", "date-fns/locale", "react-use-measure", "yup", "react-window", "@dnd-kit/core", "@dnd-kit/sortable", "@dnd-kit/utilities", "@dnd-kit/modifiers", "react-dropzone", "compressorjs", "@firecms/editor", "prism-react-renderer", "react-router", "@radix-ui/react-portal"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["FireCMS Core"] = {}, global.jsxRuntime, global.reactCompilerRuntime, global.React, global.ui, global.notistack, global.hash, global.formex, global.reactRouterDom, global.Fuse, global.equal, global.dateFns, global.locales, global.useMeasure, global.yup, global.reactWindow, global.core, global.sortable, global.utilities, global.modifiers, global.reactDropzone, global.Compressor, global.editor, global.prismReactRenderer, global.reactRouter, global.Portal));
3
+ })(this, (function(exports2, jsxRuntime, reactCompilerRuntime, React, ui, notistack, hash, formex, reactRouterDom, Fuse, equal, dateFns, locales, useMeasure, yup, reactWindow, core, sortable, utilities, modifiers, reactDropzone, Compressor, editor, prismReactRenderer, reactRouter, Portal) {
4
4
  "use strict";
5
5
  function _interopNamespaceDefault(e) {
6
6
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -253,6 +253,13 @@
253
253
  function isObject(item) {
254
254
  return item && typeof item === "object" && !Array.isArray(item);
255
255
  }
256
+ function isPlainObject(obj) {
257
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
258
+ return false;
259
+ }
260
+ const proto = Object.getPrototypeOf(obj);
261
+ return proto === Object.prototype;
262
+ }
256
263
  function mergeDeep(target, source, ignoreUndefined = false) {
257
264
  if (!isObject(target)) {
258
265
  return target;
@@ -273,7 +280,28 @@
273
280
  if (sourceValue instanceof Date) {
274
281
  output[key] = new Date(sourceValue.getTime());
275
282
  } else if (Array.isArray(sourceValue)) {
276
- output[key] = [...sourceValue];
283
+ if (Array.isArray(outputValue)) {
284
+ const newArray = [];
285
+ const maxLength = Math.max(outputValue.length, sourceValue.length);
286
+ for (let i = 0; i < maxLength; i++) {
287
+ const sourceItem = sourceValue[i];
288
+ const targetItem = outputValue[i];
289
+ if (i >= sourceValue.length) {
290
+ newArray[i] = targetItem;
291
+ } else if (i >= outputValue.length) {
292
+ newArray[i] = sourceItem;
293
+ } else if (sourceItem === null) {
294
+ newArray[i] = targetItem;
295
+ } else if (isObject(sourceItem) && isObject(targetItem)) {
296
+ newArray[i] = mergeDeep(targetItem, sourceItem, ignoreUndefined);
297
+ } else {
298
+ newArray[i] = sourceItem;
299
+ }
300
+ }
301
+ output[key] = newArray;
302
+ } else {
303
+ output[key] = [...sourceValue];
304
+ }
277
305
  } else if (isObject(sourceValue)) {
278
306
  if (isObject(outputValue)) {
279
307
  output[key] = mergeDeep(outputValue, sourceValue, ignoreUndefined);
@@ -1052,6 +1080,12 @@
1052
1080
  };
1053
1081
  });
1054
1082
  };
1083
+ function getLocalChangesBackup(collection) {
1084
+ if (!collection.localChangesBackup) {
1085
+ return "manual_apply";
1086
+ }
1087
+ return collection.localChangesBackup;
1088
+ }
1055
1089
  const kebabCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g;
1056
1090
  const toKebabCase = (str) => {
1057
1091
  const regExpMatchArray = str.match(kebabCaseRegex);
@@ -3508,26 +3542,6 @@
3508
3542
  });
3509
3543
  return properties;
3510
3544
  }
3511
- function makePropertiesNonEditable(properties) {
3512
- return Object.entries(properties).reduce((acc, [key, property]) => {
3513
- if (!isPropertyBuilder(property) && property.dataType === "map" && property.properties) {
3514
- const updated = {
3515
- ...property,
3516
- properties: makePropertiesNonEditable(property.properties)
3517
- };
3518
- acc[key] = updated;
3519
- }
3520
- if (isPropertyBuilder(property)) {
3521
- acc[key] = property;
3522
- } else {
3523
- acc[key] = {
3524
- ...property,
3525
- editable: false
3526
- };
3527
- }
3528
- return acc;
3529
- }, {});
3530
- }
3531
3545
  function applyModifyFunction(modifyCollection, collection, parentPaths) {
3532
3546
  if (modifyCollection) {
3533
3547
  const modified = modifyCollection({
@@ -3609,8 +3623,8 @@
3609
3623
  return target;
3610
3624
  } else {
3611
3625
  const mergedProperty = mergeDeep(target, source);
3612
- const targetEditable = Boolean(target.editable);
3613
- const sourceEditable = Boolean(source.editable);
3626
+ const targetEditable = target.editable === void 0 ? true : Boolean(target.editable);
3627
+ const sourceEditable = source.editable === void 0 ? true : Boolean(source.editable);
3614
3628
  if (source.dataType === "map" && source.properties) {
3615
3629
  const targetProperties = "properties" in target ? target.properties : {};
3616
3630
  const sourceProperties = "properties" in source ? source.properties : {};
@@ -6636,7 +6650,7 @@
6636
6650
  const isArrayOrMap = childProperty.dataType === "map" || childProperty === "array";
6637
6651
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls(ui.defaultBorderMixin, "last:border-b-0 border-b"), children: [
6638
6652
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row pt-0.5 pb-0.5 gap-2", children: [
6639
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[140px] w-[25%] py-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", className: "font-mono break-words", color: "secondary", children: childProperty.name }) }),
6653
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[140px] w-[25%] py-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", className: "break-words font-semibold", color: "secondary", children: childProperty.name }) }),
6640
6654
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow max-w-[75%]", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: !isArrayOrMap && /* @__PURE__ */ jsxRuntime.jsx(PropertyPreview, { propertyKey: key_0, value: value[key_0], property: childProperty, size }) }) })
6641
6655
  ] }),
6642
6656
  isArrayOrMap && /* @__PURE__ */ jsxRuntime.jsx("div", { className: ui.cls(ui.defaultBorderMixin, "border-l pl-4 ml-2 my-2"), children: /* @__PURE__ */ jsxRuntime.jsx(PropertyPreview, { propertyKey: key_0, value: value[key_0], property: childProperty, size }) })
@@ -6692,7 +6706,7 @@
6692
6706
  const [key, childValue] = t0;
6693
6707
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls(ui.defaultBorderMixin, "last:border-b-0 border-b"), children: [
6694
6708
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row pt-0.5 pb-0.5 gap-2", children: [
6695
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[140px] w-[25%] py-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", className: "font-mono break-words", color: "secondary", children: key }) }, `table-cell-title-${key}-${key}`),
6709
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[140px] w-[25%] py-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", className: "font-semibold break-words", color: "secondary", children: key }) }, `table-cell-title-${key}-${key}`),
6696
6710
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow max-w-[75%]", children: childValue && typeof childValue !== "object" && /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: childValue.toString() }) }) })
6697
6711
  ] }),
6698
6712
  typeof childValue === "object" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: ui.cls(ui.defaultBorderMixin, "border-l pl-4"), children: /* @__PURE__ */ jsxRuntime.jsx(KeyValuePreview, { value: childValue }) })
@@ -6769,7 +6783,7 @@
6769
6783
  return t3;
6770
6784
  }
6771
6785
  function NumberPropertyPreview(t0) {
6772
- const $ = reactCompilerRuntime.c(10);
6786
+ const $ = reactCompilerRuntime.c(12);
6773
6787
  const {
6774
6788
  value,
6775
6789
  property,
@@ -6787,38 +6801,42 @@
6787
6801
  }
6788
6802
  const enumValues = t1;
6789
6803
  if (!enumValues) {
6790
- let t22;
6791
- if ($[2] !== value) {
6792
- t22 = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: value });
6793
- $[2] = value;
6794
- $[3] = t22;
6804
+ const t22 = size === "small" ? "text-sm" : "";
6805
+ let t32;
6806
+ if ($[2] !== t22 || $[3] !== value) {
6807
+ t32 = /* @__PURE__ */ jsxRuntime.jsx("span", { className: t22, children: value });
6808
+ $[2] = t22;
6809
+ $[3] = value;
6810
+ $[4] = t32;
6795
6811
  } else {
6796
- t22 = $[3];
6812
+ t32 = $[4];
6797
6813
  }
6798
- return t22;
6814
+ return t32;
6799
6815
  }
6800
6816
  const t2 = size !== "medium" ? "small" : "medium";
6801
6817
  let t3;
6802
- if ($[4] !== enumKey || $[5] !== enumValues || $[6] !== t2) {
6818
+ if ($[5] !== enumKey || $[6] !== enumValues || $[7] !== t2) {
6803
6819
  t3 = /* @__PURE__ */ jsxRuntime.jsx(EnumValuesChip, { enumKey, enumValues, size: t2 });
6804
- $[4] = enumKey;
6805
- $[5] = enumValues;
6806
- $[6] = t2;
6807
- $[7] = t3;
6820
+ $[5] = enumKey;
6821
+ $[6] = enumValues;
6822
+ $[7] = t2;
6823
+ $[8] = t3;
6808
6824
  } else {
6809
- t3 = $[7];
6825
+ t3 = $[8];
6810
6826
  }
6811
6827
  return t3;
6812
6828
  } else {
6813
- let t1;
6814
- if ($[8] !== value) {
6815
- t1 = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: value });
6816
- $[8] = value;
6829
+ const t1 = size === "small" ? "text-sm" : "";
6830
+ let t2;
6831
+ if ($[9] !== t1 || $[10] !== value) {
6832
+ t2 = /* @__PURE__ */ jsxRuntime.jsx("span", { className: t1, children: value });
6817
6833
  $[9] = t1;
6834
+ $[10] = value;
6835
+ $[11] = t2;
6818
6836
  } else {
6819
- t1 = $[9];
6837
+ t2 = $[11];
6820
6838
  }
6821
- return t1;
6839
+ return t2;
6822
6840
  }
6823
6841
  }
6824
6842
  function UserDisplay(t0) {
@@ -7367,6 +7385,389 @@
7367
7385
  }
7368
7386
  return t3;
7369
7387
  });
7388
+ function buildPropertyLabelAndGetProperty(properties, key) {
7389
+ if (!key) return {
7390
+ label: "",
7391
+ property: void 0
7392
+ };
7393
+ const segments = [];
7394
+ const re = /([^[.\]]+)|\[(\d+)\]/g;
7395
+ let m;
7396
+ while ((m = re.exec(key)) !== null) {
7397
+ if (m[1] !== void 0) segments.push(m[1]);
7398
+ else if (m[2] !== void 0) segments.push(Number(m[2]));
7399
+ }
7400
+ let currentProps = properties;
7401
+ let currentProp;
7402
+ let lastLabel = "";
7403
+ const getArrayOfProp = (p) => {
7404
+ if (!p || p.dataType !== "array") return void 0;
7405
+ return Array.isArray(p.of) ? p.of[0] : p.of;
7406
+ };
7407
+ for (const seg of segments) {
7408
+ if (typeof seg === "number") {
7409
+ lastLabel = `[${seg}]`;
7410
+ if (currentProp?.dataType === "array") {
7411
+ currentProp = getArrayOfProp(currentProp);
7412
+ if (currentProp?.dataType === "map" && currentProp.properties) {
7413
+ currentProps = currentProp.properties;
7414
+ } else {
7415
+ currentProps = void 0;
7416
+ }
7417
+ } else {
7418
+ currentProp = void 0;
7419
+ currentProps = void 0;
7420
+ }
7421
+ continue;
7422
+ }
7423
+ if (currentProps && currentProps[seg]) {
7424
+ const nextProp = currentProps[seg];
7425
+ currentProp = nextProp;
7426
+ lastLabel = nextProp.name || String(seg);
7427
+ if (nextProp.dataType === "map" && nextProp.properties) {
7428
+ currentProps = nextProp.properties;
7429
+ } else if (nextProp.dataType === "array") {
7430
+ currentProps = void 0;
7431
+ } else {
7432
+ currentProps = void 0;
7433
+ }
7434
+ } else {
7435
+ currentProp = void 0;
7436
+ currentProps = void 0;
7437
+ lastLabel = String(seg);
7438
+ }
7439
+ }
7440
+ return {
7441
+ label: lastLabel,
7442
+ property: currentProp
7443
+ };
7444
+ }
7445
+ const pathEndsWithIndex = (p) => /\[\d+\]$/.test(p);
7446
+ const PropertyCollectionView = (t0) => {
7447
+ const $ = reactCompilerRuntime.c(89);
7448
+ const {
7449
+ data,
7450
+ properties,
7451
+ baseKey: t1,
7452
+ suppressHeader: t2,
7453
+ size: t3
7454
+ } = t0;
7455
+ const baseKey = t1 === void 0 ? "" : t1;
7456
+ const suppressHeader = t2 === void 0 ? false : t2;
7457
+ const size = t3 === void 0 ? "small" : t3;
7458
+ const isTopLevel = !!baseKey && !baseKey.includes(".") && !baseKey.includes("[");
7459
+ if (Array.isArray(data)) {
7460
+ let t4;
7461
+ if ($[0] !== baseKey || $[1] !== properties) {
7462
+ t4 = baseKey ? buildPropertyLabelAndGetProperty(properties, baseKey) : {
7463
+ label: "",
7464
+ property: void 0
7465
+ };
7466
+ $[0] = baseKey;
7467
+ $[1] = properties;
7468
+ $[2] = t4;
7469
+ } else {
7470
+ t4 = $[2];
7471
+ }
7472
+ const {
7473
+ label: arrayLabel,
7474
+ property
7475
+ } = t4;
7476
+ const ofProp = property?.dataType === "array" ? Array.isArray(property.of) ? property.of[0] : property.of : void 0;
7477
+ const isArrayOfMaps = ofProp?.dataType === "map";
7478
+ const isArrayOfPrimitives = property?.dataType === "array" && ofProp && ofProp.dataType !== "map";
7479
+ if (baseKey && property && isArrayOfPrimitives) {
7480
+ const t52 = `grid grid-cols-12 gap-x-4 ${isTopLevel ? "py-4" : "py-2"} items-start ${isTopLevel ? `border-b ${ui.defaultBorderMixin} last:border-b-0` : ""}`;
7481
+ let t62;
7482
+ if ($[3] !== arrayLabel) {
7483
+ t62 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: arrayLabel }) });
7484
+ $[3] = arrayLabel;
7485
+ $[4] = t62;
7486
+ } else {
7487
+ t62 = $[4];
7488
+ }
7489
+ let t72;
7490
+ if ($[5] !== baseKey || $[6] !== data || $[7] !== property || $[8] !== size) {
7491
+ t72 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsxRuntime.jsx(PropertyPreview, { propertyKey: baseKey, value: data, property, size }) });
7492
+ $[5] = baseKey;
7493
+ $[6] = data;
7494
+ $[7] = property;
7495
+ $[8] = size;
7496
+ $[9] = t72;
7497
+ } else {
7498
+ t72 = $[9];
7499
+ }
7500
+ let t82;
7501
+ if ($[10] !== t52 || $[11] !== t62 || $[12] !== t72) {
7502
+ t82 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: t52, children: [
7503
+ t62,
7504
+ t72
7505
+ ] });
7506
+ $[10] = t52;
7507
+ $[11] = t62;
7508
+ $[12] = t72;
7509
+ $[13] = t82;
7510
+ } else {
7511
+ t82 = $[13];
7512
+ }
7513
+ return t82;
7514
+ }
7515
+ const t5 = `${isTopLevel ? "py-4" : "py-1"} ${isTopLevel ? `border-b ${ui.defaultBorderMixin} last:border-b-0` : ""}`;
7516
+ let t6;
7517
+ if ($[14] !== arrayLabel || $[15] !== baseKey || $[16] !== suppressHeader) {
7518
+ t6 = baseKey && arrayLabel && !suppressHeader && /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", children: arrayLabel });
7519
+ $[14] = arrayLabel;
7520
+ $[15] = baseKey;
7521
+ $[16] = suppressHeader;
7522
+ $[17] = t6;
7523
+ } else {
7524
+ t6 = $[17];
7525
+ }
7526
+ const t7 = baseKey ? `pl-4 mt-1 border-l ${ui.defaultBorderMixin}` : "";
7527
+ let t8;
7528
+ if ($[18] !== baseKey || $[19] !== data || $[20] !== isArrayOfMaps || $[21] !== ofProp || $[22] !== properties || $[23] !== size) {
7529
+ let t92;
7530
+ if ($[25] !== baseKey || $[26] !== isArrayOfMaps || $[27] !== ofProp || $[28] !== properties || $[29] !== size) {
7531
+ t92 = (item, index) => {
7532
+ if (item === null || item === void 0) {
7533
+ return null;
7534
+ }
7535
+ const currentKey = baseKey ? `${baseKey}[${index}]` : `[${index}]`;
7536
+ const itemHeader = isArrayOfMaps && ofProp?.name ? `${ofProp.name} [${index}]` : `[${index}]`;
7537
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-1", children: [
7538
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", children: itemHeader }),
7539
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `pl-4 mt-1 border-l ${ui.defaultBorderMixin}`, children: /* @__PURE__ */ jsxRuntime.jsx(PropertyCollectionView, { data: item, properties, baseKey: currentKey, suppressHeader: true, size }) })
7540
+ ] }, currentKey);
7541
+ };
7542
+ $[25] = baseKey;
7543
+ $[26] = isArrayOfMaps;
7544
+ $[27] = ofProp;
7545
+ $[28] = properties;
7546
+ $[29] = size;
7547
+ $[30] = t92;
7548
+ } else {
7549
+ t92 = $[30];
7550
+ }
7551
+ t8 = data.map(t92);
7552
+ $[18] = baseKey;
7553
+ $[19] = data;
7554
+ $[20] = isArrayOfMaps;
7555
+ $[21] = ofProp;
7556
+ $[22] = properties;
7557
+ $[23] = size;
7558
+ $[24] = t8;
7559
+ } else {
7560
+ t8 = $[24];
7561
+ }
7562
+ let t9;
7563
+ if ($[31] !== t7 || $[32] !== t8) {
7564
+ t9 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: t7, children: t8 });
7565
+ $[31] = t7;
7566
+ $[32] = t8;
7567
+ $[33] = t9;
7568
+ } else {
7569
+ t9 = $[33];
7570
+ }
7571
+ let t10;
7572
+ if ($[34] !== t5 || $[35] !== t6 || $[36] !== t9) {
7573
+ t10 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: t5, children: [
7574
+ t6,
7575
+ t9
7576
+ ] });
7577
+ $[34] = t5;
7578
+ $[35] = t6;
7579
+ $[36] = t9;
7580
+ $[37] = t10;
7581
+ } else {
7582
+ t10 = $[37];
7583
+ }
7584
+ return t10;
7585
+ }
7586
+ if (typeof data === "object" && data !== null) {
7587
+ let t4;
7588
+ if ($[38] !== baseKey || $[39] !== properties) {
7589
+ t4 = baseKey ? buildPropertyLabelAndGetProperty(properties, baseKey) : {
7590
+ label: "",
7591
+ property: void 0
7592
+ };
7593
+ $[38] = baseKey;
7594
+ $[39] = properties;
7595
+ $[40] = t4;
7596
+ } else {
7597
+ t4 = $[40];
7598
+ }
7599
+ const {
7600
+ label,
7601
+ property: property_0
7602
+ } = t4;
7603
+ if (baseKey && (!property_0 || property_0.dataType !== "map" || !property_0.properties)) {
7604
+ if (!property_0) {
7605
+ return null;
7606
+ }
7607
+ const t52 = `grid grid-cols-12 gap-x-4 ${isTopLevel ? "py-4" : "py-2"} items-start ${isTopLevel ? `border-b ${ui.defaultBorderMixin} last:border-b-0` : ""}`;
7608
+ let t62;
7609
+ if ($[41] !== label) {
7610
+ t62 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: label }) });
7611
+ $[41] = label;
7612
+ $[42] = t62;
7613
+ } else {
7614
+ t62 = $[42];
7615
+ }
7616
+ let t72;
7617
+ if ($[43] !== baseKey || $[44] !== data || $[45] !== property_0 || $[46] !== size) {
7618
+ t72 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsxRuntime.jsx(PropertyPreview, { propertyKey: baseKey, value: data, property: property_0, size }) });
7619
+ $[43] = baseKey;
7620
+ $[44] = data;
7621
+ $[45] = property_0;
7622
+ $[46] = size;
7623
+ $[47] = t72;
7624
+ } else {
7625
+ t72 = $[47];
7626
+ }
7627
+ let t82;
7628
+ if ($[48] !== t52 || $[49] !== t62 || $[50] !== t72) {
7629
+ t82 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: t52, children: [
7630
+ t62,
7631
+ t72
7632
+ ] });
7633
+ $[48] = t52;
7634
+ $[49] = t62;
7635
+ $[50] = t72;
7636
+ $[51] = t82;
7637
+ } else {
7638
+ t82 = $[51];
7639
+ }
7640
+ return t82;
7641
+ }
7642
+ let t5;
7643
+ if ($[52] !== baseKey || $[53] !== property_0 || $[54] !== suppressHeader) {
7644
+ t5 = baseKey && !suppressHeader && property_0?.dataType === "map" && (property_0.name || !pathEndsWithIndex(baseKey));
7645
+ $[52] = baseKey;
7646
+ $[53] = property_0;
7647
+ $[54] = suppressHeader;
7648
+ $[55] = t5;
7649
+ } else {
7650
+ t5 = $[55];
7651
+ }
7652
+ const showMapHeader = t5;
7653
+ const headerText = property_0?.name || label;
7654
+ const t6 = `${isTopLevel ? "py-4" : "py-1"} ${isTopLevel ? `border-b ${ui.defaultBorderMixin} last:border-b-0` : ""}`;
7655
+ let t7;
7656
+ if ($[56] !== headerText || $[57] !== showMapHeader) {
7657
+ t7 = showMapHeader && /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", children: headerText });
7658
+ $[56] = headerText;
7659
+ $[57] = showMapHeader;
7660
+ $[58] = t7;
7661
+ } else {
7662
+ t7 = $[58];
7663
+ }
7664
+ const t8 = baseKey ? `pl-4 mt-1 border-l ${ui.defaultBorderMixin}` : "";
7665
+ let t9;
7666
+ if ($[59] !== baseKey || $[60] !== data || $[61] !== properties || $[62] !== size) {
7667
+ let t102;
7668
+ if ($[64] !== baseKey || $[65] !== properties || $[66] !== size) {
7669
+ t102 = (t112) => {
7670
+ const [key, value] = t112;
7671
+ if (value === null || value === void 0) {
7672
+ return null;
7673
+ }
7674
+ const currentKey_0 = baseKey ? `${baseKey}.${key}` : key;
7675
+ return /* @__PURE__ */ jsxRuntime.jsx(PropertyCollectionView, { data: value, properties, baseKey: currentKey_0, size }, currentKey_0);
7676
+ };
7677
+ $[64] = baseKey;
7678
+ $[65] = properties;
7679
+ $[66] = size;
7680
+ $[67] = t102;
7681
+ } else {
7682
+ t102 = $[67];
7683
+ }
7684
+ t9 = Object.entries(data).map(t102);
7685
+ $[59] = baseKey;
7686
+ $[60] = data;
7687
+ $[61] = properties;
7688
+ $[62] = size;
7689
+ $[63] = t9;
7690
+ } else {
7691
+ t9 = $[63];
7692
+ }
7693
+ let t10;
7694
+ if ($[68] !== t8 || $[69] !== t9) {
7695
+ t10 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: t8, children: t9 });
7696
+ $[68] = t8;
7697
+ $[69] = t9;
7698
+ $[70] = t10;
7699
+ } else {
7700
+ t10 = $[70];
7701
+ }
7702
+ let t11;
7703
+ if ($[71] !== t10 || $[72] !== t6 || $[73] !== t7) {
7704
+ t11 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: t6, children: [
7705
+ t7,
7706
+ t10
7707
+ ] });
7708
+ $[71] = t10;
7709
+ $[72] = t6;
7710
+ $[73] = t7;
7711
+ $[74] = t11;
7712
+ } else {
7713
+ t11 = $[74];
7714
+ }
7715
+ return t11;
7716
+ }
7717
+ if (baseKey) {
7718
+ let t4;
7719
+ if ($[75] !== baseKey || $[76] !== properties) {
7720
+ t4 = buildPropertyLabelAndGetProperty(properties, baseKey);
7721
+ $[75] = baseKey;
7722
+ $[76] = properties;
7723
+ $[77] = t4;
7724
+ } else {
7725
+ t4 = $[77];
7726
+ }
7727
+ const {
7728
+ label: label_0,
7729
+ property: property_1
7730
+ } = t4;
7731
+ if (!property_1) {
7732
+ return null;
7733
+ }
7734
+ const t5 = `grid grid-cols-12 gap-x-4 ${isTopLevel ? "py-4" : "py-2"} items-start ${isTopLevel ? `border-b ${ui.defaultBorderMixin} last:border-b-0` : ""}`;
7735
+ let t6;
7736
+ if ($[78] !== label_0) {
7737
+ t6 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: label_0 }) });
7738
+ $[78] = label_0;
7739
+ $[79] = t6;
7740
+ } else {
7741
+ t6 = $[79];
7742
+ }
7743
+ let t7;
7744
+ if ($[80] !== baseKey || $[81] !== data || $[82] !== property_1 || $[83] !== size) {
7745
+ t7 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsxRuntime.jsx(PropertyPreview, { propertyKey: baseKey, value: data, property: property_1, size }) });
7746
+ $[80] = baseKey;
7747
+ $[81] = data;
7748
+ $[82] = property_1;
7749
+ $[83] = size;
7750
+ $[84] = t7;
7751
+ } else {
7752
+ t7 = $[84];
7753
+ }
7754
+ let t8;
7755
+ if ($[85] !== t5 || $[86] !== t6 || $[87] !== t7) {
7756
+ t8 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: t5, children: [
7757
+ t6,
7758
+ t7
7759
+ ] });
7760
+ $[85] = t5;
7761
+ $[86] = t6;
7762
+ $[87] = t7;
7763
+ $[88] = t8;
7764
+ } else {
7765
+ t8 = $[88];
7766
+ }
7767
+ return t8;
7768
+ }
7769
+ return null;
7770
+ };
7370
7771
  function EntityView({
7371
7772
  entity,
7372
7773
  collection,
@@ -7384,31 +7785,17 @@
7384
7785
  authController
7385
7786
  }), [collection, path, entity, customizationController.propertyConfigs]);
7386
7787
  const properties = resolvedCollection.properties;
7387
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full " + className, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full mb-4", children: [
7388
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls(ui.defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0"), children: [
7389
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center w-1/4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pl-2 text-sm text-surface-600", children: "Id" }) }),
7390
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-2 ml-2 w-3/4 text-surface-900 dark:text-white min-h-[56px] flex items-center", children: [
7788
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full " + className, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full mb-4 p-4", children: [
7789
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-12 gap-x-4 py-4 items-start border-b ${ui.defaultBorderMixin}`, children: [
7790
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: "Id" }) }),
7791
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow text-surface-900 dark:text-white flex items-center", children: [
7391
7792
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-grow mr-2", children: entity.id }),
7392
7793
  customizationController?.entityLinkBuilder && /* @__PURE__ */ jsxRuntime.jsx("a", { href: customizationController.entityLinkBuilder({
7393
7794
  entity
7394
7795
  }), rel: "noopener noreferrer", target: "_blank", children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.OpenInNewIcon, { size: "small" }) }) })
7395
- ] })
7796
+ ] }) })
7396
7797
  ] }),
7397
- Object.entries(properties).map(([key, property]) => {
7398
- const value = entity.values?.[key];
7399
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls(ui.defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0"), children: [
7400
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center w-1/4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pl-2 text-sm text-surface-600", children: property.name }) }),
7401
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-2 ml-2 w-3/4 text-surface-900 dark:text-white min-h-[56px] flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
7402
- PropertyPreview,
7403
- {
7404
- propertyKey: key,
7405
- value,
7406
- property,
7407
- size: "medium"
7408
- }
7409
- ) })
7410
- ] }, `reference_previews_${key}`);
7411
- })
7798
+ /* @__PURE__ */ jsxRuntime.jsx(PropertyCollectionView, { data: entity.values, properties, size: "medium" })
7412
7799
  ] }) });
7413
7800
  }
7414
7801
  function VirtualTableInput(props) {
@@ -7911,7 +8298,8 @@
7911
8298
  const processFile = storage?.processFile;
7912
8299
  const metadata = storage?.metadata;
7913
8300
  const size = multipleFilesSupported ? "medium" : "large";
7914
- const compression = storage?.imageCompression;
8301
+ const imageResize = storage?.imageResize;
8302
+ const legacyCompression = storage?.imageCompression;
7915
8303
  const internalInitialValue = getInternalInitialValue(multipleFilesSupported, value, metadata, size);
7916
8304
  const [initialValue, setInitialValue] = React.useState(value);
7917
8305
  const [internalValue, setInternalValue] = React.useState(internalInitialValue);
@@ -7983,6 +8371,11 @@
7983
8371
  onChange(fieldValue ? fieldValue[0] : null);
7984
8372
  }
7985
8373
  }, [internalValue, multipleFilesSupported, onChange, storage, storageSource]);
8374
+ const onFileUploadError = React.useCallback((entry_0) => {
8375
+ console.debug("onFileUploadError", entry_0);
8376
+ const newValue_0 = internalValue.filter((item) => item.id !== entry_0.id);
8377
+ setInternalValue(newValue_0);
8378
+ }, [internalValue]);
7986
8379
  const onFilesAdded = React.useCallback(async (acceptedFiles) => {
7987
8380
  if (!acceptedFiles.length || disabled) return;
7988
8381
  if (processFile) {
@@ -8001,8 +8394,8 @@
8001
8394
  let newInternalValue;
8002
8395
  if (multipleFilesSupported) {
8003
8396
  newInternalValue = [...internalValue, ...await Promise.all(acceptedFiles.map(async (file_2) => {
8004
- if (compression && compressionFormat(file_2)) {
8005
- file_2 = await resizeAndCompressImage(file_2, compression);
8397
+ if ((imageResize || legacyCompression) && isImageFile(file_2)) {
8398
+ file_2 = await resizeImage(file_2, imageResize, legacyCompression);
8006
8399
  }
8007
8400
  return {
8008
8401
  id: getRandomId$2(),
@@ -8014,8 +8407,8 @@
8014
8407
  }))];
8015
8408
  } else {
8016
8409
  let file_3 = acceptedFiles[0];
8017
- if (compression && compressionFormat(file_3)) {
8018
- file_3 = await resizeAndCompressImage(file_3, compression);
8410
+ if ((imageResize || legacyCompression) && isImageFile(file_3)) {
8411
+ file_3 = await resizeImage(file_3, imageResize, legacyCompression);
8019
8412
  }
8020
8413
  newInternalValue = [{
8021
8414
  id: getRandomId$2(),
@@ -8027,7 +8420,7 @@
8027
8420
  }
8028
8421
  newInternalValue = removeDuplicates(newInternalValue);
8029
8422
  setInternalValue(newInternalValue);
8030
- }, [disabled, fileNameBuilder, internalValue, metadata, multipleFilesSupported, size]);
8423
+ }, [disabled, fileNameBuilder, internalValue, metadata, multipleFilesSupported, size, imageResize, legacyCompression]);
8031
8424
  return {
8032
8425
  internalValue,
8033
8426
  setInternalValue,
@@ -8035,6 +8428,7 @@
8035
8428
  fileNameBuilder,
8036
8429
  storagePathBuilder,
8037
8430
  onFileUploadComplete,
8431
+ onFileUploadError,
8038
8432
  onFilesAdded,
8039
8433
  multipleFilesSupported
8040
8434
  };
@@ -8065,22 +8459,42 @@
8065
8459
  function getRandomId$2() {
8066
8460
  return Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER));
8067
8461
  }
8068
- const supportedTypes = {
8069
- "image/jpeg": "JPEG",
8070
- "image/png": "PNG",
8071
- "image/webp": "WEBP"
8072
- };
8073
- const compressionFormat = (file) => supportedTypes[file.type] ? supportedTypes[file.type] : null;
8074
- const defaultQuality = 100;
8075
- const resizeAndCompressImage = (file, compression) => new Promise((resolve) => {
8076
- const inputQuality = compression.quality === void 0 ? defaultQuality : compression.quality;
8077
- const quality = inputQuality >= 0 ? inputQuality <= 100 ? inputQuality : 100 : 100;
8078
- const format = compressionFormat(file);
8079
- if (!format) {
8080
- throw Error("resizeAndCompressImage: Unsupported image format");
8081
- }
8082
- Resizer.imageFileResizer(file, compression.maxWidth || Number.MAX_VALUE, compression.maxHeight || Number.MAX_VALUE, format, quality, 0, (file2) => resolve(file2), "file");
8083
- });
8462
+ function isImageFile(file) {
8463
+ return file.type === "image/jpeg" || file.type === "image/png" || file.type === "image/webp";
8464
+ }
8465
+ async function resizeImage(file, imageResize, legacyCompression) {
8466
+ const maxWidth = imageResize?.maxWidth ?? legacyCompression?.maxWidth;
8467
+ const maxHeight = imageResize?.maxHeight ?? legacyCompression?.maxHeight;
8468
+ const quality = (imageResize?.quality ?? legacyCompression?.quality ?? 80) / 100;
8469
+ const mode = imageResize?.mode ?? "contain";
8470
+ let mimeType = file.type;
8471
+ if (imageResize?.format && imageResize.format !== "original") {
8472
+ mimeType = `image/${imageResize.format}`;
8473
+ }
8474
+ return new Promise((resolve, reject) => {
8475
+ new Compressor(file, {
8476
+ quality,
8477
+ maxWidth,
8478
+ maxHeight,
8479
+ mimeType,
8480
+ // Use cover mode if specified (crops to fit)
8481
+ // Otherwise use contain mode (scales to fit)
8482
+ ...mode === "cover" || mode === void 0 ? {
8483
+ width: maxWidth,
8484
+ height: maxHeight,
8485
+ resize: "cover"
8486
+ } : {},
8487
+ success: (result) => {
8488
+ const compressedFile = new File([result], file.name, {
8489
+ type: result.type,
8490
+ lastModified: Date.now()
8491
+ });
8492
+ resolve(compressedFile);
8493
+ },
8494
+ error: reject
8495
+ });
8496
+ });
8497
+ }
8084
8498
  function StorageUploadProgress({
8085
8499
  storagePath,
8086
8500
  entry,
@@ -9754,50 +10168,41 @@
9754
10168
  }
9755
10169
  return value;
9756
10170
  }
9757
- if (isLocalStorageAvailable) {
9758
- try {
9759
- for (let i = 0; i < localStorage.length; i++) {
9760
- const fullKey = localStorage.key(i);
9761
- if (fullKey && fullKey.startsWith(LOCAL_STORAGE_PREFIX)) {
9762
- const path = fullKey.substring(LOCAL_STORAGE_PREFIX.length);
9763
- const entityString = localStorage.getItem(fullKey);
9764
- if (entityString) {
9765
- try {
9766
- const entity = JSON.parse(entityString, customReviver);
9767
- entityCache.set(path, entity);
9768
- } catch (parseError) {
9769
- console.error(`Failed to parse entity for path "${path}" from localStorage:`, parseError);
9770
- }
9771
- }
9772
- }
9773
- }
9774
- } catch (error) {
9775
- console.error("Error accessing localStorage during initialization:", error);
9776
- }
9777
- }
9778
10171
  function saveEntityToCache(path, data) {
9779
- entityCache.set(path, data);
9780
10172
  if (isLocalStorageAvailable) {
9781
10173
  try {
9782
10174
  const key = LOCAL_STORAGE_PREFIX + path;
9783
10175
  const entityString = JSON.stringify(data, customReplacer);
10176
+ console.log("Saving entity to localStorage:", {
10177
+ key,
10178
+ entityString
10179
+ });
9784
10180
  localStorage.setItem(key, entityString);
9785
10181
  } catch (error) {
9786
10182
  console.error(`Failed to save entity for path "${path}" to localStorage:`, error);
9787
10183
  }
9788
10184
  }
9789
10185
  }
10186
+ function removeEntityFromMemoryCache(path) {
10187
+ entityCache.delete(path);
10188
+ }
10189
+ function saveEntityToMemoryCache(path, data) {
10190
+ entityCache.set(path, data);
10191
+ }
10192
+ function getEntityFromMemoryCache(path) {
10193
+ return entityCache.get(path);
10194
+ }
9790
10195
  function getEntityFromCache(path) {
9791
- if (entityCache.has(path)) {
9792
- return entityCache.get(path);
9793
- }
9794
10196
  if (isLocalStorageAvailable) {
9795
10197
  try {
9796
10198
  const key = LOCAL_STORAGE_PREFIX + path;
9797
10199
  const entityString = localStorage.getItem(key);
9798
10200
  if (entityString) {
9799
10201
  const entity = JSON.parse(entityString, customReviver);
9800
- entityCache.set(path, entity);
10202
+ console.log("Loaded entity from localStorage:", {
10203
+ key,
10204
+ entity
10205
+ });
9801
10206
  return entity;
9802
10207
  }
9803
10208
  } catch (error) {
@@ -9806,12 +10211,7 @@
9806
10211
  }
9807
10212
  return void 0;
9808
10213
  }
9809
- function hasEntityInCache(path) {
9810
- return entityCache.has(path);
9811
- }
9812
10214
  function removeEntityFromCache(path) {
9813
- console.debug("Removing entity from cache", path);
9814
- entityCache.delete(path);
9815
10215
  if (isLocalStorageAvailable) {
9816
10216
  try {
9817
10217
  const key = LOCAL_STORAGE_PREFIX + path;
@@ -9821,6 +10221,26 @@
9821
10221
  }
9822
10222
  }
9823
10223
  }
10224
+ function flattenKeys(obj, prefix = "", result = []) {
10225
+ if (isObject(obj) || Array.isArray(obj)) {
10226
+ const plainObject = isPlainObject(obj);
10227
+ if (!plainObject && prefix) {
10228
+ result.push(prefix);
10229
+ } else {
10230
+ for (const key in obj) {
10231
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
10232
+ const newKey = prefix ? Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}.${key}` : key;
10233
+ if (isObject(obj[key]) || Array.isArray(obj[key])) {
10234
+ flattenKeys(obj[key], newKey, result);
10235
+ } else {
10236
+ result.push(newKey);
10237
+ }
10238
+ }
10239
+ }
10240
+ }
10241
+ }
10242
+ return result;
10243
+ }
9824
10244
  const EntityCollectionRowActions = function EntityCollectionRowActions2({
9825
10245
  entity,
9826
10246
  collection,
@@ -9848,7 +10268,9 @@
9848
10268
  const hasCollapsedActions = actions.some((a) => a.collapsed || a.collapsed === void 0);
9849
10269
  const collapsedActions = actions.filter((a_0) => a_0.collapsed || a_0.collapsed === void 0);
9850
10270
  const uncollapsedActions = actions.filter((a_1) => a_1.collapsed === false);
9851
- const hasDraft = hasEntityInCache(fullPath + "/" + entity.id);
10271
+ const enableLocalChangesBackup = collection ? getLocalChangesBackup(collection) : false;
10272
+ const hasDraft = enableLocalChangesBackup ? getEntityFromCache(fullPath + "/" + entity.id) : false;
10273
+ const iconSize = largeLayout && (size === "m" || size === "l" || size == "xl") ? "medium" : "small";
9852
10274
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls("h-full flex items-center justify-center flex-col bg-surface-50 dark:bg-surface-900 bg-opacity-90 dark:bg-opacity-90 z-10", frozen ? "sticky left-0" : ""), onClick: React.useCallback((event) => {
9853
10275
  event.stopPropagation();
9854
10276
  }, []), style: {
@@ -9858,23 +10280,31 @@
9858
10280
  contain: "strict"
9859
10281
  }, children: [
9860
10282
  (hasActions || selectionEnabled) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-34 flex justify-center", children: [
9861
- uncollapsedActions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: action.name, asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { onClick: (event_0) => {
9862
- event_0.stopPropagation();
9863
- action.onClick({
9864
- view: "collection",
9865
- entity,
9866
- fullPath,
9867
- fullIdPath,
9868
- collection,
9869
- context,
9870
- selectionController,
9871
- highlightEntity,
9872
- unhighlightEntity,
9873
- onCollectionChange,
9874
- openEntityMode: openEntityMode ?? collection?.openEntityMode
9875
- });
9876
- }, size: largeLayout ? "medium" : "small", children: action.icon }) }, index)),
9877
- hasCollapsedActions && /* @__PURE__ */ jsxRuntime.jsx(ui.Menu, { trigger: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: largeLayout ? "medium" : "small", children: /* @__PURE__ */ jsxRuntime.jsx(ui.MoreVertIcon, {}) }), children: collapsedActions.map((action_0, index_0) => /* @__PURE__ */ jsxRuntime.jsxs(ui.MenuItem, { onClick: (e) => {
10283
+ uncollapsedActions.map((action, index) => {
10284
+ const isEditAction = action.key === "edit";
10285
+ const tooltip = isEditAction && hasDraft ? "Local unsaved changes" : action.name;
10286
+ let iconButton = /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { onClick: (event_0) => {
10287
+ event_0.stopPropagation();
10288
+ action.onClick({
10289
+ view: "collection",
10290
+ entity,
10291
+ fullPath,
10292
+ fullIdPath,
10293
+ collection,
10294
+ context,
10295
+ selectionController,
10296
+ highlightEntity,
10297
+ unhighlightEntity,
10298
+ onCollectionChange,
10299
+ openEntityMode: openEntityMode ?? collection?.openEntityMode
10300
+ });
10301
+ }, size: iconSize, children: action.icon });
10302
+ if (isEditAction && hasDraft) {
10303
+ iconButton = /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "warning", children: iconButton });
10304
+ }
10305
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: tooltip, asChild: true, children: iconButton }, index);
10306
+ }),
10307
+ hasCollapsedActions && /* @__PURE__ */ jsxRuntime.jsx(ui.Menu, { trigger: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: iconSize, children: /* @__PURE__ */ jsxRuntime.jsx(ui.MoreVertIcon, {}) }), children: collapsedActions.map((action_0, index_0) => /* @__PURE__ */ jsxRuntime.jsxs(ui.MenuItem, { onClick: (e) => {
9878
10308
  e.stopPropagation();
9879
10309
  action_0.onClick({
9880
10310
  view: "collection",
@@ -9893,14 +10323,11 @@
9893
10323
  action_0.icon,
9894
10324
  action_0.name
9895
10325
  ] }, index_0)) }),
9896
- selectionEnabled && /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: `Select ${entity.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Checkbox, { size: largeLayout ? "medium" : "small", checked: Boolean(isSelected), onCheckedChange }) })
10326
+ selectionEnabled && /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: `Select ${entity.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Checkbox, { size: iconSize, checked: Boolean(isSelected), onCheckedChange }) })
9897
10327
  ] }),
9898
- !hideId && size !== "xs" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-[138px] overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2 align-center justify-center flex items-center gap-1", onClick: (event_1) => {
10328
+ !hideId && size !== "xs" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[138px] overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2 align-center justify-center flex items-center gap-1", onClick: (event_1) => {
9899
10329
  event_1.stopPropagation();
9900
- }, children: [
9901
- hasDraft && /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "Local unsaved changes", className: "inline", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { colorScheme: "orangeDarker", className: "p-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.EditIcon, { size: 12 }) }) }),
9902
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-center", children: entity ? entity.id : /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, {}) })
9903
- ] })
10330
+ }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-center", children: entity ? entity.id : /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, {}) }) })
9904
10331
  ] });
9905
10332
  };
9906
10333
  function CollectionTableToolbar(t0) {
@@ -11412,7 +11839,7 @@
11412
11839
  if (value_1 !== "") {
11413
11840
  updateFilter(operation, dataType === "number" ? parseInt(value_1) : value_1);
11414
11841
  }
11415
- }, endAdornment: internalValue && /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { className: "absolute right-2 top-3", onClick: (e_0) => updateFilter(operation, void 0), children: /* @__PURE__ */ jsxRuntime.jsx(ui.CloseIcon, {}) }), renderValue: (enumKey) => {
11842
+ }, endAdornment: internalValue && /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { onClick: (e_0) => updateFilter(operation, void 0), children: /* @__PURE__ */ jsxRuntime.jsx(ui.CloseIcon, {}) }), renderValue: (enumKey) => {
11416
11843
  if (enumKey === null) {
11417
11844
  return "Filter for null values";
11418
11845
  }
@@ -11569,7 +11996,7 @@
11569
11996
  };
11570
11997
  const multipleSelectOperations = ["array-contains-any", "in"];
11571
11998
  function DateTimeFilterField(t0) {
11572
- const $ = reactCompilerRuntime.c(38);
11999
+ const $ = reactCompilerRuntime.c(39);
11573
12000
  const {
11574
12001
  isArray,
11575
12002
  mode,
@@ -11610,7 +12037,7 @@
11610
12037
  newValue = newOpIsArray ? val ? [val] : [] : "";
11611
12038
  }
11612
12039
  setOperation(op);
11613
- setInternalValue(newValue === null ? void 0 : newValue);
12040
+ setInternalValue(newValue);
11614
12041
  const hasNewValue = newValue !== null && Array.isArray(newValue) ? newValue.length > 0 : newValue !== void 0;
11615
12042
  if (op && hasNewValue) {
11616
12043
  setValue([op, newValue]);
@@ -11654,83 +12081,85 @@
11654
12081
  } else {
11655
12082
  t6 = $[16];
11656
12083
  }
11657
- const t7 = internalValue ?? void 0;
11658
- let t8;
12084
+ const t7 = internalValue === null;
12085
+ const t8 = internalValue ?? void 0;
12086
+ let t9;
11659
12087
  if ($[17] !== operation || $[18] !== updateFilter) {
11660
- t8 = (dateValue) => {
12088
+ t9 = (dateValue) => {
11661
12089
  updateFilter(operation, dateValue === null ? void 0 : dateValue);
11662
12090
  };
11663
12091
  $[17] = operation;
11664
12092
  $[18] = updateFilter;
11665
- $[19] = t8;
12093
+ $[19] = t9;
11666
12094
  } else {
11667
- t8 = $[19];
12095
+ t9 = $[19];
11668
12096
  }
11669
- let t9;
11670
- if ($[20] !== locale || $[21] !== mode || $[22] !== t7 || $[23] !== t8) {
11671
- t9 = /* @__PURE__ */ jsxRuntime.jsx(ui.DateTimeField, { mode, size: "large", locale, value: t7, onChange: t8, clearable: true });
12097
+ let t10;
12098
+ if ($[20] !== locale || $[21] !== mode || $[22] !== t7 || $[23] !== t8 || $[24] !== t9) {
12099
+ t10 = /* @__PURE__ */ jsxRuntime.jsx(ui.DateTimeField, { mode, size: "large", locale, disabled: t7, value: t8, onChange: t9, clearable: true });
11672
12100
  $[20] = locale;
11673
12101
  $[21] = mode;
11674
12102
  $[22] = t7;
11675
12103
  $[23] = t8;
11676
12104
  $[24] = t9;
12105
+ $[25] = t10;
11677
12106
  } else {
11678
- t9 = $[24];
12107
+ t10 = $[25];
11679
12108
  }
11680
- const t10 = internalValue === null;
11681
- let t11;
11682
- if ($[25] !== internalValue || $[26] !== operation || $[27] !== updateFilter) {
11683
- t11 = (checked) => {
12109
+ const t11 = internalValue === null;
12110
+ let t12;
12111
+ if ($[26] !== internalValue || $[27] !== operation || $[28] !== updateFilter) {
12112
+ t12 = (checked) => {
11684
12113
  if (internalValue !== null) {
11685
12114
  updateFilter(operation, null);
11686
12115
  } else {
11687
12116
  updateFilter(operation, void 0);
11688
12117
  }
11689
12118
  };
11690
- $[25] = internalValue;
11691
- $[26] = operation;
11692
- $[27] = updateFilter;
11693
- $[28] = t11;
12119
+ $[26] = internalValue;
12120
+ $[27] = operation;
12121
+ $[28] = updateFilter;
12122
+ $[29] = t12;
11694
12123
  } else {
11695
- t11 = $[28];
12124
+ t12 = $[29];
11696
12125
  }
11697
- let t12;
11698
- if ($[29] !== t10 || $[30] !== t11) {
11699
- t12 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { className: "border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-surface-100 dark:[&:has(:checked)]:bg-surface-800", htmlFor: "null-filter", children: [
11700
- /* @__PURE__ */ jsxRuntime.jsx(ui.Checkbox, { id: "null-filter", checked: t10, size: "small", onCheckedChange: t11 }),
12126
+ let t13;
12127
+ if ($[30] !== t11 || $[31] !== t12) {
12128
+ t13 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { className: "border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-surface-100 dark:[&:has(:checked)]:bg-surface-800", htmlFor: "null-filter", children: [
12129
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Checkbox, { id: "null-filter", checked: t11, size: "small", onCheckedChange: t12 }),
11701
12130
  "Filter for null values"
11702
12131
  ] });
11703
- $[29] = t10;
11704
12132
  $[30] = t11;
11705
12133
  $[31] = t12;
12134
+ $[32] = t13;
11706
12135
  } else {
11707
- t12 = $[31];
12136
+ t13 = $[32];
11708
12137
  }
11709
- let t13;
11710
- if ($[32] !== t12 || $[33] !== t9) {
11711
- t13 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow ml-2 flex flex-col gap-2", children: [
11712
- t9,
11713
- t12
12138
+ let t14;
12139
+ if ($[33] !== t10 || $[34] !== t13) {
12140
+ t14 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow ml-2 flex flex-col gap-2", children: [
12141
+ t10,
12142
+ t13
11714
12143
  ] });
11715
- $[32] = t12;
11716
- $[33] = t9;
12144
+ $[33] = t10;
11717
12145
  $[34] = t13;
12146
+ $[35] = t14;
11718
12147
  } else {
11719
- t13 = $[34];
12148
+ t14 = $[35];
11720
12149
  }
11721
- let t14;
11722
- if ($[35] !== t13 || $[36] !== t6) {
11723
- t14 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-[440px]", children: [
12150
+ let t15;
12151
+ if ($[36] !== t14 || $[37] !== t6) {
12152
+ t15 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-[440px]", children: [
11724
12153
  t6,
11725
- t13
12154
+ t14
11726
12155
  ] });
11727
- $[35] = t13;
11728
- $[36] = t6;
11729
- $[37] = t14;
12156
+ $[36] = t14;
12157
+ $[37] = t6;
12158
+ $[38] = t15;
11730
12159
  } else {
11731
- t14 = $[37];
12160
+ t15 = $[38];
11732
12161
  }
11733
- return t14;
12162
+ return t15;
11734
12163
  }
11735
12164
  function _temp2$5(op_1) {
11736
12165
  return /* @__PURE__ */ jsxRuntime.jsx(ui.SelectItem, { value: op_1, children: operationLabels[op_1] }, op_1);
@@ -13875,14 +14304,16 @@
13875
14304
  return t5;
13876
14305
  }
13877
14306
  function useHomePageDnd({
13878
- items: dndItems,
13879
- setItems: setDndItems,
14307
+ items,
14308
+ setItems,
13880
14309
  disabled,
13881
14310
  onCardMovedBetweenGroups,
13882
14311
  onGroupMoved,
13883
14312
  onNewGroupDrop,
13884
14313
  onPersist
13885
14314
  }) {
14315
+ const dndItems = items;
14316
+ const setDndItems = setItems;
13886
14317
  const [activeId, setActiveId] = React.useState(null);
13887
14318
  const [activeIsGroup, setActiveIsGroup] = React.useState(false);
13888
14319
  const [currentDraggingGroupId, setCurrentDraggingGroupId] = React.useState(null);
@@ -13890,6 +14321,9 @@
13890
14321
  const [isDraggingCardOnly, setIsDraggingCardOnly] = React.useState(false);
13891
14322
  const [dialogOpenForGroup, setDialogOpenForGroup] = React.useState(null);
13892
14323
  const [isHoveringNewGroupDropZone, setIsHoveringNewGroupDropZone] = React.useState(false);
14324
+ const [pendingNewGroupName, setPendingNewGroupName] = React.useState(null);
14325
+ const [stateBeforeNewGroup, setStateBeforeNewGroup] = React.useState(null);
14326
+ const preDragItemsRef = React.useRef(null);
13893
14327
  const interimItemsRef = React.useRef(null);
13894
14328
  React.useEffect(() => {
13895
14329
  interimItemsRef.current = dndItems;
@@ -13981,6 +14415,7 @@
13981
14415
  }) => {
13982
14416
  setDndKitActiveNode(active);
13983
14417
  if (disabled) return;
14418
+ preDragItemsRef.current = cloneItemsForDnd(dndItems);
13984
14419
  const isGroup = dndItems.some((g_7) => g_7.name === active.id);
13985
14420
  if (!active.data.current) active.data.current = {};
13986
14421
  active.data.current.type = isGroup ? "group" : "item";
@@ -14004,18 +14439,30 @@
14004
14439
  if (!activeCont) return;
14005
14440
  if (overCont && activeCont !== overCont) {
14006
14441
  recentlyMovedToNewContainer.current = true;
14007
- const newState = cloneItemsForDnd(dndItems);
14008
- const srcIdx = newState.findIndex((g_8) => g_8.name === activeCont);
14009
- const tgtIdx = newState.findIndex((g_9) => g_9.name === overCont);
14010
- if (srcIdx === -1 || tgtIdx === -1) return;
14011
- const src = newState[srcIdx];
14012
- const tgt = newState[tgtIdx];
14013
- const idxInSrc = src.entries.findIndex((e_2) => e_2.url === activeIdNow);
14014
- if (idxInSrc === -1) return;
14015
- const [moved] = src.entries.splice(idxInSrc, 1);
14016
- tgt.entries.push(moved);
14017
- interimItemsRef.current = newState;
14018
- setDndItems(newState);
14442
+ lastOverId.current = overIdNow;
14443
+ setDndItems((current) => {
14444
+ const newState = cloneItemsForDnd(current);
14445
+ const srcIdx = newState.findIndex((g_8) => g_8.name === activeCont);
14446
+ const tgtIdx = newState.findIndex((g_9) => g_9.name === overCont);
14447
+ if (srcIdx === -1 || tgtIdx === -1) return current;
14448
+ const src = newState[srcIdx];
14449
+ const tgt = newState[tgtIdx];
14450
+ const idxInSrc = src.entries.findIndex((e_2) => e_2.url === activeIdNow);
14451
+ if (idxInSrc === -1) return current;
14452
+ const [moved] = src.entries.splice(idxInSrc, 1);
14453
+ const overIsContainer_0 = overIdNow === overCont;
14454
+ if (overIsContainer_0) {
14455
+ tgt.entries.push(moved);
14456
+ } else {
14457
+ const overIdx = tgt.entries.findIndex((e_3) => e_3.url === overIdNow);
14458
+ if (overIdx !== -1) {
14459
+ tgt.entries.splice(overIdx, 0, moved);
14460
+ } else {
14461
+ tgt.entries.push(moved);
14462
+ }
14463
+ }
14464
+ return newState;
14465
+ });
14019
14466
  } else if (activeCont === overCont) {
14020
14467
  recentlyMovedToNewContainer.current = false;
14021
14468
  }
@@ -14042,55 +14489,101 @@
14042
14489
  }
14043
14490
  }
14044
14491
  } else {
14045
- const activeCont_0 = findDndContainer(activeIdNow_0);
14492
+ const findContainerInState = (id_0, state) => {
14493
+ const group_0 = state.find((g_13) => g_13.name === id_0);
14494
+ if (group_0) return group_0.name;
14495
+ for (const g_14 of state) {
14496
+ if (g_14.entries.some((e_4) => e_4.url === id_0)) return g_14.name;
14497
+ }
14498
+ return void 0;
14499
+ };
14500
+ const sourceState = preDragItemsRef.current || dndItems;
14501
+ const activeCont_0 = findContainerInState(activeIdNow_0, sourceState);
14502
+ findDndContainer(overIdNow_0);
14046
14503
  if (overIdNow_0 === "new-group-drop-zone") {
14047
14504
  if (activeCont_0) {
14505
+ setStateBeforeNewGroup(cloneItemsForDnd(dndItems));
14048
14506
  const newState_1 = cloneItemsForDnd(dndItems);
14049
- const srcIdx_0 = newState_1.findIndex((g_13) => g_13.name === activeCont_0);
14507
+ const srcIdx_0 = newState_1.findIndex((g_15) => g_15.name === activeCont_0);
14050
14508
  if (srcIdx_0 !== -1) {
14051
14509
  const src_0 = newState_1[srcIdx_0];
14052
- const idxInSrc_0 = src_0.entries.findIndex((e_3) => e_3.url === activeIdNow_0);
14510
+ const idxInSrc_0 = src_0.entries.findIndex((e_5) => e_5.url === activeIdNow_0);
14053
14511
  if (idxInSrc_0 !== -1) {
14054
14512
  const [dragged] = src_0.entries.splice(idxInSrc_0, 1);
14055
14513
  if (src_0.entries.length === 0) newState_1.splice(srcIdx_0, 1);
14056
14514
  let tentative = "New Group";
14057
14515
  let counter = 1;
14058
- while (newState_1.some((g_14) => g_14.name === tentative)) tentative = `New Group ${counter++}`;
14516
+ while (newState_1.some((g_16) => g_16.name === tentative)) tentative = `New Group ${counter++}`;
14059
14517
  newState_1.push({
14060
14518
  name: tentative,
14061
14519
  entries: [dragged]
14062
14520
  });
14063
14521
  setDndItems(newState_1);
14064
- onPersist?.(newState_1);
14522
+ setPendingNewGroupName(tentative);
14065
14523
  setDialogOpenForGroup(tentative);
14066
14524
  onNewGroupDrop?.();
14067
14525
  }
14068
14526
  }
14069
14527
  }
14070
14528
  } else {
14071
- const overCont_0 = findDndContainer(overIdNow_0);
14072
- if (activeCont_0 === overCont_0) {
14073
- const grpIdx = dndItems.findIndex((g_15) => g_15.name === activeCont_0);
14529
+ const overCont_1 = findDndContainer(overIdNow_0);
14530
+ if (activeCont_0 === overCont_1) {
14531
+ const grpIdx = dndItems.findIndex((g_17) => g_17.name === activeCont_0);
14074
14532
  if (grpIdx !== -1) {
14075
- const group_0 = dndItems[grpIdx];
14076
- const oldIdx = group_0.entries.findIndex((e_4) => e_4.url === activeIdNow_0);
14077
- let newIdx = group_0.entries.findIndex((e_5) => e_5.url === overIdNow_0);
14078
- if (newIdx === -1 && overIdNow_0 === activeCont_0) newIdx = group_0.entries.length - 1;
14533
+ const group_1 = dndItems[grpIdx];
14534
+ const oldIdx = group_1.entries.findIndex((e_6) => e_6.url === activeIdNow_0);
14535
+ let newIdx = group_1.entries.findIndex((e_7) => e_7.url === overIdNow_0);
14536
+ if (newIdx === -1 && overIdNow_0 === activeCont_0) newIdx = group_1.entries.length - 1;
14079
14537
  if (oldIdx !== -1 && newIdx !== -1 && oldIdx !== newIdx) {
14080
- const reordered = sortable.arrayMove(group_0.entries, oldIdx, newIdx);
14538
+ const reordered = sortable.arrayMove(group_1.entries, oldIdx, newIdx);
14081
14539
  const newState_2 = [...dndItems];
14082
14540
  newState_2[grpIdx] = {
14083
- ...group_0,
14541
+ ...group_1,
14084
14542
  entries: reordered
14085
14543
  };
14086
14544
  setDndItems(newState_2);
14087
14545
  onPersist?.(newState_2);
14088
14546
  }
14089
14547
  }
14090
- } else if (recentlyMovedToNewContainer.current && interimItemsRef.current) {
14091
- onPersist?.(interimItemsRef.current);
14548
+ } else if (overCont_1 && activeCont_0 !== overCont_1) {
14549
+ const finalState = cloneItemsForDnd(sourceState);
14550
+ const finalOverId = lastOverId.current || overIdNow_0;
14551
+ const cleanOverCont = findContainerInState(finalOverId, sourceState) || overCont_1;
14552
+ const srcIdx_1 = finalState.findIndex((g_18) => g_18.name === activeCont_0);
14553
+ const tgtIdx_0 = finalState.findIndex((g_19) => g_19.name === cleanOverCont);
14554
+ if (srcIdx_1 !== -1 && tgtIdx_0 !== -1) {
14555
+ const src_1 = finalState[srcIdx_1];
14556
+ const tgt_0 = finalState[tgtIdx_0];
14557
+ const idxInSrc_1 = src_1.entries.findIndex((e_8) => e_8.url === activeIdNow_0);
14558
+ if (idxInSrc_1 !== -1) {
14559
+ const [moved_0] = src_1.entries.splice(idxInSrc_1, 1);
14560
+ const overIsContainer_1 = finalOverId === cleanOverCont;
14561
+ if (overIsContainer_1) {
14562
+ tgt_0.entries.push(moved_0);
14563
+ } else {
14564
+ const overIdx_0 = tgt_0.entries.findIndex((e_9) => e_9.url === finalOverId);
14565
+ if (overIdx_0 !== -1) {
14566
+ tgt_0.entries.splice(overIdx_0, 0, moved_0);
14567
+ } else {
14568
+ tgt_0.entries.push(moved_0);
14569
+ }
14570
+ }
14571
+ if (src_1.entries.length === 0) {
14572
+ finalState.splice(srcIdx_1, 1);
14573
+ }
14574
+ setDndItems(finalState);
14575
+ onPersist?.(finalState);
14576
+ onCardMovedBetweenGroups?.(moved_0);
14577
+ }
14578
+ }
14579
+ } else if (recentlyMovedToNewContainer.current) {
14580
+ console.error("Move between containers detected but conditions not met", {
14581
+ activeCont: activeCont_0,
14582
+ overCont: overCont_1,
14583
+ activeIdNow: activeIdNow_0,
14584
+ overIdNow: overIdNow_0
14585
+ });
14092
14586
  }
14093
- onCardMovedBetweenGroups?.(dndItems.flatMap((g_16) => g_16.entries).find((e_6) => e_6.url === activeIdNow_0));
14094
14587
  }
14095
14588
  }
14096
14589
  resetDragState();
@@ -14105,11 +14598,11 @@
14105
14598
  };
14106
14599
  const handleDragCancel = () => resetDragState();
14107
14600
  const handleRenameGroup = (oldName, newName) => {
14108
- setDndItems((current) => {
14109
- const idx = current.findIndex((g_17) => g_17.name === oldName);
14110
- if (idx === -1) return current;
14111
- if (current.some((g_18) => g_18.name === newName && g_18.name !== oldName)) return current;
14112
- const updated = [...current];
14601
+ setDndItems((current_0) => {
14602
+ const idx = current_0.findIndex((g_20) => g_20.name === oldName);
14603
+ if (idx === -1) return current_0;
14604
+ if (current_0.some((g_21) => g_21.name === newName && g_21.name !== oldName)) return current_0;
14605
+ const updated = [...current_0];
14113
14606
  updated[idx] = {
14114
14607
  ...updated[idx],
14115
14608
  name: newName
@@ -14117,10 +14610,20 @@
14117
14610
  onPersist?.(updated);
14118
14611
  return updated;
14119
14612
  });
14613
+ setPendingNewGroupName(null);
14614
+ setStateBeforeNewGroup(null);
14615
+ setDialogOpenForGroup(null);
14616
+ };
14617
+ const handleDialogClose = () => {
14618
+ if (pendingNewGroupName && dialogOpenForGroup === pendingNewGroupName && stateBeforeNewGroup) {
14619
+ setDndItems(stateBeforeNewGroup);
14620
+ }
14621
+ setPendingNewGroupName(null);
14622
+ setStateBeforeNewGroup(null);
14120
14623
  setDialogOpenForGroup(null);
14121
14624
  };
14122
- const activeItemForOverlay = disabled || !activeId || activeIsGroup ? null : dndItems.flatMap((g_19) => g_19.entries).find((e_7) => e_7.url === activeId) || null;
14123
- const activeGroupData = disabled || !activeId || !activeIsGroup ? null : dndItems.find((g_20) => g_20.name === activeId) || null;
14625
+ const activeItemForOverlay = disabled || !activeId || activeIsGroup ? null : dndItems.flatMap((g_22) => g_22.entries).find((e_10) => e_10.url === activeId) || null;
14626
+ const activeGroupData = disabled || !activeId || !activeIsGroup ? null : dndItems.find((g_23) => g_23.name === activeId) || null;
14124
14627
  return {
14125
14628
  sensors,
14126
14629
  collisionDetection,
@@ -14138,6 +14641,7 @@
14138
14641
  dialogOpenForGroup,
14139
14642
  setDialogOpenForGroup,
14140
14643
  handleRenameGroup,
14644
+ handleDialogClose,
14141
14645
  isHoveringNewGroupDropZone,
14142
14646
  setIsHoveringNewGroupDropZone
14143
14647
  };
@@ -14515,7 +15019,7 @@
14515
15019
  entries: []
14516
15020
  });
14517
15021
  }
14518
- allProcessed = allProcessed.filter((g_4) => g_4.entries.length || groupOrderFromNavController.includes(g_4.name) || g_4.name === DEFAULT_GROUP_NAME && hasPluginAdditionalCards);
15022
+ allProcessed = allProcessed.filter((g_4) => g_4.entries.length || g_4.name === DEFAULT_GROUP_NAME && hasPluginAdditionalCards);
14519
15023
  }
14520
15024
  const admin = allProcessed.find((g_5) => g_5.name === ADMIN_GROUP_NAME);
14521
15025
  return {
@@ -14563,6 +15067,7 @@
14563
15067
  dialogOpenForGroup,
14564
15068
  setDialogOpenForGroup,
14565
15069
  handleRenameGroup,
15070
+ handleDialogClose,
14566
15071
  isHoveringNewGroupDropZone,
14567
15072
  setIsHoveringNewGroupDropZone
14568
15073
  } = useHomePageDnd({
@@ -14570,7 +15075,6 @@
14570
15075
  setItems: updateItems,
14571
15076
  disabled: !allowDragAndDrop || performingSearch,
14572
15077
  onPersist: persistNavigationGroups,
14573
- // ——► persistence here
14574
15078
  onGroupMoved: (g_8) => context.analyticsController?.onAnalyticsEvent?.("home_move_group", {
14575
15079
  name: g_8
14576
15080
  }),
@@ -14616,7 +15120,7 @@
14616
15120
  frequency: 500
14617
15121
  }
14618
15122
  }, onDragStart, onDragOver, onDragEnd, onDragCancel, modifiers: dndModifiers, children: [
14619
- /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items: containers, strategy: sortable.verticalListSortingStrategy, children: items.map((groupData) => {
15123
+ /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items: containers, strategy: sortable.verticalListSortingStrategy, children: items.map((groupData, groupIndex) => {
14620
15124
  const groupKey = groupData.name;
14621
15125
  const entriesInGroup = groupData.entries;
14622
15126
  const AdditionalCards = [];
@@ -14627,7 +15131,7 @@
14627
15131
  group: groupKey === DEFAULT_GROUP_NAME ? void 0 : groupKey,
14628
15132
  context
14629
15133
  };
14630
- if (entriesInGroup.length === 0 && (AdditionalCards.length === 0 || performingSearch) && !groupOrderFromNavController.includes(groupKey)) return null;
15134
+ if (entriesInGroup.length === 0 && (AdditionalCards.length === 0 || performingSearch)) return null;
14631
15135
  return /* @__PURE__ */ jsxRuntime.jsx(SortableNavigationGroup, { groupName: groupKey, disabled: dndDisabled, children: /* @__PURE__ */ jsxRuntime.jsx(NavigationGroup, { group: groupKey === DEFAULT_GROUP_NAME ? void 0 : groupKey, minimised: draggingGroupId === groupKey && !isDraggingCardOnly, isPotentialCardDropTarget: isDraggingCardOnly, dndDisabled, onEditGroup: () => {
14632
15136
  if (dndDisabled) return;
14633
15137
  setDialogOpenForGroup(groupKey);
@@ -14642,7 +15146,7 @@
14642
15146
  });
14643
15147
  } }, entry.url)),
14644
15148
  !performingSearch && groupKey.toLowerCase() !== ADMIN_GROUP_NAME.toLowerCase() && AdditionalCards.map((C, i_1) => /* @__PURE__ */ jsxRuntime.jsx(C, { ...actionProps }, `extra_${groupKey}_${i_1}`))
14645
- ] }) }) }) }, groupKey);
15149
+ ] }) }) }) }, `group-${groupIndex}`);
14646
15150
  }) }, JSON.stringify(containers)),
14647
15151
  /* @__PURE__ */ jsxRuntime.jsx(NewGroupDropZone, { disabled: dndDisabled, setIsHovering: setIsHoveringNewGroupDropZone }),
14648
15152
  /* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { adjustScale: false, dropAnimation, children: activeGroupData && draggingGroupId === activeGroupData.name ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-transparent", style: {
@@ -14663,7 +15167,7 @@
14663
15167
  additionalPluginChildrenEnd,
14664
15168
  additionalChildrenEnd
14665
15169
  ] }),
14666
- dialogOpenForGroup && /* @__PURE__ */ jsxRuntime.jsx(RenameGroupDialog, { open: true, initialName: dialogOpenForGroup, existingGroupNames: items.map((g_9) => g_9.name).filter((n) => n !== dialogOpenForGroup), onClose: () => setDialogOpenForGroup(null), onRename: (newName) => {
15170
+ dialogOpenForGroup && /* @__PURE__ */ jsxRuntime.jsx(RenameGroupDialog, { open: true, initialName: dialogOpenForGroup, existingGroupNames: items.map((g_9) => g_9.name).filter((n) => n !== dialogOpenForGroup), onClose: handleDialogClose, onRename: (newName) => {
14667
15171
  handleRenameGroup(dialogOpenForGroup, newName);
14668
15172
  } })
14669
15173
  ] });
@@ -14987,61 +15491,60 @@
14987
15491
  ] });
14988
15492
  }
14989
15493
  const ErrorFocus = (t0) => {
14990
- const $ = reactCompilerRuntime.c(6);
15494
+ const $ = reactCompilerRuntime.c(10);
14991
15495
  const {
14992
15496
  containerRef
14993
15497
  } = t0;
14994
15498
  const {
14995
- isSubmitting,
14996
15499
  isValidating,
14997
- errors
15500
+ errors,
15501
+ version
14998
15502
  } = formex.useFormex();
15503
+ const prevVersion = React.useRef(version);
14999
15504
  let t1;
15000
- let t2;
15001
- if ($[0] !== containerRef || $[1] !== errors || $[2] !== isSubmitting || $[3] !== isValidating) {
15505
+ if ($[0] !== containerRef?.current || $[1] !== errors || $[2] !== isValidating || $[3] !== version) {
15002
15506
  t1 = () => {
15507
+ if (version === prevVersion.current) {
15508
+ return;
15509
+ }
15003
15510
  const keys = Object.keys(errors);
15004
- if (keys.length > 0 && isSubmitting && !isValidating) {
15511
+ if (!isValidating && keys.length > 0) {
15005
15512
  const errorElement = containerRef?.current?.querySelector(`#form_field_${keys[0]}`);
15006
- if (errorElement && containerRef?.current) {
15007
- const scrollableParent = getScrollableParent(containerRef.current);
15008
- if (scrollableParent) {
15009
- const top = errorElement.getBoundingClientRect().top;
15010
- scrollableParent.scrollTo({
15011
- top: scrollableParent.scrollTop + top - 196,
15012
- behavior: "smooth"
15013
- });
15014
- }
15513
+ if (errorElement) {
15514
+ errorElement.scrollIntoView({
15515
+ behavior: "smooth",
15516
+ block: "center"
15517
+ });
15015
15518
  const input = errorElement.querySelector("input");
15016
15519
  if (input) {
15017
15520
  input.focus();
15018
15521
  }
15019
15522
  }
15523
+ prevVersion.current = version;
15020
15524
  }
15021
15525
  };
15022
- t2 = [isSubmitting, isValidating, errors, containerRef];
15023
- $[0] = containerRef;
15526
+ $[0] = containerRef?.current;
15024
15527
  $[1] = errors;
15025
- $[2] = isSubmitting;
15026
- $[3] = isValidating;
15528
+ $[2] = isValidating;
15529
+ $[3] = version;
15027
15530
  $[4] = t1;
15028
- $[5] = t2;
15029
15531
  } else {
15030
15532
  t1 = $[4];
15031
- t2 = $[5];
15533
+ }
15534
+ let t2;
15535
+ if ($[5] !== containerRef || $[6] !== errors || $[7] !== isValidating || $[8] !== version) {
15536
+ t2 = [isValidating, errors, containerRef, version];
15537
+ $[5] = containerRef;
15538
+ $[6] = errors;
15539
+ $[7] = isValidating;
15540
+ $[8] = version;
15541
+ $[9] = t2;
15542
+ } else {
15543
+ t2 = $[9];
15032
15544
  }
15033
15545
  React.useEffect(t1, t2);
15034
15546
  return null;
15035
15547
  };
15036
- const isScrollable = (ele) => {
15037
- const hasScrollableContent = ele && ele.scrollHeight > ele.clientHeight;
15038
- const overflowYStyle = ele ? window.getComputedStyle(ele).overflowY : null;
15039
- const isOverflowHidden = overflowYStyle && overflowYStyle.indexOf("hidden") !== -1;
15040
- return hasScrollableContent && !isOverflowHidden;
15041
- };
15042
- const getScrollableParent = (ele) => {
15043
- return !ele || ele === document.body ? document.body : isScrollable(ele) ? ele : getScrollableParent(ele.parentNode);
15044
- };
15045
15548
  function EntityFormActions(t0) {
15046
15549
  const $ = reactCompilerRuntime.c(16);
15047
15550
  const {
@@ -15062,7 +15565,7 @@
15062
15565
  const context = useFireCMSContext();
15063
15566
  const sideEntityController = useSideEntityController();
15064
15567
  let t1;
15065
- if ($[0] !== collection || $[1] !== context || $[2] !== disabled || $[3] !== entity || $[4] !== formContext || $[5] !== formex2.isSubmitting || $[6] !== fullIdPath || $[7] !== fullPath || $[8] !== layout || $[9] !== navigateBack || $[10] !== openEntityMode || $[11] !== pluginActions || $[12] !== savingError || $[13] !== sideEntityController || $[14] !== status) {
15568
+ if ($[0] !== collection || $[1] !== context || $[2] !== disabled || $[3] !== entity || $[4] !== formContext || $[5] !== formex2 || $[6] !== fullIdPath || $[7] !== fullPath || $[8] !== layout || $[9] !== navigateBack || $[10] !== openEntityMode || $[11] !== pluginActions || $[12] !== savingError || $[13] !== sideEntityController || $[14] !== status) {
15066
15569
  t1 = layout === "bottom" ? buildBottomActions$1({
15067
15570
  fullPath,
15068
15571
  fullIdPath,
@@ -15071,13 +15574,13 @@
15071
15574
  collection,
15072
15575
  context,
15073
15576
  sideEntityController,
15074
- isSubmitting: formex2.isSubmitting,
15075
15577
  disabled,
15076
15578
  status,
15077
15579
  pluginActions,
15078
15580
  openEntityMode,
15079
15581
  navigateBack,
15080
- formContext
15582
+ formContext,
15583
+ formex: formex2
15081
15584
  }) : buildSideActions$1({
15082
15585
  fullPath,
15083
15586
  fullIdPath,
@@ -15086,18 +15589,18 @@
15086
15589
  collection,
15087
15590
  context,
15088
15591
  sideEntityController,
15089
- isSubmitting: formex2.isSubmitting,
15090
15592
  disabled,
15091
15593
  status,
15092
15594
  pluginActions,
15093
- openEntityMode
15595
+ openEntityMode,
15596
+ formex: formex2
15094
15597
  });
15095
15598
  $[0] = collection;
15096
15599
  $[1] = context;
15097
15600
  $[2] = disabled;
15098
15601
  $[3] = entity;
15099
15602
  $[4] = formContext;
15100
- $[5] = formex2.isSubmitting;
15603
+ $[5] = formex2;
15101
15604
  $[6] = fullIdPath;
15102
15605
  $[7] = fullPath;
15103
15606
  $[8] = layout;
@@ -15122,14 +15625,15 @@
15122
15625
  collection,
15123
15626
  context,
15124
15627
  sideEntityController,
15125
- isSubmitting,
15126
15628
  disabled,
15127
15629
  status,
15128
15630
  pluginActions,
15129
15631
  openEntityMode,
15130
15632
  navigateBack,
15131
- formContext
15633
+ formContext,
15634
+ formex: formex2
15132
15635
  }) {
15636
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
15133
15637
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { position: "absolute", children: [
15134
15638
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) }),
15135
15639
  entity && (formActions ?? []).length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow flex overflow-auto no-scrollbar", children: (formActions ?? []).map((action) => /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { color: "primary", onClick: (event) => {
@@ -15148,8 +15652,8 @@
15148
15652
  });
15149
15653
  }, children: action.icon }, action.name)) }),
15150
15654
  pluginActions,
15151
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || isSubmitting, color: "primary", type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15152
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "filled", color: "primary", type: "submit", disabled: disabled || isSubmitting, children: [
15655
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || formex2.isSubmitting, color: "primary", type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15656
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "filled", color: "primary", type: "submit", disabled: disabled || formex2.isSubmitting, startIcon: hasErrors ? /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, {}) : void 0, children: [
15153
15657
  status === "existing" && "Save",
15154
15658
  status === "copy" && "Create copy",
15155
15659
  status === "new" && "Create"
@@ -15166,22 +15670,344 @@
15166
15670
  collection,
15167
15671
  context,
15168
15672
  sideEntityController,
15169
- isSubmitting,
15170
15673
  disabled,
15171
15674
  status,
15172
- pluginActions
15675
+ pluginActions,
15676
+ formex: formex2
15173
15677
  }) {
15678
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
15174
15679
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", ui.defaultBorderMixin), children: [
15175
- /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", disabled: disabled || isSubmitting, children: [
15680
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", startIcon: hasErrors ? /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, {}) : void 0, disabled: disabled || formex2.isSubmitting, children: [
15176
15681
  status === "existing" && "Save",
15177
15682
  status === "copy" && "Create copy",
15178
15683
  status === "new" && "Create"
15179
15684
  ] }),
15180
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15685
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || formex2.isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15181
15686
  pluginActions,
15182
15687
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) })
15183
15688
  ] });
15184
15689
  }
15690
+ function LocalChangesMenu(t0) {
15691
+ const $ = reactCompilerRuntime.c(42);
15692
+ const {
15693
+ localChangesData,
15694
+ formex: formex2,
15695
+ onClearLocalChanges,
15696
+ cacheKey,
15697
+ properties
15698
+ } = t0;
15699
+ const snackbarController = useSnackbarController();
15700
+ const [previewDialogOpen, setPreviewDialogOpen] = React.useState(false);
15701
+ const [open, setOpen] = React.useState(false);
15702
+ let t1;
15703
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
15704
+ t1 = () => setOpen(true);
15705
+ $[0] = t1;
15706
+ } else {
15707
+ t1 = $[0];
15708
+ }
15709
+ const handleOpenMenu = t1;
15710
+ let t2;
15711
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
15712
+ t2 = () => setOpen(false);
15713
+ $[1] = t2;
15714
+ } else {
15715
+ t2 = $[1];
15716
+ }
15717
+ const handleCloseMenu = t2;
15718
+ let t3;
15719
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
15720
+ t3 = () => {
15721
+ setPreviewDialogOpen(true);
15722
+ handleCloseMenu();
15723
+ };
15724
+ $[2] = t3;
15725
+ } else {
15726
+ t3 = $[2];
15727
+ }
15728
+ const handlePreview = t3;
15729
+ let t4;
15730
+ if ($[3] !== formex2 || $[4] !== localChangesData || $[5] !== onClearLocalChanges || $[6] !== snackbarController) {
15731
+ t4 = () => {
15732
+ const mergedValues = mergeDeep(formex2.values, localChangesData);
15733
+ const touched = {
15734
+ ...formex2.touched
15735
+ };
15736
+ const previewKeys = flattenKeys(localChangesData);
15737
+ previewKeys.forEach((key) => {
15738
+ touched[key] = true;
15739
+ });
15740
+ formex2.setTouched(touched);
15741
+ formex2.setValues(mergedValues);
15742
+ snackbarController.open({
15743
+ type: "info",
15744
+ message: "Local changes applied to the form"
15745
+ });
15746
+ handleCloseMenu();
15747
+ onClearLocalChanges?.();
15748
+ };
15749
+ $[3] = formex2;
15750
+ $[4] = localChangesData;
15751
+ $[5] = onClearLocalChanges;
15752
+ $[6] = snackbarController;
15753
+ $[7] = t4;
15754
+ } else {
15755
+ t4 = $[7];
15756
+ }
15757
+ const handleApply = t4;
15758
+ let t5;
15759
+ if ($[8] !== cacheKey || $[9] !== onClearLocalChanges || $[10] !== snackbarController) {
15760
+ t5 = () => {
15761
+ removeEntityFromCache(cacheKey);
15762
+ snackbarController.open({
15763
+ type: "info",
15764
+ message: "Local changes discarded"
15765
+ });
15766
+ handleCloseMenu();
15767
+ onClearLocalChanges?.();
15768
+ };
15769
+ $[8] = cacheKey;
15770
+ $[9] = onClearLocalChanges;
15771
+ $[10] = snackbarController;
15772
+ $[11] = t5;
15773
+ } else {
15774
+ t5 = $[11];
15775
+ }
15776
+ const handleDiscard = t5;
15777
+ let t6;
15778
+ if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
15779
+ t6 = /* @__PURE__ */ jsxRuntime.jsx(ui.WarningIcon, { size: "smallest", className: "mr-1 text-yellow-600 dark:text-yellow-400" });
15780
+ $[12] = t6;
15781
+ } else {
15782
+ t6 = $[12];
15783
+ }
15784
+ let t7;
15785
+ if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
15786
+ t7 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", className: "font-semibold text-xs rounded-full px-4 py-1 bg-yellow-200 dark:bg-yellow-900 hover:bg-yellow-300 dark:hover:bg-yellow-800 text-yellow-800 dark:text-yellow-200", onClick: handleOpenMenu, children: [
15787
+ t6,
15788
+ "Unsaved Local changes",
15789
+ /* @__PURE__ */ jsxRuntime.jsx(ui.KeyboardArrowDownIcon, { size: "smallest" })
15790
+ ] });
15791
+ $[13] = t7;
15792
+ } else {
15793
+ t7 = $[13];
15794
+ }
15795
+ let t8;
15796
+ if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
15797
+ t8 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xs px-4 py-4 text-sm text-gray-700 dark:text-gray-300", children: "This document was edited locally and has unsaved changes. These local changes will be lost if you don't apply them." });
15798
+ $[14] = t8;
15799
+ } else {
15800
+ t8 = $[14];
15801
+ }
15802
+ let t9;
15803
+ if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
15804
+ t9 = /* @__PURE__ */ jsxRuntime.jsxs(ui.MenuItem, { dense: true, onClick: handlePreview, children: [
15805
+ /* @__PURE__ */ jsxRuntime.jsx(ui.VisibilityIcon, { size: "small" }),
15806
+ "Preview Changes"
15807
+ ] });
15808
+ $[15] = t9;
15809
+ } else {
15810
+ t9 = $[15];
15811
+ }
15812
+ let t10;
15813
+ if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
15814
+ t10 = /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "small" });
15815
+ $[16] = t10;
15816
+ } else {
15817
+ t10 = $[16];
15818
+ }
15819
+ let t11;
15820
+ if ($[17] !== handleApply) {
15821
+ t11 = /* @__PURE__ */ jsxRuntime.jsxs(ui.MenuItem, { dense: true, onClick: handleApply, children: [
15822
+ t10,
15823
+ "Apply Changes"
15824
+ ] });
15825
+ $[17] = handleApply;
15826
+ $[18] = t11;
15827
+ } else {
15828
+ t11 = $[18];
15829
+ }
15830
+ let t12;
15831
+ if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
15832
+ t12 = /* @__PURE__ */ jsxRuntime.jsx(ui.CancelIcon, { size: "small" });
15833
+ $[19] = t12;
15834
+ } else {
15835
+ t12 = $[19];
15836
+ }
15837
+ let t13;
15838
+ if ($[20] !== handleDiscard) {
15839
+ t13 = /* @__PURE__ */ jsxRuntime.jsxs(ui.MenuItem, { dense: true, onClick: handleDiscard, children: [
15840
+ t12,
15841
+ "Discard Local Changes"
15842
+ ] });
15843
+ $[20] = handleDiscard;
15844
+ $[21] = t13;
15845
+ } else {
15846
+ t13 = $[21];
15847
+ }
15848
+ let t14;
15849
+ if ($[22] !== open || $[23] !== t11 || $[24] !== t13) {
15850
+ t14 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Menu, { trigger: t7, open, onOpenChange: setOpen, children: [
15851
+ t8,
15852
+ t9,
15853
+ t11,
15854
+ t13
15855
+ ] });
15856
+ $[22] = open;
15857
+ $[23] = t11;
15858
+ $[24] = t13;
15859
+ $[25] = t14;
15860
+ } else {
15861
+ t14 = $[25];
15862
+ }
15863
+ let t15;
15864
+ if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
15865
+ t15 = /* @__PURE__ */ jsxRuntime.jsx(ui.DialogTitle, { variant: "h6", children: "Preview Local Changes" });
15866
+ $[26] = t15;
15867
+ } else {
15868
+ t15 = $[26];
15869
+ }
15870
+ let t16;
15871
+ if ($[27] === Symbol.for("react.memo_cache_sentinel")) {
15872
+ t16 = /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "body2", className: "mb-4", children: "These are the local changes that will be applied to the form." });
15873
+ $[27] = t16;
15874
+ } else {
15875
+ t16 = $[27];
15876
+ }
15877
+ let t17;
15878
+ if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
15879
+ t17 = {
15880
+ maxHeight: 520,
15881
+ overflow: "auto"
15882
+ };
15883
+ $[28] = t17;
15884
+ } else {
15885
+ t17 = $[28];
15886
+ }
15887
+ const t18 = properties;
15888
+ let t19;
15889
+ if ($[29] !== localChangesData || $[30] !== t18) {
15890
+ t19 = /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogContent, { className: "my-4", children: [
15891
+ t16,
15892
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `border rounded-lg ${ui.defaultBorderMixin}`, style: t17, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(PropertyCollectionView, { data: localChangesData, properties: t18 }) }) })
15893
+ ] });
15894
+ $[29] = localChangesData;
15895
+ $[30] = t18;
15896
+ $[31] = t19;
15897
+ } else {
15898
+ t19 = $[31];
15899
+ }
15900
+ let t20;
15901
+ if ($[32] === Symbol.for("react.memo_cache_sentinel")) {
15902
+ t20 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: () => setPreviewDialogOpen(false), children: "Close" });
15903
+ $[32] = t20;
15904
+ } else {
15905
+ t20 = $[32];
15906
+ }
15907
+ let t21;
15908
+ if ($[33] !== handleApply) {
15909
+ t21 = /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { children: [
15910
+ t20,
15911
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "filled", onClick: () => {
15912
+ handleApply();
15913
+ setPreviewDialogOpen(false);
15914
+ }, children: "Apply changes" })
15915
+ ] });
15916
+ $[33] = handleApply;
15917
+ $[34] = t21;
15918
+ } else {
15919
+ t21 = $[34];
15920
+ }
15921
+ let t22;
15922
+ if ($[35] !== previewDialogOpen || $[36] !== t19 || $[37] !== t21) {
15923
+ t22 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Dialog, { open: previewDialogOpen, onOpenChange: setPreviewDialogOpen, maxWidth: "4xl", children: [
15924
+ t15,
15925
+ t19,
15926
+ t21
15927
+ ] });
15928
+ $[35] = previewDialogOpen;
15929
+ $[36] = t19;
15930
+ $[37] = t21;
15931
+ $[38] = t22;
15932
+ } else {
15933
+ t22 = $[38];
15934
+ }
15935
+ let t23;
15936
+ if ($[39] !== t14 || $[40] !== t22) {
15937
+ t23 = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15938
+ t14,
15939
+ t22
15940
+ ] });
15941
+ $[39] = t14;
15942
+ $[40] = t22;
15943
+ $[41] = t23;
15944
+ } else {
15945
+ t23 = $[41];
15946
+ }
15947
+ return t23;
15948
+ }
15949
+ function extractTouchedValues(values, touched) {
15950
+ let acc = {};
15951
+ if (!touched || typeof touched !== "object") {
15952
+ return acc;
15953
+ }
15954
+ Object.entries(touched).forEach(([key, value]) => {
15955
+ if (value) {
15956
+ acc = formex.setIn(acc, key, formex.getIn(values, key));
15957
+ }
15958
+ });
15959
+ return acc;
15960
+ }
15961
+ function getChanges(source, comparison) {
15962
+ const changes = {};
15963
+ if (!source) {
15964
+ return {};
15965
+ }
15966
+ if (!comparison) {
15967
+ return source;
15968
+ }
15969
+ const allKeys = Array.from(/* @__PURE__ */ new Set([...Object.keys(source), ...Object.keys(comparison)]));
15970
+ for (const key of allKeys) {
15971
+ const sourceValue = source[key];
15972
+ const comparisonValue = comparison[key];
15973
+ if (equal(sourceValue, comparisonValue)) {
15974
+ continue;
15975
+ }
15976
+ const sourceHasKey = source && typeof source === "object" && Object.prototype.hasOwnProperty.call(source, key);
15977
+ const comparisonHasKey = comparison && typeof comparison === "object" && Object.prototype.hasOwnProperty.call(comparison, key);
15978
+ if (comparisonHasKey && !sourceHasKey) {
15979
+ changes[key] = void 0;
15980
+ } else if (Array.isArray(sourceValue)) {
15981
+ const comparisonArray = Array.isArray(comparisonValue) ? comparisonValue : [];
15982
+ if (sourceValue.length < comparisonArray.length) {
15983
+ changes[key] = sourceValue;
15984
+ continue;
15985
+ }
15986
+ const changedArray = sourceValue.map((item, index) => {
15987
+ const comparisonItem = comparisonArray[index];
15988
+ if (equal(item, comparisonItem)) {
15989
+ return null;
15990
+ }
15991
+ if (isObject(item) && item && isObject(comparisonItem) && comparisonItem) {
15992
+ const nestedChanges = getChanges(item, comparisonItem);
15993
+ return Object.keys(nestedChanges).length > 0 ? nestedChanges : item;
15994
+ }
15995
+ return item;
15996
+ });
15997
+ if (changedArray.some((item) => item !== null) || sourceValue.length > comparisonArray.length) {
15998
+ changes[key] = changedArray;
15999
+ }
16000
+ } else if (isObject(sourceValue) && sourceValue && isObject(comparisonValue) && comparisonValue) {
16001
+ const nestedChanges = getChanges(sourceValue, comparisonValue);
16002
+ if (Object.keys(nestedChanges).length > 0) {
16003
+ changes[key] = nestedChanges;
16004
+ }
16005
+ } else {
16006
+ changes[key] = sourceValue;
16007
+ }
16008
+ }
16009
+ return changes;
16010
+ }
15185
16011
  function EntityForm({
15186
16012
  path,
15187
16013
  fullIdPath,
@@ -15239,7 +16065,7 @@
15239
16065
  const customizationController = useCustomizationController();
15240
16066
  const context = useFireCMSContext();
15241
16067
  const analyticsController = useAnalyticsController();
15242
- const [underlyingChanges, setUnderlyingChanges] = React.useState({});
16068
+ const [underlyingChanges] = React.useState({});
15243
16069
  const [customIdLoading, setCustomIdLoading] = React.useState(false);
15244
16070
  const mustSetCustomId = (status === "new" || status === "copy") && Boolean(collection.customId) && collection.customId !== "optional";
15245
16071
  const initialEntityId = React.useMemo(() => {
@@ -15257,6 +16083,12 @@
15257
16083
  const [entityIdError, setEntityIdError] = React.useState(false);
15258
16084
  const [savingError, setSavingError] = React.useState();
15259
16085
  const autoSave = collection.formAutoSave && !collection.customId;
16086
+ const baseInitialValues = React.useMemo(() => getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs), [authController, collection, path, status, entity, customizationController.propertyConfigs]);
16087
+ const localChangesDataRaw = React.useMemo(() => entityId ? getEntityFromCache(path + "/" + entityId) : getEntityFromCache(path + "#new"), [entityId, path]);
16088
+ const [localChangesCleared, setLocalChangesCleared] = React.useState(false);
16089
+ const localChangesBackup = getLocalChangesBackup(collection);
16090
+ const autoApplyLocalChanges = localChangesBackup === "auto_apply";
16091
+ const manualApplyLocalChanges = localChangesBackup === "manual_apply";
15260
16092
  const onSubmit = (values, formexController) => {
15261
16093
  if (mustSetCustomId && !entityId) {
15262
16094
  console.error("Missing custom Id");
@@ -15287,16 +16119,40 @@
15287
16119
  formexController.setSubmitting(false);
15288
16120
  });
15289
16121
  };
16122
+ const [initialValues_0, initialDirty_0] = React.useMemo(() => {
16123
+ const initialValuesWithLocalChanges = autoApplyLocalChanges && localChangesDataRaw ? mergeDeep(baseInitialValues, localChangesDataRaw) : baseInitialValues;
16124
+ const initialValues = initialDirtyValues ? mergeDeep(initialValuesWithLocalChanges, initialDirtyValues) : initialValuesWithLocalChanges;
16125
+ const initialDirty = Boolean(initialDirtyValues) && initialDirtyValues && Object.keys(initialDirtyValues).length > 0;
16126
+ return [initialValues, initialDirty];
16127
+ }, [autoApplyLocalChanges, localChangesDataRaw, baseInitialValues, initialDirtyValues]);
16128
+ const localChangesData = React.useMemo(() => {
16129
+ if (!localChangesDataRaw) {
16130
+ return void 0;
16131
+ }
16132
+ return getChanges(localChangesDataRaw, initialValues_0);
16133
+ }, [localChangesDataRaw, initialValues_0]);
16134
+ const hasLocalChanges = !localChangesCleared && localChangesData && Object.keys(localChangesData).length > 0;
15290
16135
  const formex$1 = formexProp ?? formex.useCreateFormex({
15291
- initialValues: initialDirtyValues ?? getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs),
15292
- initialDirty: Boolean(initialDirtyValues),
16136
+ initialValues: initialValues_0,
16137
+ initialDirty: initialDirty_0,
16138
+ initialTouched: initialDirtyValues ? flattenKeys(initialDirtyValues).reduce((previousValue, currentValue) => ({
16139
+ ...previousValue,
16140
+ [currentValue]: true
16141
+ }), {}) : {},
15293
16142
  onSubmit,
15294
16143
  onReset: () => {
15295
16144
  clearDirtyCache();
15296
- onValuesModified?.(false);
16145
+ onValuesModified?.(false, initialValues_0);
15297
16146
  },
15298
- validation: (values_0) => {
15299
- return validationSchema?.validate(values_0, {
16147
+ onValuesChangeDeferred: (values_0, controller) => {
16148
+ const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
16149
+ if (controller.dirty) {
16150
+ const touchedValues = extractTouchedValues(values_0, controller.touched);
16151
+ saveEntityToCache(key, touchedValues);
16152
+ }
16153
+ },
16154
+ validation: (values_1) => {
16155
+ return validationSchema?.validate(values_1, {
15300
16156
  abortEarly: false
15301
16157
  }).then(() => {
15302
16158
  return {};
@@ -15345,14 +16201,16 @@
15345
16201
  }, [snackbarController]);
15346
16202
  function clearDirtyCache() {
15347
16203
  if (status === "new" || status === "copy") {
16204
+ removeEntityFromMemoryCache(path + "#new");
15348
16205
  removeEntityFromCache(path + "#new");
15349
16206
  } else {
16207
+ removeEntityFromMemoryCache(path + "/" + entityId);
15350
16208
  removeEntityFromCache(path + "/" + entityId);
15351
16209
  }
15352
16210
  }
15353
16211
  const onSaveSuccess = (updatedEntity) => {
15354
16212
  clearDirtyCache();
15355
- onValuesModified?.(false);
16213
+ onValuesModified?.(false, updatedEntity.values);
15356
16214
  if (!autoSave) snackbarController.open({
15357
16215
  type: "success",
15358
16216
  message: `${collection.singularName ?? collection.name}: Saved correctly`
@@ -15379,7 +16237,7 @@
15379
16237
  console.error(e_3);
15380
16238
  }, [entityId, path, snackbarController]);
15381
16239
  const saveEntity = ({
15382
- values: values_1,
16240
+ values: values_2,
15383
16241
  previousValues,
15384
16242
  entityId: entityId_0,
15385
16243
  collection: collection_0,
@@ -15388,7 +16246,7 @@
15388
16246
  return saveEntityWithCallbacks({
15389
16247
  path: path_0,
15390
16248
  entityId: entityId_0,
15391
- values: values_1,
16249
+ values: values_2,
15392
16250
  previousValues,
15393
16251
  collection: collection_0,
15394
16252
  status,
@@ -15404,34 +16262,34 @@
15404
16262
  collection: collection_1,
15405
16263
  path: path_1,
15406
16264
  entityId: entityId_1,
15407
- values: values_2,
16265
+ values: values_3,
15408
16266
  previousValues: previousValues_0,
15409
16267
  autoSave: autoSave_0
15410
16268
  }) => {
15411
16269
  if (!status) return;
15412
16270
  if (autoSave_0) {
15413
- setValuesToBeSaved(values_2);
16271
+ setValuesToBeSaved(values_3);
15414
16272
  } else {
15415
16273
  return saveEntity({
15416
16274
  collection: collection_1,
15417
16275
  path: path_1,
15418
16276
  entityId: entityId_1,
15419
- values: values_2,
16277
+ values: values_3,
15420
16278
  previousValues: previousValues_0
15421
16279
  });
15422
16280
  }
15423
16281
  };
15424
16282
  const lastSavedValues = React.useRef(entity?.values);
15425
- const save = (values_3) => {
15426
- lastSavedValues.current = values_3;
16283
+ const save = (values_4) => {
16284
+ lastSavedValues.current = values_4;
15427
16285
  return onSaveEntityRequest({
15428
16286
  collection: resolvedCollection,
15429
16287
  path,
15430
16288
  entityId,
15431
- values: values_3,
16289
+ values: values_4,
15432
16290
  previousValues: entity?.values,
15433
16291
  autoSave: autoSave ?? false
15434
- }).then((res) => {
16292
+ }).then(() => {
15435
16293
  const eventName = status === "new" ? "new_entity_saved" : status === "copy" ? "entity_copied" : status === "existing" ? "entity_edited" : "unmapped_event";
15436
16294
  analyticsController.onAnalyticsEvent?.(eventName, {
15437
16295
  path
@@ -15465,7 +16323,8 @@
15465
16323
  type: "error",
15466
16324
  message: "Error updating id, check the console"
15467
16325
  });
15468
- }, []);
16326
+ console.error(error);
16327
+ }, [snackbarController]);
15469
16328
  const pluginActions = [];
15470
16329
  const plugins = customizationController.plugins;
15471
16330
  const actionsDisabled = disabled || formex$1.isSubmitting || status === "existing" && !formex$1.dirty || Boolean(disabledProp);
@@ -15511,23 +16370,15 @@
15511
16370
  }, [doOnIdUpdate]);
15512
16371
  React.useEffect(() => {
15513
16372
  if (!autoSave) {
15514
- onValuesModified?.(modified);
16373
+ onValuesModified?.(modified, formex$1.values);
15515
16374
  }
15516
16375
  }, [formex$1.dirty]);
15517
- const deferredValues = React.useDeferredValue(formex$1.values);
15518
16376
  const modified = formex$1.dirty;
15519
16377
  const uniqueFieldValidator = React.useCallback(({
15520
16378
  name,
15521
- value,
15522
- property
16379
+ value
15523
16380
  }) => dataSource.checkUniqueField(path, name, value, entityId, collection), [dataSource, path, entityId]);
15524
16381
  const validationSchema = React.useMemo(() => entityId ? getYupEntitySchema(entityId, resolvedCollection.properties, uniqueFieldValidator) : void 0, [entityId, resolvedCollection.properties, uniqueFieldValidator]);
15525
- React.useEffect(() => {
15526
- const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
15527
- if (modified) {
15528
- saveEntityToCache(key, deferredValues);
15529
- }
15530
- }, [deferredValues, modified, path, entityId, status]);
15531
16382
  useOnAutoSave(autoSave, formex$1, lastSavedValues, save);
15532
16383
  React.useEffect(() => {
15533
16384
  if (!autoSave && !formex$1.isSubmitting && underlyingChanges && entity) {
@@ -15546,18 +16397,18 @@
15546
16397
  return /* @__PURE__ */ jsxRuntime.jsx(Builder, { collection, entity, modifiedValues: formex$1.values, formContext });
15547
16398
  }
15548
16399
  return /* @__PURE__ */ jsxRuntime.jsx(FormLayout, { children: formFieldKeys.map((key_1) => {
15549
- const property_0 = resolvedCollection.properties[key_1];
15550
- if (property_0) {
16400
+ const property = resolvedCollection.properties[key_1];
16401
+ if (property) {
15551
16402
  const underlyingValueHasChanged = !!underlyingChanges && Object.keys(underlyingChanges).includes(key_1) && formex$1.touched[key_1];
15552
- const disabled_0 = disabledProp || !autoSave && formex$1.isSubmitting || isReadOnly(property_0) || Boolean(property_0.disabled);
15553
- const hidden = isHidden(property_0);
16403
+ const disabled_0 = disabledProp || !autoSave && formex$1.isSubmitting || isReadOnly(property) || Boolean(property.disabled);
16404
+ const hidden = isHidden(property);
15554
16405
  if (hidden) return null;
15555
- const widthPercentage = property_0.widthPercentage ?? 100;
16406
+ const widthPercentage = property.widthPercentage ?? 100;
15556
16407
  const cmsFormFieldProps = {
15557
16408
  propertyKey: key_1,
15558
16409
  disabled: disabled_0,
15559
- property: property_0,
15560
- includeDescription: property_0.description || property_0.longDescription,
16410
+ property,
16411
+ includeDescription: property.description || property.longDescription,
15561
16412
  underlyingValueHasChanged: underlyingValueHasChanged && !autoSave,
15562
16413
  context: formContext,
15563
16414
  partOfArray: false,
@@ -15613,10 +16464,13 @@
15613
16464
  }
15614
16465
  const dialogActions = /* @__PURE__ */ jsxRuntime.jsx(EntityFormActionsComponent, { collection: resolvedCollection, path, fullPath: path, fullIdPath, entity, layout: forceActionsAtTheBottom ? "bottom" : "side", savingError, formex: formex$1, disabled: actionsDisabled, status, pluginActions: pluginActions ?? [], openEntityMode, showDefaultActions, navigateBack, formContext });
15615
16466
  return /* @__PURE__ */ jsxRuntime.jsx(formex.Formex, { value: formex$1, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: formex$1.handleSubmit, onReset: () => formex$1.resetForm({
15616
- values: getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs)
16467
+ values: baseInitialValues
15617
16468
  }), noValidate: true, className: ui.cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className), children: [
15618
16469
  /* @__PURE__ */ jsxRuntime.jsx("div", { id: `form_${path}`, className: ui.cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit"), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10"), children: [
15619
- formex$1.dirty ? /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "Local unsaved changes", className: "self-end sticky top-4 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { size: "small", colorScheme: "orangeDarker", children: /* @__PURE__ */ jsxRuntime.jsx(ui.EditIcon, { size: "smallest" }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "In sync with the database", className: "self-end sticky top-4 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { size: "small", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "smallest" }) }) }),
16470
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row gap-4 self-end sticky top-4 z-10", children: [
16471
+ manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsxRuntime.jsx(LocalChangesMenu, { cacheKey: status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId, properties: resolvedCollection.properties, localChangesData, formex: formex$1, onClearLocalChanges: () => setLocalChangesCleared(true) }),
16472
+ formex$1.dirty ? /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "This form has been modified", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { size: "small", className: "py-1", colorScheme: "orangeDarker", children: /* @__PURE__ */ jsxRuntime.jsx(ui.EditIcon, { size: "smallest" }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "The current form is in sync with the database", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { size: "small", className: "py-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "smallest" }) }) })
16473
+ ] }),
15620
16474
  formView
15621
16475
  ] }) }),
15622
16476
  dialogActions
@@ -17780,7 +18634,7 @@
17780
18634
  return false;
17781
18635
  });
17782
18636
  function PropertyFieldBindingInternal(t0) {
17783
- const $ = reactCompilerRuntime.c(18);
18637
+ const $ = reactCompilerRuntime.c(19);
17784
18638
  const {
17785
18639
  propertyKey,
17786
18640
  property,
@@ -17789,6 +18643,7 @@
17789
18643
  underlyingValueHasChanged,
17790
18644
  disabled: disabledProp,
17791
18645
  partOfArray,
18646
+ partOfBlock,
17792
18647
  minimalistView,
17793
18648
  autoFocus,
17794
18649
  index,
@@ -17797,15 +18652,8 @@
17797
18652
  } = t0;
17798
18653
  const authController = useAuthController();
17799
18654
  const customizationController = useCustomizationController();
17800
- if (propertyKey === "created_by") {
17801
- console.log("Rendering field for created_by", {
17802
- propertyKey,
17803
- property,
17804
- context
17805
- });
17806
- }
17807
18655
  let t1;
17808
- if ($[0] !== authController || $[1] !== autoFocus || $[2] !== context || $[3] !== customizationController.propertyConfigs || $[4] !== disabledProp || $[5] !== includeDescription || $[6] !== index || $[7] !== minimalistView || $[8] !== onPropertyChange || $[9] !== partOfArray || $[10] !== property || $[11] !== propertyKey || $[12] !== size || $[13] !== underlyingValueHasChanged) {
18656
+ if ($[0] !== authController || $[1] !== autoFocus || $[2] !== context || $[3] !== customizationController.propertyConfigs || $[4] !== disabledProp || $[5] !== includeDescription || $[6] !== index || $[7] !== minimalistView || $[8] !== onPropertyChange || $[9] !== partOfArray || $[10] !== partOfBlock || $[11] !== property || $[12] !== propertyKey || $[13] !== size || $[14] !== underlyingValueHasChanged) {
17809
18657
  t1 = (fieldProps) => {
17810
18658
  let Component;
17811
18659
  const resolvedProperty = resolveProperty({
@@ -17868,6 +18716,7 @@
17868
18716
  context,
17869
18717
  disabled,
17870
18718
  partOfArray,
18719
+ partOfBlock,
17871
18720
  minimalistView,
17872
18721
  autoFocus,
17873
18722
  size,
@@ -17885,22 +18734,23 @@
17885
18734
  $[7] = minimalistView;
17886
18735
  $[8] = onPropertyChange;
17887
18736
  $[9] = partOfArray;
17888
- $[10] = property;
17889
- $[11] = propertyKey;
17890
- $[12] = size;
17891
- $[13] = underlyingValueHasChanged;
17892
- $[14] = t1;
18737
+ $[10] = partOfBlock;
18738
+ $[11] = property;
18739
+ $[12] = propertyKey;
18740
+ $[13] = size;
18741
+ $[14] = underlyingValueHasChanged;
18742
+ $[15] = t1;
17893
18743
  } else {
17894
- t1 = $[14];
18744
+ t1 = $[15];
17895
18745
  }
17896
18746
  let t2;
17897
- if ($[15] !== propertyKey || $[16] !== t1) {
18747
+ if ($[16] !== propertyKey || $[17] !== t1) {
17898
18748
  t2 = /* @__PURE__ */ jsxRuntime.jsx(formex.Field, { name: propertyKey, children: t1 }, propertyKey);
17899
- $[15] = propertyKey;
17900
- $[16] = t1;
17901
- $[17] = t2;
18749
+ $[16] = propertyKey;
18750
+ $[17] = t1;
18751
+ $[18] = t2;
17902
18752
  } else {
17903
- t2 = $[17];
18753
+ t2 = $[18];
17904
18754
  }
17905
18755
  return t2;
17906
18756
  }
@@ -17912,6 +18762,7 @@
17912
18762
  includeDescription,
17913
18763
  underlyingValueHasChanged,
17914
18764
  partOfArray,
18765
+ partOfBlock,
17915
18766
  minimalistView,
17916
18767
  autoFocus,
17917
18768
  context,
@@ -17961,6 +18812,7 @@
17961
18812
  disabled: disabled ?? false,
17962
18813
  underlyingValueHasChanged: underlyingValueHasChanged ?? false,
17963
18814
  partOfArray: partOfArray ?? false,
18815
+ partOfBlock: partOfBlock ?? false,
17964
18816
  minimalistView: minimalistView ?? false,
17965
18817
  autoFocus: autoFocus ?? false,
17966
18818
  customProps: customFieldProps,
@@ -19272,6 +20124,7 @@
19272
20124
  context,
19273
20125
  autoFocus,
19274
20126
  partOfArray: false,
20127
+ partOfBlock: true,
19275
20128
  minimalistView: true,
19276
20129
  onPropertyChange: storeProps
19277
20130
  } : void 0;
@@ -20283,6 +21136,11 @@
20283
21136
  console.error("Save failure");
20284
21137
  console.error(e_0);
20285
21138
  setError(e_0);
21139
+ },
21140
+ onPreSaveHookError: (e_1) => {
21141
+ console.error("Pre-save hook error");
21142
+ console.error(e_1);
21143
+ setError(e_1);
20286
21144
  }
20287
21145
  });
20288
21146
  };
@@ -20380,7 +21238,7 @@
20380
21238
  width: width_0,
20381
21239
  frozen
20382
21240
  }) => {
20383
- const isSelected = Boolean(usedSelectionController.selectedEntities.find((e_1) => e_1.id == entity_6.id && e_1.path == entity_6.path));
21241
+ const isSelected = Boolean(usedSelectionController.selectedEntities.find((e_2) => e_2.id == entity_6.id && e_2.path == entity_6.path));
20384
21242
  const customEntityActions_0 = (collection.entityActions ?? []).map((action) => resolveEntityAction(action, customizationController.entityActions)).filter(Boolean);
20385
21243
  const actions_0 = getActionsForEntity({
20386
21244
  entity: entity_6,
@@ -20389,9 +21247,9 @@
20389
21247
  return /* @__PURE__ */ jsxRuntime.jsx(EntityCollectionRowActions, { entity: entity_6, width: width_0, frozen, isSelected, selectionEnabled, size: size_0, highlightEntity: setHighlightedEntity, unhighlightEntity: unselectNavigatedEntity, collection, fullPath, fullIdPath, actions: actions_0, hideId: collection?.hideIdFromCollection, onCollectionChange: updateLastDeleteTimestamp, selectionController: usedSelectionController, openEntityMode });
20390
21248
  }, [updateLastDeleteTimestamp, usedSelectionController]);
20391
21249
  const title = /* @__PURE__ */ jsxRuntime.jsx(ui.Popover, { open: popOverOpen, onOpenChange: setPopOverOpen, enabled: Boolean(collection.description), trigger: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start", children: [
20392
- /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "subtitle1", className: `leading-none truncate max-w-[160px] lg:max-w-[240px] ${collection.description ? "cursor-pointer" : "cursor-auto"}`, onClick: collection.description ? (e_2) => {
21250
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "subtitle1", className: `leading-none truncate max-w-[160px] lg:max-w-[240px] ${collection.description ? "cursor-pointer" : "cursor-auto"}`, onClick: collection.description ? (e_3) => {
20393
21251
  setPopOverOpen(true);
20394
- e_2.stopPropagation();
21252
+ e_3.stopPropagation();
20395
21253
  } : void 0, children: `${collection.name}` }),
20396
21254
  /* @__PURE__ */ jsxRuntime.jsx(EntitiesCount, { fullPath, collection, filter: tableController.filterValues, sortBy: tableController.sortBy, onCountChange: setDocsCount })
20397
21255
  ] }), children: collection.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "m-4 text-surface-900 dark:text-white", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Markdown, { source: collection.description }) }) });
@@ -20426,7 +21284,7 @@
20426
21284
  /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "subtitle2", children: "So empty..." }),
20427
21285
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { color: "primary", variant: "outlined", onClick: onNewClick, className: "mt-4", children: [
20428
21286
  /* @__PURE__ */ jsxRuntime.jsx(ui.AddIcon, {}),
20429
- "Create your first entity"
21287
+ "Create your first entry"
20430
21288
  ] })
20431
21289
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "label", children: "No results with the applied filter/sort" }), hoverRow, inlineEditing: checkInlineEditing(), AdditionalHeaderWidget: buildAdditionalHeaderWidget, AddColumnComponent: addColumnComponentInternal, getIdColumnWidth, additionalIDHeaderWidget: /* @__PURE__ */ jsxRuntime.jsx(EntityIdHeaderWidget, { path: fullPath, fullIdPath: fullIdPath ?? fullPath, collection }), openEntityMode }, `collection_table_${fullPath}`),
20432
21290
  popupCell && /* @__PURE__ */ jsxRuntime.jsx(PopupFormField, { open: Boolean(popupCell), onClose: onPopupClose, cellRect: popupCell?.cellRect, propertyKey: popupCell?.propertyKey, collection, entityId: popupCell.entityId, tableKey: tableKey.current, customFieldValidator: uniqueFieldValidator, path: resolvedFullPath, onCellValueChange: onValueChange, container: containerRef.current }, `popup_form_${popupCell?.propertyKey}_${popupCell?.entityId}`),
@@ -21805,22 +22663,9 @@
21805
22663
  const buildUrlCollectionPath = React.useCallback((path_0) => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path_0)}`, [baseCollectionPath]);
21806
22664
  const allPluginGroups = plugins?.flatMap((plugin) => plugin.homePage?.navigationEntries ? plugin.homePage.navigationEntries.map((e) => e.name) : []) ?? [];
21807
22665
  const pluginGroups = [...new Set(allPluginGroups)];
21808
- const onNavigationEntriesOrderUpdate = React.useCallback((entries) => {
21809
- if (!plugins) {
21810
- return;
21811
- }
21812
- const filteredEntries = entries.filter((entry) => entry.entries.length > 0);
21813
- if (plugins.some((plugin_1) => plugin_1.homePage?.onNavigationEntriesUpdate)) {
21814
- plugins.forEach((plugin_0) => {
21815
- if (plugin_0.homePage?.onNavigationEntriesUpdate) {
21816
- plugin_0.homePage.onNavigationEntriesUpdate(filteredEntries);
21817
- }
21818
- });
21819
- }
21820
- }, [plugins]);
21821
- const computeTopNavigation = React.useCallback((collections, views, adminViews, viewsOrder_0) => {
22666
+ const computeTopNavigation = React.useCallback((collections, views, adminViews, viewsOrder_0, navigationGroupMappingsOverride, onNavigationEntriesUpdateCallback) => {
21822
22667
  const finalNavigationGroupMappings = computeNavigationGroups({
21823
- navigationGroupMappings,
22668
+ navigationGroupMappings: navigationGroupMappingsOverride ?? navigationGroupMappings,
21824
22669
  collections,
21825
22670
  views,
21826
22671
  plugins
@@ -21852,7 +22697,7 @@
21852
22697
  return acc;
21853
22698
  }, []), ...(views ?? []).reduce((acc_0, view) => {
21854
22699
  if (view.hideFromNavigation) return acc_0;
21855
- const pathKey_0 = Array.isArray(view.path) ? view.path[0] : view.path;
22700
+ const pathKey_0 = view.path;
21856
22701
  let groupName_0 = getGroup(view);
21857
22702
  if (finalNavigationGroupMappings) {
21858
22703
  for (const pluginGroupDef_0 of finalNavigationGroupMappings) {
@@ -21875,7 +22720,7 @@
21875
22720
  return acc_0;
21876
22721
  }, []), ...(adminViews ?? []).reduce((acc_1, view_0) => {
21877
22722
  if (view_0.hideFromNavigation) return acc_1;
21878
- const pathKey_1 = Array.isArray(view_0.path) ? view_0.path[0] : view_0.path;
22723
+ const pathKey_1 = view_0.path;
21879
22724
  const groupName_1 = NAVIGATION_ADMIN_GROUP_NAME;
21880
22725
  acc_1.push({
21881
22726
  id: `admin:${pathKey_1}`,
@@ -21909,21 +22754,43 @@
21909
22754
  });
21910
22755
  }
21911
22756
  const collectedGroupsFromEntries = navigationEntries.map((e_0) => e_0.group).filter(Boolean);
21912
- const allDefinedGroups = [...pluginGroups ?? [], ...collectedGroupsFromEntries];
21913
- const uniqueGroups = [...new Set(allDefinedGroups)].sort((a_1, b_1) => groupOrderValue(a_1) - groupOrderValue(b_1));
22757
+ const groupsFromMappings = finalNavigationGroupMappings.map((g_0) => g_0.name);
22758
+ const additionalGroups = collectedGroupsFromEntries.filter((g_1) => !groupsFromMappings.includes(g_1));
22759
+ const allDefinedGroups = [...pluginGroups ?? [], ...groupsFromMappings, ...additionalGroups];
22760
+ const uniqueGroupsArray = [...new Set(allDefinedGroups)];
22761
+ const adminGroups = uniqueGroupsArray.filter((g_2) => g_2 === NAVIGATION_ADMIN_GROUP_NAME);
22762
+ const nonAdminGroups = uniqueGroupsArray.filter((g_3) => g_3 !== NAVIGATION_ADMIN_GROUP_NAME);
22763
+ const uniqueGroups = [...nonAdminGroups, ...adminGroups];
21914
22764
  return {
21915
- allowDragAndDrop: plugins?.some((plugin_2) => plugin_2.homePage?.allowDragAndDrop) ?? false,
22765
+ allowDragAndDrop: plugins?.some((plugin_0) => plugin_0.homePage?.allowDragAndDrop) ?? false,
21916
22766
  navigationEntries,
21917
22767
  groups: uniqueGroups,
21918
- onNavigationEntriesUpdate: onNavigationEntriesOrderUpdate
22768
+ onNavigationEntriesUpdate: onNavigationEntriesUpdateCallback
21919
22769
  };
21920
- }, [navigationGroupMappings, buildCMSUrlPath, buildUrlCollectionPath, pluginGroups, onNavigationEntriesOrderUpdate]);
22770
+ }, [navigationGroupMappings, buildCMSUrlPath, buildUrlCollectionPath, pluginGroups]);
22771
+ const onNavigationEntriesOrderUpdate = React.useCallback((entries) => {
22772
+ if (!plugins) {
22773
+ return;
22774
+ }
22775
+ const filteredEntries = entries.filter((entry) => entry.entries.length > 0);
22776
+ if (collectionsRef.current && viewsRef.current) {
22777
+ const updatedNav = computeTopNavigation(collectionsRef.current, viewsRef.current, adminViewsRef.current ?? [], viewsOrder, filteredEntries, onNavigationEntriesOrderUpdate);
22778
+ setTopLevelNavigation(updatedNav);
22779
+ }
22780
+ if (plugins.some((plugin_2) => plugin_2.homePage?.onNavigationEntriesUpdate)) {
22781
+ plugins.forEach((plugin_1) => {
22782
+ if (plugin_1.homePage?.onNavigationEntriesUpdate) {
22783
+ plugin_1.homePage.onNavigationEntriesUpdate(filteredEntries);
22784
+ }
22785
+ });
22786
+ }
22787
+ }, [plugins, computeTopNavigation, viewsOrder]);
21921
22788
  const refreshNavigation = React.useCallback(async () => {
21922
22789
  if (disabled || authController.initialLoading) return;
21923
22790
  console.debug("Refreshing navigation");
21924
22791
  try {
21925
22792
  const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, plugins), resolveCMSViews(viewsProp, authController, dataSourceDelegate), resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)]);
21926
- const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
22793
+ const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder, void 0, onNavigationEntriesOrderUpdate);
21927
22794
  let shouldUpdateTopLevelNav = false;
21928
22795
  if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
21929
22796
  collectionsRef.current = resolvedCollections;
@@ -22019,7 +22886,8 @@
22019
22886
  }, []);
22020
22887
  const isUrlCollectionPath = React.useCallback((path_1) => removeInitialAndTrailingSlashes(path_1 + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"), [fullCollectionPath]);
22021
22888
  const urlPathToDataPath = React.useCallback((path_2) => {
22022
- if (path_2.startsWith(fullCollectionPath)) return path_2.replace(fullCollectionPath, "");
22889
+ const decodedPath = decodeURIComponent(path_2);
22890
+ if (decodedPath.startsWith(fullCollectionPath)) return decodedPath.replace(fullCollectionPath, "");
22023
22891
  throw Error("Expected path starting with " + fullCollectionPath);
22024
22892
  }, [fullCollectionPath]);
22025
22893
  const resolveIdsFrom = React.useCallback((path_3) => {
@@ -22207,6 +23075,42 @@
22207
23075
  }
22208
23076
  return acc;
22209
23077
  }, [...result ?? []]) : result;
23078
+ const assignedEntries = /* @__PURE__ */ new Set();
23079
+ if (result) {
23080
+ result.forEach((group) => {
23081
+ group.entries.forEach((entry) => assignedEntries.add(entry));
23082
+ });
23083
+ }
23084
+ const unassignedGroupMap = {};
23085
+ (collections ?? []).forEach((collection) => {
23086
+ const entry = collection.id ?? collection.path;
23087
+ if (!assignedEntries.has(entry)) {
23088
+ const groupName = getGroup(collection);
23089
+ if (!unassignedGroupMap[groupName]) unassignedGroupMap[groupName] = [];
23090
+ unassignedGroupMap[groupName].push(entry);
23091
+ }
23092
+ });
23093
+ (views ?? []).forEach((view) => {
23094
+ const entry = view.path;
23095
+ if (!assignedEntries.has(entry)) {
23096
+ const groupName = getGroup(view);
23097
+ if (!unassignedGroupMap[groupName]) unassignedGroupMap[groupName] = [];
23098
+ unassignedGroupMap[groupName].push(entry);
23099
+ }
23100
+ });
23101
+ Object.entries(unassignedGroupMap).forEach(([groupName, entries]) => {
23102
+ if (result) {
23103
+ const existingGroup = result.find((g) => g.name === groupName);
23104
+ if (existingGroup) {
23105
+ existingGroup.entries.push(...entries);
23106
+ } else {
23107
+ result.push({
23108
+ name: groupName,
23109
+ entries
23110
+ });
23111
+ }
23112
+ }
23113
+ });
22210
23114
  if (!result) {
22211
23115
  result = [];
22212
23116
  const groupMap = {};
@@ -22218,7 +23122,7 @@
22218
23122
  });
22219
23123
  (views ?? []).forEach((view) => {
22220
23124
  const name = getGroup(view);
22221
- const entry = Array.isArray(view.path) ? view.path[0] : view.path;
23125
+ const entry = view.path;
22222
23126
  if (!groupMap[name]) groupMap[name] = [];
22223
23127
  groupMap[name].push(entry);
22224
23128
  });
@@ -22825,14 +23729,14 @@
22825
23729
  collection,
22826
23730
  context,
22827
23731
  sideEntityController,
22828
- isSubmitting: formex2.isSubmitting,
22829
23732
  disabled,
22830
23733
  status,
22831
23734
  sideDialogContext,
22832
23735
  pluginActions,
22833
23736
  openEntityMode,
22834
23737
  navigateBack,
22835
- formContext
23738
+ formContext,
23739
+ formex: formex2
22836
23740
  }) : buildSideActions({
22837
23741
  savingError,
22838
23742
  entity,
@@ -22840,14 +23744,14 @@
22840
23744
  collection,
22841
23745
  context,
22842
23746
  sideEntityController,
22843
- isSubmitting: formex2.isSubmitting,
22844
23747
  sideDialogContext,
22845
23748
  disabled,
22846
23749
  status,
22847
23750
  pluginActions,
22848
23751
  openEntityMode,
22849
23752
  navigateBack,
22850
- formContext
23753
+ formContext,
23754
+ formex: formex2
22851
23755
  });
22852
23756
  }
22853
23757
  function buildBottomActions({
@@ -22857,15 +23761,16 @@
22857
23761
  collection,
22858
23762
  context,
22859
23763
  sideEntityController,
22860
- isSubmitting,
22861
23764
  disabled,
22862
23765
  status,
22863
23766
  sideDialogContext,
22864
23767
  pluginActions,
22865
23768
  openEntityMode,
22866
23769
  navigateBack,
22867
- formContext
23770
+ formContext,
23771
+ formex: formex2
22868
23772
  }) {
23773
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
22869
23774
  const canClose = openEntityMode === "side_panel";
22870
23775
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { position: "absolute", children: [
22871
23776
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) }),
@@ -22885,15 +23790,16 @@
22885
23790
  return /* @__PURE__ */ jsxRuntime.jsx(EntityActionButton, { action, enabled: isEnabled, props }, action.key);
22886
23791
  }) }),
22887
23792
  pluginActions,
22888
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", color: "primary", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22889
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: canClose ? "text" : "filled", color: "primary", type: "submit", disabled: disabled || isSubmitting, onClick: () => {
23793
+ hasErrors ? /* @__PURE__ */ jsxRuntime.jsx(ErrorTooltip, { title: "This form has errors", children: /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, { className: "ml-4", color: "error", size: "smallest" }) }) : null,
23794
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", color: "primary", disabled: disabled || formex2.isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
23795
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: canClose ? "text" : "filled", color: "primary", type: "submit", disabled: disabled || formex2.isSubmitting, onClick: () => {
22890
23796
  sideDialogContext.setPendingClose(false);
22891
23797
  }, children: [
22892
23798
  status === "existing" && "Save",
22893
23799
  status === "copy" && "Create copy",
22894
23800
  status === "new" && "Create"
22895
23801
  ] }),
22896
- canClose && /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { variant: "filled", color: "primary", type: "submit", loading: isSubmitting, disabled, onClick: () => {
23802
+ canClose && /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { variant: "filled", color: "primary", type: "submit", loading: formex2.isSubmitting, disabled, onClick: () => {
22897
23803
  sideDialogContext.setPendingClose?.(true);
22898
23804
  }, children: [
22899
23805
  status === "existing" && "Save and close",
@@ -22909,24 +23815,25 @@
22909
23815
  collection,
22910
23816
  context,
22911
23817
  sideEntityController,
22912
- isSubmitting,
22913
23818
  disabled,
22914
23819
  status,
22915
23820
  sideDialogContext,
22916
23821
  pluginActions,
22917
23822
  openEntityMode,
22918
23823
  navigateBack,
22919
- formContext
23824
+ formContext,
23825
+ formex: formex2
22920
23826
  }) {
23827
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
22921
23828
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", ui.defaultBorderMixin), children: [
22922
- /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", disabled: disabled || isSubmitting, onClick: () => {
23829
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", startIcon: hasErrors ? /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, {}) : void 0, disabled: disabled || formex2.isSubmitting, onClick: () => {
22923
23830
  sideDialogContext.setPendingClose?.(false);
22924
23831
  }, children: [
22925
23832
  status === "existing" && "Save",
22926
23833
  status === "copy" && "Create copy",
22927
23834
  status === "new" && "Create"
22928
23835
  ] }),
22929
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
23836
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || formex2.isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22930
23837
  pluginActions,
22931
23838
  formActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row flex-wrap mt-2", children: formActions.map((action) => {
22932
23839
  const props = {
@@ -23081,6 +23988,7 @@
23081
23988
  function createFormexStub(values) {
23082
23989
  const errorMessage = "You are in a read-only context. You cannot modify the formex controller.";
23083
23990
  return {
23991
+ debugId: "",
23084
23992
  values,
23085
23993
  initialValues: values,
23086
23994
  touched: {},
@@ -23095,6 +24003,9 @@
23095
24003
  setValues: () => {
23096
24004
  throw new Error(errorMessage);
23097
24005
  },
24006
+ setTouched(touched) {
24007
+ throw new Error(errorMessage);
24008
+ },
23098
24009
  setFieldValue: () => {
23099
24010
  throw new Error(errorMessage);
23100
24011
  },
@@ -23154,7 +24065,7 @@
23154
24065
  databaseId: props.databaseId,
23155
24066
  useCache: false
23156
24067
  });
23157
- const cachedValues = entityId ? getEntityFromCache(props.path + "/" + entityId) : getEntityFromCache(props.path + "#new");
24068
+ const initialDirtyValues = entityId ? getEntityFromMemoryCache(props.path + "/" + entityId) : getEntityFromMemoryCache(props.path + "#new");
23158
24069
  const authController = useAuthController();
23159
24070
  const initialStatus = props.copy ? "copy" : entityId ? "existing" : "new";
23160
24071
  const [status, setStatus] = React.useState(initialStatus);
@@ -23165,13 +24076,13 @@
23165
24076
  return entity ? canEditEntity(props.collection, authController, props.path, entity ?? null) : void 0;
23166
24077
  }
23167
24078
  }, [authController, entity, status]);
23168
- if (dataLoading && !cachedValues || (!entity || canEdit === void 0) && (status === "existing" || status === "copy")) {
24079
+ if (dataLoading && !initialDirtyValues || (!entity || canEdit === void 0) && (status === "existing" || status === "copy")) {
23169
24080
  return /* @__PURE__ */ jsxRuntime.jsx(CircularProgressCenter, {});
23170
24081
  }
23171
- if (entityId && !entity && !cachedValues) {
24082
+ if (entityId && !entity && !initialDirtyValues) {
23172
24083
  console.error(`Entity with id ${entityId} not found in collection ${props.path}`);
23173
24084
  }
23174
- return /* @__PURE__ */ jsxRuntime.jsx(EntityEditViewInner, { ...props, entityId, entity, cachedDirtyValues: cachedValues, dataLoading, status, setStatus, canEdit });
24085
+ return /* @__PURE__ */ jsxRuntime.jsx(EntityEditViewInner, { ...props, entityId, entity, initialDirtyValues, dataLoading, status, setStatus, canEdit });
23175
24086
  }
23176
24087
  function EntityEditViewInner({
23177
24088
  path,
@@ -23184,7 +24095,7 @@
23184
24095
  onSaved,
23185
24096
  onTabChange,
23186
24097
  entity,
23187
- cachedDirtyValues,
24098
+ initialDirtyValues,
23188
24099
  dataLoading,
23189
24100
  layout = "side_panel",
23190
24101
  barActions,
@@ -23312,7 +24223,8 @@
23312
24223
  /* @__PURE__ */ jsxRuntime.jsx(EntityView, { className: "px-8 h-full overflow-auto", entity, path, collection }),
23313
24224
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-16" })
23314
24225
  ] }) }) : null;
23315
- const entityView = /* @__PURE__ */ jsxRuntime.jsx(EntityForm, { fullIdPath, collection, path, entityId: entityId ?? usedEntity?.id, onValuesModified, entity, initialDirtyValues: cachedDirtyValues, openEntityMode: layout, forceActionsAtTheBottom: actionsAtTheBottom, initialStatus: status, className: ui.cls((!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : "", formProps?.className), EntityFormActionsComponent: EntityEditViewFormActions, disabled: !canEdit, ...formProps, onEntityChange: (entity_0) => {
24226
+ const entityView = /* @__PURE__ */ jsxRuntime.jsx(EntityForm, { fullIdPath, collection, path, entityId: entityId ?? usedEntity?.id, onValuesModified, entity, initialDirtyValues, openEntityMode: layout, forceActionsAtTheBottom: actionsAtTheBottom, initialStatus: status, className: ui.cls((!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : "", formProps?.className), EntityFormActionsComponent: EntityEditViewFormActions, disabled: !canEdit, ...formProps, onEntityChange: (entity_0) => {
24227
+ console.log("333 EntityEditView onEntityChange:", entity_0);
23316
24228
  setUsedEntity(entity_0);
23317
24229
  formProps?.onEntityChange?.(entity_0);
23318
24230
  }, onStatusChange: (status_0) => {
@@ -23335,7 +24247,12 @@
23335
24247
  const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
23336
24248
  let result = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col h-full w-full bg-white dark:bg-surface-900", children: [
23337
24249
  shouldShowTopBar && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls("h-14 items-center flex overflow-visible overflow-x-scroll w-full no-scrollbar h-14 border-b pl-2 pr-2 pt-1 flex bg-surface-50 dark:bg-surface-900", ui.defaultBorderMixin), children: [
23338
- barActions,
24250
+ barActions?.({
24251
+ path: fullIdPath ?? path,
24252
+ entityId,
24253
+ values: formContext?.values ?? usedEntity?.values ?? {},
24254
+ status
24255
+ }),
23339
24256
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow" }),
23340
24257
  pluginActionsTop,
23341
24258
  globalLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "self-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CircularProgress, { size: "small" }) }),
@@ -23438,9 +24355,14 @@
23438
24355
  if (!props || !collection) {
23439
24356
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full" });
23440
24357
  }
23441
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(EntityEditView, { ...props, fullIdPath, layout: "side_panel", collection, parentCollectionIds, onValuesModified, onSaved: onUpdate, barActions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
24358
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(EntityEditView, { ...props, fullIdPath, layout: "side_panel", collection, parentCollectionIds, onValuesModified, onSaved: onUpdate, barActions: ({
24359
+ status,
24360
+ values
24361
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
23442
24362
  /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { className: "self-center", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(ui.CloseIcon, { size: "small" }) }),
23443
24363
  allowFullScreen && /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { className: "self-center", onClick: () => {
24364
+ const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
24365
+ saveEntityToMemoryCache(key, values);
23444
24366
  if (entityId) navigate(location.pathname);
23445
24367
  else navigate(location.pathname + "#new");
23446
24368
  }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.OpenInFullIcon, { size: "small" }) })
@@ -26220,6 +27142,7 @@
26220
27142
  exports2.getIdIcon = getIdIcon;
26221
27143
  exports2.getLabelOrConfigFrom = getLabelOrConfigFrom;
26222
27144
  exports2.getLastSegment = getLastSegment;
27145
+ exports2.getLocalChangesBackup = getLocalChangesBackup;
26223
27146
  exports2.getPropertiesWithPropertiesOrder = getPropertiesWithPropertiesOrder;
26224
27147
  exports2.getPropertyInPath = getPropertyInPath;
26225
27148
  exports2.getRandomId = getRandomId;
@@ -26234,13 +27157,13 @@
26234
27157
  exports2.isEnumValueDisabled = isEnumValueDisabled;
26235
27158
  exports2.isHidden = isHidden;
26236
27159
  exports2.isObject = isObject;
27160
+ exports2.isPlainObject = isPlainObject;
26237
27161
  exports2.isPropertyBuilder = isPropertyBuilder;
26238
27162
  exports2.isReadOnly = isReadOnly;
26239
27163
  exports2.isReferenceProperty = isReferenceProperty;
26240
27164
  exports2.isValidRegExp = isValidRegExp;
26241
27165
  exports2.joinCollectionLists = joinCollectionLists;
26242
27166
  exports2.makePropertiesEditable = makePropertiesEditable;
26243
- exports2.makePropertiesNonEditable = makePropertiesNonEditable;
26244
27167
  exports2.mergeCallbacks = mergeCallbacks;
26245
27168
  exports2.mergeCollection = mergeCollection;
26246
27169
  exports2.mergeDeep = mergeDeep;