@firecms/core 3.0.0-canary.286 → 3.0.0-canary.288

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -2,10 +2,10 @@ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
2
  import { c } from "react-compiler-runtime";
3
3
  import * as React from "react";
4
4
  import React__default, { useRef, useEffect, useContext, useCallback, useMemo, useState, createElement, createRef, createContext, forwardRef, useLayoutEffect } from "react";
5
- import { getColorSchemeForSeed, CHIP_COLORS, FunctionsIcon, CircleIcon, iconKeys, coolIconKeys, Icon, Tooltip, ErrorIcon, Typography, IconButton, ContentCopyIcon, OpenInNewIcon, DescriptionIcon, cls, Skeleton, Chip, defaultBorderMixin, KeyboardTabIcon, Checkbox, AccountCircleIcon, Markdown, TextareaAutosize, focusedDisabled, MultiSelect, MultiSelectItem, Select, SelectItem, BooleanSwitch, DateTimeField, paperMixin, EditIcon, DoNotDisturbOnIcon, Menu, MenuItem, MoreVertIcon, CircularProgress, SearchBar, Badge, ArrowUpwardIcon, Popover, FilterListIcon, Button, CenteredView, AssignmentIcon, Label, CloseIcon, TextField, BooleanSwitchWithLabel, useOutsideAlerter, Dialog, DialogTitle, DialogContent, DialogActions, FileCopyIcon, DeleteIcon, AddIcon, StarIcon, Collapse, ExpandablePanel, ArrowForwardIcon, Card, cardMixin, cardClickableMixin, Container, LoadingButton, Alert, CheckIcon, NotesIcon, InfoIcon, fieldBackgroundMixin, RemoveIcon, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, ArrowDropDownIcon, FilterListOffIcon, SearchIcon, Avatar, DarkModeIcon, LightModeIcon, BrightnessMediumIcon, LogoutIcon, HandleIcon, KeyboardArrowUpIcon, KeyboardArrowDownIcon, debounce, Sheet, Tab, Tabs, CodeIcon, OpenInFullIcon, ViewStreamIcon, RepeatIcon, BallotIcon, ScheduleIcon, AddLinkIcon, LinkIcon, DriveFolderUploadIcon, UploadFileIcon, FormatListNumberedIcon, NumbersIcon, PersonIcon, ListAltIcon, ListIcon, FlagIcon, MailIcon, HttpIcon, FormatQuoteIcon, SubjectIcon, ShortTextIcon, MenuIcon, ChevronLeftIcon } from "@firecms/ui";
5
+ import { getColorSchemeForSeed, CHIP_COLORS, FunctionsIcon, CircleIcon, iconKeys, coolIconKeys, Icon, Tooltip, ErrorIcon, Typography, IconButton, ContentCopyIcon, OpenInNewIcon, DescriptionIcon, cls, Skeleton, Chip, defaultBorderMixin, KeyboardTabIcon, Checkbox, AccountCircleIcon, Markdown, TextareaAutosize, focusedDisabled, MultiSelect, MultiSelectItem, Select, SelectItem, BooleanSwitch, DateTimeField, paperMixin, EditIcon, DoNotDisturbOnIcon, Menu, MenuItem, MoreVertIcon, CircularProgress, SearchBar, Badge, ArrowUpwardIcon, Popover, FilterListIcon, Button, CenteredView, AssignmentIcon, Label, CloseIcon, TextField, BooleanSwitchWithLabel, useOutsideAlerter, Dialog, DialogTitle, DialogContent, DialogActions, FileCopyIcon, DeleteIcon, AddIcon, StarIcon, Collapse, ExpandablePanel, ArrowForwardIcon, Card, cardMixin, cardClickableMixin, Container, LoadingButton, WarningIcon, KeyboardArrowDownIcon, VisibilityIcon, CheckIcon, CancelIcon, Alert, NotesIcon, InfoIcon, fieldBackgroundMixin, RemoveIcon, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, ArrowDropDownIcon, FilterListOffIcon, SearchIcon, Avatar, DarkModeIcon, LightModeIcon, BrightnessMediumIcon, LogoutIcon, HandleIcon, KeyboardArrowUpIcon, debounce, Sheet, Tab, Tabs, CodeIcon, OpenInFullIcon, ViewStreamIcon, RepeatIcon, BallotIcon, ScheduleIcon, AddLinkIcon, LinkIcon, DriveFolderUploadIcon, UploadFileIcon, FormatListNumberedIcon, NumbersIcon, PersonIcon, ListAltIcon, ListIcon, FlagIcon, MailIcon, HttpIcon, FormatQuoteIcon, SubjectIcon, ShortTextIcon, MenuIcon, ChevronLeftIcon } from "@firecms/ui";
6
6
  import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack";
7
7
  import hash from "object-hash";
8
- import { getIn, useFormex, setIn, useCreateFormex, flattenKeys, Formex, Field } from "@firecms/formex";
8
+ import { getIn, useFormex, setIn, useCreateFormex, Formex, Field } from "@firecms/formex";
9
9
  import { useNavigate, useLocation, Link, NavLink, Routes, Route, createBrowserRouter, RouterProvider } from "react-router-dom";
10
10
  import Fuse from "fuse.js";
11
11
  import equal from "react-fast-compare";
@@ -255,6 +255,13 @@ const pick = (obj, ...args) => ({
255
255
  function isObject(item) {
256
256
  return item && typeof item === "object" && !Array.isArray(item);
257
257
  }
258
+ function isPlainObject(obj) {
259
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
260
+ return false;
261
+ }
262
+ const proto = Object.getPrototypeOf(obj);
263
+ return proto === Object.prototype;
264
+ }
258
265
  function mergeDeep(target, source, ignoreUndefined = false) {
259
266
  if (!isObject(target)) {
260
267
  return target;
@@ -275,7 +282,28 @@ function mergeDeep(target, source, ignoreUndefined = false) {
275
282
  if (sourceValue instanceof Date) {
276
283
  output[key] = new Date(sourceValue.getTime());
277
284
  } else if (Array.isArray(sourceValue)) {
278
- output[key] = [...sourceValue];
285
+ if (Array.isArray(outputValue)) {
286
+ const newArray = [];
287
+ const maxLength = Math.max(outputValue.length, sourceValue.length);
288
+ for (let i = 0; i < maxLength; i++) {
289
+ const sourceItem = sourceValue[i];
290
+ const targetItem = outputValue[i];
291
+ if (i >= sourceValue.length) {
292
+ newArray[i] = targetItem;
293
+ } else if (i >= outputValue.length) {
294
+ newArray[i] = sourceItem;
295
+ } else if (sourceItem === null) {
296
+ newArray[i] = targetItem;
297
+ } else if (isObject(sourceItem) && isObject(targetItem)) {
298
+ newArray[i] = mergeDeep(targetItem, sourceItem, ignoreUndefined);
299
+ } else {
300
+ newArray[i] = sourceItem;
301
+ }
302
+ }
303
+ output[key] = newArray;
304
+ } else {
305
+ output[key] = [...sourceValue];
306
+ }
279
307
  } else if (isObject(sourceValue)) {
280
308
  if (isObject(outputValue)) {
281
309
  output[key] = mergeDeep(outputValue, sourceValue, ignoreUndefined);
@@ -1054,6 +1082,12 @@ const applyPermissionsFunctionIfEmpty = (collections, permissionsBuilder) => {
1054
1082
  };
1055
1083
  });
1056
1084
  };
1085
+ function getLocalChangesBackup(collection) {
1086
+ if (!collection.localChangesBackup) {
1087
+ return "manual_apply";
1088
+ }
1089
+ return collection.localChangesBackup;
1090
+ }
1057
1091
  const kebabCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g;
1058
1092
  const toKebabCase = (str) => {
1059
1093
  const regExpMatchArray = str.match(kebabCaseRegex);
@@ -6771,7 +6805,7 @@ function BooleanPreview(t0) {
6771
6805
  return t3;
6772
6806
  }
6773
6807
  function NumberPropertyPreview(t0) {
6774
- const $ = c(10);
6808
+ const $ = c(12);
6775
6809
  const {
6776
6810
  value,
6777
6811
  property,
@@ -6789,38 +6823,42 @@ function NumberPropertyPreview(t0) {
6789
6823
  }
6790
6824
  const enumValues = t1;
6791
6825
  if (!enumValues) {
6792
- let t22;
6793
- if ($[2] !== value) {
6794
- t22 = /* @__PURE__ */ jsx(Fragment, { children: value });
6795
- $[2] = value;
6796
- $[3] = t22;
6826
+ const t22 = size === "small" ? "text-sm" : "";
6827
+ let t32;
6828
+ if ($[2] !== t22 || $[3] !== value) {
6829
+ t32 = /* @__PURE__ */ jsx("span", { className: t22, children: value });
6830
+ $[2] = t22;
6831
+ $[3] = value;
6832
+ $[4] = t32;
6797
6833
  } else {
6798
- t22 = $[3];
6834
+ t32 = $[4];
6799
6835
  }
6800
- return t22;
6836
+ return t32;
6801
6837
  }
6802
6838
  const t2 = size !== "medium" ? "small" : "medium";
6803
6839
  let t3;
6804
- if ($[4] !== enumKey || $[5] !== enumValues || $[6] !== t2) {
6840
+ if ($[5] !== enumKey || $[6] !== enumValues || $[7] !== t2) {
6805
6841
  t3 = /* @__PURE__ */ jsx(EnumValuesChip, { enumKey, enumValues, size: t2 });
6806
- $[4] = enumKey;
6807
- $[5] = enumValues;
6808
- $[6] = t2;
6809
- $[7] = t3;
6842
+ $[5] = enumKey;
6843
+ $[6] = enumValues;
6844
+ $[7] = t2;
6845
+ $[8] = t3;
6810
6846
  } else {
6811
- t3 = $[7];
6847
+ t3 = $[8];
6812
6848
  }
6813
6849
  return t3;
6814
6850
  } else {
6815
- let t1;
6816
- if ($[8] !== value) {
6817
- t1 = /* @__PURE__ */ jsx(Fragment, { children: value });
6818
- $[8] = value;
6851
+ const t1 = size === "small" ? "text-sm" : "";
6852
+ let t2;
6853
+ if ($[9] !== t1 || $[10] !== value) {
6854
+ t2 = /* @__PURE__ */ jsx("span", { className: t1, children: value });
6819
6855
  $[9] = t1;
6856
+ $[10] = value;
6857
+ $[11] = t2;
6820
6858
  } else {
6821
- t1 = $[9];
6859
+ t2 = $[11];
6822
6860
  }
6823
- return t1;
6861
+ return t2;
6824
6862
  }
6825
6863
  }
6826
6864
  function UserDisplay(t0) {
@@ -7369,6 +7407,389 @@ const AsyncPreviewComponent = React.memo(function AsyncPreviewComponentInternal(
7369
7407
  }
7370
7408
  return t3;
7371
7409
  });
7410
+ function buildPropertyLabelAndGetProperty(properties, key) {
7411
+ if (!key) return {
7412
+ label: "",
7413
+ property: void 0
7414
+ };
7415
+ const segments = [];
7416
+ const re = /([^[.\]]+)|\[(\d+)\]/g;
7417
+ let m;
7418
+ while ((m = re.exec(key)) !== null) {
7419
+ if (m[1] !== void 0) segments.push(m[1]);
7420
+ else if (m[2] !== void 0) segments.push(Number(m[2]));
7421
+ }
7422
+ let currentProps = properties;
7423
+ let currentProp;
7424
+ let lastLabel = "";
7425
+ const getArrayOfProp = (p) => {
7426
+ if (!p || p.dataType !== "array") return void 0;
7427
+ return Array.isArray(p.of) ? p.of[0] : p.of;
7428
+ };
7429
+ for (const seg of segments) {
7430
+ if (typeof seg === "number") {
7431
+ lastLabel = `[${seg}]`;
7432
+ if (currentProp?.dataType === "array") {
7433
+ currentProp = getArrayOfProp(currentProp);
7434
+ if (currentProp?.dataType === "map" && currentProp.properties) {
7435
+ currentProps = currentProp.properties;
7436
+ } else {
7437
+ currentProps = void 0;
7438
+ }
7439
+ } else {
7440
+ currentProp = void 0;
7441
+ currentProps = void 0;
7442
+ }
7443
+ continue;
7444
+ }
7445
+ if (currentProps && currentProps[seg]) {
7446
+ const nextProp = currentProps[seg];
7447
+ currentProp = nextProp;
7448
+ lastLabel = nextProp.name || String(seg);
7449
+ if (nextProp.dataType === "map" && nextProp.properties) {
7450
+ currentProps = nextProp.properties;
7451
+ } else if (nextProp.dataType === "array") {
7452
+ currentProps = void 0;
7453
+ } else {
7454
+ currentProps = void 0;
7455
+ }
7456
+ } else {
7457
+ currentProp = void 0;
7458
+ currentProps = void 0;
7459
+ lastLabel = String(seg);
7460
+ }
7461
+ }
7462
+ return {
7463
+ label: lastLabel,
7464
+ property: currentProp
7465
+ };
7466
+ }
7467
+ const pathEndsWithIndex = (p) => /\[\d+\]$/.test(p);
7468
+ const PropertyCollectionView = (t0) => {
7469
+ const $ = c(89);
7470
+ const {
7471
+ data,
7472
+ properties,
7473
+ baseKey: t1,
7474
+ suppressHeader: t2,
7475
+ size: t3
7476
+ } = t0;
7477
+ const baseKey = t1 === void 0 ? "" : t1;
7478
+ const suppressHeader = t2 === void 0 ? false : t2;
7479
+ const size = t3 === void 0 ? "small" : t3;
7480
+ const isTopLevel = !!baseKey && !baseKey.includes(".") && !baseKey.includes("[");
7481
+ if (Array.isArray(data)) {
7482
+ let t4;
7483
+ if ($[0] !== baseKey || $[1] !== properties) {
7484
+ t4 = baseKey ? buildPropertyLabelAndGetProperty(properties, baseKey) : {
7485
+ label: "",
7486
+ property: void 0
7487
+ };
7488
+ $[0] = baseKey;
7489
+ $[1] = properties;
7490
+ $[2] = t4;
7491
+ } else {
7492
+ t4 = $[2];
7493
+ }
7494
+ const {
7495
+ label: arrayLabel,
7496
+ property
7497
+ } = t4;
7498
+ const ofProp = property?.dataType === "array" ? Array.isArray(property.of) ? property.of[0] : property.of : void 0;
7499
+ const isArrayOfMaps = ofProp?.dataType === "map";
7500
+ const isArrayOfPrimitives = property?.dataType === "array" && ofProp && ofProp.dataType !== "map";
7501
+ if (baseKey && property && isArrayOfPrimitives) {
7502
+ const t52 = `grid grid-cols-12 gap-x-4 ${isTopLevel ? "py-4" : "py-2"} items-start ${isTopLevel ? `border-b ${defaultBorderMixin}` : ""}`;
7503
+ let t62;
7504
+ if ($[3] !== arrayLabel) {
7505
+ t62 = /* @__PURE__ */ jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: arrayLabel }) });
7506
+ $[3] = arrayLabel;
7507
+ $[4] = t62;
7508
+ } else {
7509
+ t62 = $[4];
7510
+ }
7511
+ let t72;
7512
+ if ($[5] !== baseKey || $[6] !== data || $[7] !== property || $[8] !== size) {
7513
+ t72 = /* @__PURE__ */ jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsx(PropertyPreview, { propertyKey: baseKey, value: data, property, size }) });
7514
+ $[5] = baseKey;
7515
+ $[6] = data;
7516
+ $[7] = property;
7517
+ $[8] = size;
7518
+ $[9] = t72;
7519
+ } else {
7520
+ t72 = $[9];
7521
+ }
7522
+ let t82;
7523
+ if ($[10] !== t52 || $[11] !== t62 || $[12] !== t72) {
7524
+ t82 = /* @__PURE__ */ jsxs("div", { className: t52, children: [
7525
+ t62,
7526
+ t72
7527
+ ] });
7528
+ $[10] = t52;
7529
+ $[11] = t62;
7530
+ $[12] = t72;
7531
+ $[13] = t82;
7532
+ } else {
7533
+ t82 = $[13];
7534
+ }
7535
+ return t82;
7536
+ }
7537
+ const t5 = `${isTopLevel ? "py-4" : "py-1"} ${isTopLevel ? `border-b ${defaultBorderMixin}` : ""}`;
7538
+ let t6;
7539
+ if ($[14] !== arrayLabel || $[15] !== baseKey || $[16] !== suppressHeader) {
7540
+ t6 = baseKey && arrayLabel && !suppressHeader && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", children: arrayLabel });
7541
+ $[14] = arrayLabel;
7542
+ $[15] = baseKey;
7543
+ $[16] = suppressHeader;
7544
+ $[17] = t6;
7545
+ } else {
7546
+ t6 = $[17];
7547
+ }
7548
+ const t7 = baseKey ? `pl-4 mt-1 border-l ${defaultBorderMixin}` : "";
7549
+ let t8;
7550
+ if ($[18] !== baseKey || $[19] !== data || $[20] !== isArrayOfMaps || $[21] !== ofProp || $[22] !== properties || $[23] !== size) {
7551
+ let t92;
7552
+ if ($[25] !== baseKey || $[26] !== isArrayOfMaps || $[27] !== ofProp || $[28] !== properties || $[29] !== size) {
7553
+ t92 = (item, index) => {
7554
+ if (item === null || item === void 0) {
7555
+ return null;
7556
+ }
7557
+ const currentKey = baseKey ? `${baseKey}[${index}]` : `[${index}]`;
7558
+ const itemHeader = isArrayOfMaps && ofProp?.name ? `${ofProp.name} [${index}]` : `[${index}]`;
7559
+ return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
7560
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", children: itemHeader }),
7561
+ /* @__PURE__ */ jsx("div", { className: `pl-4 mt-1 border-l ${defaultBorderMixin}`, children: /* @__PURE__ */ jsx(PropertyCollectionView, { data: item, properties, baseKey: currentKey, suppressHeader: true, size }) })
7562
+ ] }, currentKey);
7563
+ };
7564
+ $[25] = baseKey;
7565
+ $[26] = isArrayOfMaps;
7566
+ $[27] = ofProp;
7567
+ $[28] = properties;
7568
+ $[29] = size;
7569
+ $[30] = t92;
7570
+ } else {
7571
+ t92 = $[30];
7572
+ }
7573
+ t8 = data.map(t92);
7574
+ $[18] = baseKey;
7575
+ $[19] = data;
7576
+ $[20] = isArrayOfMaps;
7577
+ $[21] = ofProp;
7578
+ $[22] = properties;
7579
+ $[23] = size;
7580
+ $[24] = t8;
7581
+ } else {
7582
+ t8 = $[24];
7583
+ }
7584
+ let t9;
7585
+ if ($[31] !== t7 || $[32] !== t8) {
7586
+ t9 = /* @__PURE__ */ jsx("div", { className: t7, children: t8 });
7587
+ $[31] = t7;
7588
+ $[32] = t8;
7589
+ $[33] = t9;
7590
+ } else {
7591
+ t9 = $[33];
7592
+ }
7593
+ let t10;
7594
+ if ($[34] !== t5 || $[35] !== t6 || $[36] !== t9) {
7595
+ t10 = /* @__PURE__ */ jsxs("div", { className: t5, children: [
7596
+ t6,
7597
+ t9
7598
+ ] });
7599
+ $[34] = t5;
7600
+ $[35] = t6;
7601
+ $[36] = t9;
7602
+ $[37] = t10;
7603
+ } else {
7604
+ t10 = $[37];
7605
+ }
7606
+ return t10;
7607
+ }
7608
+ if (typeof data === "object" && data !== null) {
7609
+ let t4;
7610
+ if ($[38] !== baseKey || $[39] !== properties) {
7611
+ t4 = baseKey ? buildPropertyLabelAndGetProperty(properties, baseKey) : {
7612
+ label: "",
7613
+ property: void 0
7614
+ };
7615
+ $[38] = baseKey;
7616
+ $[39] = properties;
7617
+ $[40] = t4;
7618
+ } else {
7619
+ t4 = $[40];
7620
+ }
7621
+ const {
7622
+ label,
7623
+ property: property_0
7624
+ } = t4;
7625
+ if (baseKey && (!property_0 || property_0.dataType !== "map" || !property_0.properties)) {
7626
+ if (!property_0) {
7627
+ return null;
7628
+ }
7629
+ const t52 = `grid grid-cols-12 gap-x-4 ${isTopLevel ? "py-4" : "py-2"} items-start ${isTopLevel ? `border-b ${defaultBorderMixin}` : ""}`;
7630
+ let t62;
7631
+ if ($[41] !== label) {
7632
+ t62 = /* @__PURE__ */ jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: label }) });
7633
+ $[41] = label;
7634
+ $[42] = t62;
7635
+ } else {
7636
+ t62 = $[42];
7637
+ }
7638
+ let t72;
7639
+ if ($[43] !== baseKey || $[44] !== data || $[45] !== property_0 || $[46] !== size) {
7640
+ t72 = /* @__PURE__ */ jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsx(PropertyPreview, { propertyKey: baseKey, value: data, property: property_0, size }) });
7641
+ $[43] = baseKey;
7642
+ $[44] = data;
7643
+ $[45] = property_0;
7644
+ $[46] = size;
7645
+ $[47] = t72;
7646
+ } else {
7647
+ t72 = $[47];
7648
+ }
7649
+ let t82;
7650
+ if ($[48] !== t52 || $[49] !== t62 || $[50] !== t72) {
7651
+ t82 = /* @__PURE__ */ jsxs("div", { className: t52, children: [
7652
+ t62,
7653
+ t72
7654
+ ] });
7655
+ $[48] = t52;
7656
+ $[49] = t62;
7657
+ $[50] = t72;
7658
+ $[51] = t82;
7659
+ } else {
7660
+ t82 = $[51];
7661
+ }
7662
+ return t82;
7663
+ }
7664
+ let t5;
7665
+ if ($[52] !== baseKey || $[53] !== property_0 || $[54] !== suppressHeader) {
7666
+ t5 = baseKey && !suppressHeader && property_0?.dataType === "map" && (property_0.name || !pathEndsWithIndex(baseKey));
7667
+ $[52] = baseKey;
7668
+ $[53] = property_0;
7669
+ $[54] = suppressHeader;
7670
+ $[55] = t5;
7671
+ } else {
7672
+ t5 = $[55];
7673
+ }
7674
+ const showMapHeader = t5;
7675
+ const headerText = property_0?.name || label;
7676
+ const t6 = `${isTopLevel ? "py-4" : "py-1"} ${isTopLevel ? `border-b ${defaultBorderMixin}` : ""}`;
7677
+ let t7;
7678
+ if ($[56] !== headerText || $[57] !== showMapHeader) {
7679
+ t7 = showMapHeader && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", children: headerText });
7680
+ $[56] = headerText;
7681
+ $[57] = showMapHeader;
7682
+ $[58] = t7;
7683
+ } else {
7684
+ t7 = $[58];
7685
+ }
7686
+ const t8 = baseKey ? `pl-4 mt-1 border-l ${defaultBorderMixin}` : "";
7687
+ let t9;
7688
+ if ($[59] !== baseKey || $[60] !== data || $[61] !== properties || $[62] !== size) {
7689
+ let t102;
7690
+ if ($[64] !== baseKey || $[65] !== properties || $[66] !== size) {
7691
+ t102 = (t112) => {
7692
+ const [key, value] = t112;
7693
+ if (value === null || value === void 0) {
7694
+ return null;
7695
+ }
7696
+ const currentKey_0 = baseKey ? `${baseKey}.${key}` : key;
7697
+ return /* @__PURE__ */ jsx(PropertyCollectionView, { data: value, properties, baseKey: currentKey_0, size }, currentKey_0);
7698
+ };
7699
+ $[64] = baseKey;
7700
+ $[65] = properties;
7701
+ $[66] = size;
7702
+ $[67] = t102;
7703
+ } else {
7704
+ t102 = $[67];
7705
+ }
7706
+ t9 = Object.entries(data).map(t102);
7707
+ $[59] = baseKey;
7708
+ $[60] = data;
7709
+ $[61] = properties;
7710
+ $[62] = size;
7711
+ $[63] = t9;
7712
+ } else {
7713
+ t9 = $[63];
7714
+ }
7715
+ let t10;
7716
+ if ($[68] !== t8 || $[69] !== t9) {
7717
+ t10 = /* @__PURE__ */ jsx("div", { className: t8, children: t9 });
7718
+ $[68] = t8;
7719
+ $[69] = t9;
7720
+ $[70] = t10;
7721
+ } else {
7722
+ t10 = $[70];
7723
+ }
7724
+ let t11;
7725
+ if ($[71] !== t10 || $[72] !== t6 || $[73] !== t7) {
7726
+ t11 = /* @__PURE__ */ jsxs("div", { className: t6, children: [
7727
+ t7,
7728
+ t10
7729
+ ] });
7730
+ $[71] = t10;
7731
+ $[72] = t6;
7732
+ $[73] = t7;
7733
+ $[74] = t11;
7734
+ } else {
7735
+ t11 = $[74];
7736
+ }
7737
+ return t11;
7738
+ }
7739
+ if (baseKey) {
7740
+ let t4;
7741
+ if ($[75] !== baseKey || $[76] !== properties) {
7742
+ t4 = buildPropertyLabelAndGetProperty(properties, baseKey);
7743
+ $[75] = baseKey;
7744
+ $[76] = properties;
7745
+ $[77] = t4;
7746
+ } else {
7747
+ t4 = $[77];
7748
+ }
7749
+ const {
7750
+ label: label_0,
7751
+ property: property_1
7752
+ } = t4;
7753
+ if (!property_1) {
7754
+ return null;
7755
+ }
7756
+ const t5 = `grid grid-cols-12 gap-x-4 ${isTopLevel ? "py-4" : "py-2"} items-start ${isTopLevel ? `border-b ${defaultBorderMixin}` : ""}`;
7757
+ let t6;
7758
+ if ($[78] !== label_0) {
7759
+ t6 = /* @__PURE__ */ jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: label_0 }) });
7760
+ $[78] = label_0;
7761
+ $[79] = t6;
7762
+ } else {
7763
+ t6 = $[79];
7764
+ }
7765
+ let t7;
7766
+ if ($[80] !== baseKey || $[81] !== data || $[82] !== property_1 || $[83] !== size) {
7767
+ t7 = /* @__PURE__ */ jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsx(PropertyPreview, { propertyKey: baseKey, value: data, property: property_1, size }) });
7768
+ $[80] = baseKey;
7769
+ $[81] = data;
7770
+ $[82] = property_1;
7771
+ $[83] = size;
7772
+ $[84] = t7;
7773
+ } else {
7774
+ t7 = $[84];
7775
+ }
7776
+ let t8;
7777
+ if ($[85] !== t5 || $[86] !== t6 || $[87] !== t7) {
7778
+ t8 = /* @__PURE__ */ jsxs("div", { className: t5, children: [
7779
+ t6,
7780
+ t7
7781
+ ] });
7782
+ $[85] = t5;
7783
+ $[86] = t6;
7784
+ $[87] = t7;
7785
+ $[88] = t8;
7786
+ } else {
7787
+ t8 = $[88];
7788
+ }
7789
+ return t8;
7790
+ }
7791
+ return null;
7792
+ };
7372
7793
  function EntityView({
7373
7794
  entity,
7374
7795
  collection,
@@ -7386,31 +7807,17 @@ function EntityView({
7386
7807
  authController
7387
7808
  }), [collection, path, entity, customizationController.propertyConfigs]);
7388
7809
  const properties = resolvedCollection.properties;
7389
- return /* @__PURE__ */ jsx("div", { className: "w-full " + className, children: /* @__PURE__ */ jsxs("div", { className: "w-full mb-4", children: [
7390
- /* @__PURE__ */ jsxs("div", { className: cls(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0"), children: [
7391
- /* @__PURE__ */ jsx("div", { className: "flex items-center w-1/4", children: /* @__PURE__ */ jsx("span", { className: "pl-2 text-sm text-surface-600", children: "Id" }) }),
7392
- /* @__PURE__ */ 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: [
7810
+ return /* @__PURE__ */ jsx("div", { className: "w-full " + className, children: /* @__PURE__ */ jsxs("div", { className: "w-full mb-4 p-4", children: [
7811
+ /* @__PURE__ */ jsxs("div", { className: `grid grid-cols-12 gap-x-4 py-4 items-start border-b ${defaultBorderMixin}`, children: [
7812
+ /* @__PURE__ */ jsx("div", { className: "col-span-4 pr-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", component: "span", className: "break-words", children: "Id" }) }),
7813
+ /* @__PURE__ */ jsx("div", { className: "col-span-8", children: /* @__PURE__ */ jsxs("div", { className: "flex-grow text-surface-900 dark:text-white flex items-center", children: [
7393
7814
  /* @__PURE__ */ jsx("span", { className: "flex-grow mr-2", children: entity.id }),
7394
7815
  customizationController?.entityLinkBuilder && /* @__PURE__ */ jsx("a", { href: customizationController.entityLinkBuilder({
7395
7816
  entity
7396
7817
  }), rel: "noopener noreferrer", target: "_blank", children: /* @__PURE__ */ jsx(IconButton, { children: /* @__PURE__ */ jsx(OpenInNewIcon, { size: "small" }) }) })
7397
- ] })
7818
+ ] }) })
7398
7819
  ] }),
7399
- Object.entries(properties).map(([key, property]) => {
7400
- const value = entity.values?.[key];
7401
- return /* @__PURE__ */ jsxs("div", { className: cls(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0"), children: [
7402
- /* @__PURE__ */ jsx("div", { className: "flex items-center w-1/4", children: /* @__PURE__ */ jsx("span", { className: "pl-2 text-sm text-surface-600", children: property.name }) }),
7403
- /* @__PURE__ */ 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__ */ jsx(
7404
- PropertyPreview,
7405
- {
7406
- propertyKey: key,
7407
- value,
7408
- property,
7409
- size: "medium"
7410
- }
7411
- ) })
7412
- ] }, `reference_previews_${key}`);
7413
- })
7820
+ /* @__PURE__ */ jsx(PropertyCollectionView, { data: entity.values, properties, size: "medium" })
7414
7821
  ] }) });
7415
7822
  }
7416
7823
  function VirtualTableInput(props) {
@@ -9757,28 +10164,40 @@ function customReviver(key, value) {
9757
10164
  return value;
9758
10165
  }
9759
10166
  function saveEntityToCache(path, data) {
9760
- entityCache.set(path, data);
9761
10167
  if (isLocalStorageAvailable) {
9762
10168
  try {
9763
10169
  const key = LOCAL_STORAGE_PREFIX + path;
9764
10170
  const entityString = JSON.stringify(data, customReplacer);
10171
+ console.log("Saving entity to localStorage:", {
10172
+ key,
10173
+ entityString
10174
+ });
9765
10175
  localStorage.setItem(key, entityString);
9766
10176
  } catch (error) {
9767
10177
  console.error(`Failed to save entity for path "${path}" to localStorage:`, error);
9768
10178
  }
9769
10179
  }
9770
10180
  }
9771
- function getEntityFromCache(path, useLocalStorage = true) {
9772
- if (entityCache.has(path)) {
9773
- return entityCache.get(path);
9774
- }
9775
- if (isLocalStorageAvailable && useLocalStorage) {
10181
+ function removeEntityFromMemoryCache(path) {
10182
+ entityCache.delete(path);
10183
+ }
10184
+ function saveEntityToMemoryCache(path, data) {
10185
+ entityCache.set(path, data);
10186
+ }
10187
+ function getEntityFromMemoryCache(path) {
10188
+ return entityCache.get(path);
10189
+ }
10190
+ function getEntityFromCache(path) {
10191
+ if (isLocalStorageAvailable) {
9776
10192
  try {
9777
10193
  const key = LOCAL_STORAGE_PREFIX + path;
9778
10194
  const entityString = localStorage.getItem(key);
9779
10195
  if (entityString) {
9780
10196
  const entity = JSON.parse(entityString, customReviver);
9781
- entityCache.set(path, entity);
10197
+ console.log("Loaded entity from localStorage:", {
10198
+ key,
10199
+ entity
10200
+ });
9782
10201
  return entity;
9783
10202
  }
9784
10203
  } catch (error) {
@@ -9787,12 +10206,7 @@ function getEntityFromCache(path, useLocalStorage = true) {
9787
10206
  }
9788
10207
  return void 0;
9789
10208
  }
9790
- function hasEntityInCache(path) {
9791
- return entityCache.has(path);
9792
- }
9793
10209
  function removeEntityFromCache(path) {
9794
- console.debug("Removing entity from cache", path);
9795
- entityCache.delete(path);
9796
10210
  if (isLocalStorageAvailable) {
9797
10211
  try {
9798
10212
  const key = LOCAL_STORAGE_PREFIX + path;
@@ -9802,6 +10216,26 @@ function removeEntityFromCache(path) {
9802
10216
  }
9803
10217
  }
9804
10218
  }
10219
+ function flattenKeys(obj, prefix = "", result = []) {
10220
+ if (isObject(obj) || Array.isArray(obj)) {
10221
+ const plainObject = isPlainObject(obj);
10222
+ if (!plainObject && prefix) {
10223
+ result.push(prefix);
10224
+ } else {
10225
+ for (const key in obj) {
10226
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
10227
+ const newKey = prefix ? Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}.${key}` : key;
10228
+ if (isObject(obj[key]) || Array.isArray(obj[key])) {
10229
+ flattenKeys(obj[key], newKey, result);
10230
+ } else {
10231
+ result.push(newKey);
10232
+ }
10233
+ }
10234
+ }
10235
+ }
10236
+ }
10237
+ return result;
10238
+ }
9805
10239
  const EntityCollectionRowActions = function EntityCollectionRowActions2({
9806
10240
  entity,
9807
10241
  collection,
@@ -9829,8 +10263,8 @@ const EntityCollectionRowActions = function EntityCollectionRowActions2({
9829
10263
  const hasCollapsedActions = actions.some((a) => a.collapsed || a.collapsed === void 0);
9830
10264
  const collapsedActions = actions.filter((a_0) => a_0.collapsed || a_0.collapsed === void 0);
9831
10265
  const uncollapsedActions = actions.filter((a_1) => a_1.collapsed === false);
9832
- const enableLocalChangesBackup = collection?.enableLocalChangesBackup !== void 0 ? collection?.enableLocalChangesBackup : true;
9833
- const hasDraft = enableLocalChangesBackup ? hasEntityInCache(fullPath + "/" + entity.id) : false;
10266
+ const enableLocalChangesBackup = collection ? getLocalChangesBackup(collection) : false;
10267
+ const hasDraft = enableLocalChangesBackup ? getEntityFromCache(fullPath + "/" + entity.id) : false;
9834
10268
  return /* @__PURE__ */ jsxs("div", { className: 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: useCallback((event) => {
9835
10269
  event.stopPropagation();
9836
10270
  }, []), style: {
@@ -15165,6 +15599,262 @@ function buildSideActions$1({
15165
15599
  savingError && /* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsx(Typography, { color: "error", children: savingError.message }) })
15166
15600
  ] });
15167
15601
  }
15602
+ function LocalChangesMenu(t0) {
15603
+ const $ = c(42);
15604
+ const {
15605
+ localChangesData,
15606
+ formex,
15607
+ onClearLocalChanges,
15608
+ cacheKey,
15609
+ properties
15610
+ } = t0;
15611
+ const snackbarController = useSnackbarController();
15612
+ const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
15613
+ const [open, setOpen] = useState(false);
15614
+ let t1;
15615
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
15616
+ t1 = () => setOpen(true);
15617
+ $[0] = t1;
15618
+ } else {
15619
+ t1 = $[0];
15620
+ }
15621
+ const handleOpenMenu = t1;
15622
+ let t2;
15623
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
15624
+ t2 = () => setOpen(false);
15625
+ $[1] = t2;
15626
+ } else {
15627
+ t2 = $[1];
15628
+ }
15629
+ const handleCloseMenu = t2;
15630
+ let t3;
15631
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
15632
+ t3 = () => {
15633
+ setPreviewDialogOpen(true);
15634
+ handleCloseMenu();
15635
+ };
15636
+ $[2] = t3;
15637
+ } else {
15638
+ t3 = $[2];
15639
+ }
15640
+ const handlePreview = t3;
15641
+ let t4;
15642
+ if ($[3] !== formex || $[4] !== localChangesData || $[5] !== onClearLocalChanges || $[6] !== snackbarController) {
15643
+ t4 = () => {
15644
+ const mergedValues = mergeDeep(formex.values, localChangesData);
15645
+ const touched = {
15646
+ ...formex.touched
15647
+ };
15648
+ const previewKeys = flattenKeys(localChangesData);
15649
+ previewKeys.forEach((key) => {
15650
+ touched[key] = true;
15651
+ });
15652
+ formex.setTouched(touched);
15653
+ formex.setValues(mergedValues);
15654
+ snackbarController.open({
15655
+ type: "info",
15656
+ message: "Local changes applied to the form"
15657
+ });
15658
+ handleCloseMenu();
15659
+ onClearLocalChanges?.();
15660
+ };
15661
+ $[3] = formex;
15662
+ $[4] = localChangesData;
15663
+ $[5] = onClearLocalChanges;
15664
+ $[6] = snackbarController;
15665
+ $[7] = t4;
15666
+ } else {
15667
+ t4 = $[7];
15668
+ }
15669
+ const handleApply = t4;
15670
+ let t5;
15671
+ if ($[8] !== cacheKey || $[9] !== onClearLocalChanges || $[10] !== snackbarController) {
15672
+ t5 = () => {
15673
+ removeEntityFromCache(cacheKey);
15674
+ snackbarController.open({
15675
+ type: "info",
15676
+ message: "Local changes discarded"
15677
+ });
15678
+ handleCloseMenu();
15679
+ onClearLocalChanges?.();
15680
+ };
15681
+ $[8] = cacheKey;
15682
+ $[9] = onClearLocalChanges;
15683
+ $[10] = snackbarController;
15684
+ $[11] = t5;
15685
+ } else {
15686
+ t5 = $[11];
15687
+ }
15688
+ const handleDiscard = t5;
15689
+ let t6;
15690
+ if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
15691
+ t6 = /* @__PURE__ */ jsx(WarningIcon, { size: "smallest", className: "mr-1 text-yellow-600 dark:text-yellow-400" });
15692
+ $[12] = t6;
15693
+ } else {
15694
+ t6 = $[12];
15695
+ }
15696
+ let t7;
15697
+ if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
15698
+ t7 = /* @__PURE__ */ jsxs(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: [
15699
+ t6,
15700
+ "Unsaved Local changes",
15701
+ /* @__PURE__ */ jsx(KeyboardArrowDownIcon, { size: "smallest" })
15702
+ ] });
15703
+ $[13] = t7;
15704
+ } else {
15705
+ t7 = $[13];
15706
+ }
15707
+ let t8;
15708
+ if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
15709
+ t8 = /* @__PURE__ */ 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." });
15710
+ $[14] = t8;
15711
+ } else {
15712
+ t8 = $[14];
15713
+ }
15714
+ let t9;
15715
+ if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
15716
+ t9 = /* @__PURE__ */ jsxs(MenuItem, { dense: true, onClick: handlePreview, children: [
15717
+ /* @__PURE__ */ jsx(VisibilityIcon, { size: "small" }),
15718
+ "Preview Changes"
15719
+ ] });
15720
+ $[15] = t9;
15721
+ } else {
15722
+ t9 = $[15];
15723
+ }
15724
+ let t10;
15725
+ if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
15726
+ t10 = /* @__PURE__ */ jsx(CheckIcon, { size: "small" });
15727
+ $[16] = t10;
15728
+ } else {
15729
+ t10 = $[16];
15730
+ }
15731
+ let t11;
15732
+ if ($[17] !== handleApply) {
15733
+ t11 = /* @__PURE__ */ jsxs(MenuItem, { dense: true, onClick: handleApply, children: [
15734
+ t10,
15735
+ "Apply Changes"
15736
+ ] });
15737
+ $[17] = handleApply;
15738
+ $[18] = t11;
15739
+ } else {
15740
+ t11 = $[18];
15741
+ }
15742
+ let t12;
15743
+ if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
15744
+ t12 = /* @__PURE__ */ jsx(CancelIcon, { size: "small" });
15745
+ $[19] = t12;
15746
+ } else {
15747
+ t12 = $[19];
15748
+ }
15749
+ let t13;
15750
+ if ($[20] !== handleDiscard) {
15751
+ t13 = /* @__PURE__ */ jsxs(MenuItem, { dense: true, onClick: handleDiscard, children: [
15752
+ t12,
15753
+ "Discard Local Changes"
15754
+ ] });
15755
+ $[20] = handleDiscard;
15756
+ $[21] = t13;
15757
+ } else {
15758
+ t13 = $[21];
15759
+ }
15760
+ let t14;
15761
+ if ($[22] !== open || $[23] !== t11 || $[24] !== t13) {
15762
+ t14 = /* @__PURE__ */ jsxs(Menu, { trigger: t7, open, onOpenChange: setOpen, children: [
15763
+ t8,
15764
+ t9,
15765
+ t11,
15766
+ t13
15767
+ ] });
15768
+ $[22] = open;
15769
+ $[23] = t11;
15770
+ $[24] = t13;
15771
+ $[25] = t14;
15772
+ } else {
15773
+ t14 = $[25];
15774
+ }
15775
+ let t15;
15776
+ let t16;
15777
+ if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
15778
+ t15 = /* @__PURE__ */ jsx("h3", { className: "text-2xl mb-4", children: "Preview Local Changes" });
15779
+ t16 = /* @__PURE__ */ jsx("p", { className: "mb-4", children: "These are the local changes that will be applied to the form." });
15780
+ $[26] = t15;
15781
+ $[27] = t16;
15782
+ } else {
15783
+ t15 = $[26];
15784
+ t16 = $[27];
15785
+ }
15786
+ let t17;
15787
+ if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
15788
+ t17 = {
15789
+ maxHeight: 520,
15790
+ overflow: "auto"
15791
+ };
15792
+ $[28] = t17;
15793
+ } else {
15794
+ t17 = $[28];
15795
+ }
15796
+ const t18 = properties;
15797
+ let t19;
15798
+ if ($[29] !== localChangesData || $[30] !== t18) {
15799
+ t19 = /* @__PURE__ */ jsxs(DialogContent, { children: [
15800
+ t15,
15801
+ t16,
15802
+ /* @__PURE__ */ jsx("div", { className: `border rounded-lg ${defaultBorderMixin}`, style: t17, children: /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsx(PropertyCollectionView, { data: localChangesData, properties: t18 }) }) })
15803
+ ] });
15804
+ $[29] = localChangesData;
15805
+ $[30] = t18;
15806
+ $[31] = t19;
15807
+ } else {
15808
+ t19 = $[31];
15809
+ }
15810
+ let t20;
15811
+ if ($[32] === Symbol.for("react.memo_cache_sentinel")) {
15812
+ t20 = /* @__PURE__ */ jsx(Button, { onClick: () => setPreviewDialogOpen(false), children: "Close" });
15813
+ $[32] = t20;
15814
+ } else {
15815
+ t20 = $[32];
15816
+ }
15817
+ let t21;
15818
+ if ($[33] !== handleApply) {
15819
+ t21 = /* @__PURE__ */ jsxs(DialogActions, { children: [
15820
+ t20,
15821
+ /* @__PURE__ */ jsx(Button, { variant: "filled", onClick: () => {
15822
+ handleApply();
15823
+ setPreviewDialogOpen(false);
15824
+ }, children: "Apply changes" })
15825
+ ] });
15826
+ $[33] = handleApply;
15827
+ $[34] = t21;
15828
+ } else {
15829
+ t21 = $[34];
15830
+ }
15831
+ let t22;
15832
+ if ($[35] !== previewDialogOpen || $[36] !== t19 || $[37] !== t21) {
15833
+ t22 = /* @__PURE__ */ jsxs(Dialog, { open: previewDialogOpen, onOpenChange: setPreviewDialogOpen, maxWidth: "4xl", children: [
15834
+ t19,
15835
+ t21
15836
+ ] });
15837
+ $[35] = previewDialogOpen;
15838
+ $[36] = t19;
15839
+ $[37] = t21;
15840
+ $[38] = t22;
15841
+ } else {
15842
+ t22 = $[38];
15843
+ }
15844
+ let t23;
15845
+ if ($[39] !== t14 || $[40] !== t22) {
15846
+ t23 = /* @__PURE__ */ jsxs(Fragment, { children: [
15847
+ t14,
15848
+ t22
15849
+ ] });
15850
+ $[39] = t14;
15851
+ $[40] = t22;
15852
+ $[41] = t23;
15853
+ } else {
15854
+ t23 = $[41];
15855
+ }
15856
+ return t23;
15857
+ }
15168
15858
  function extractTouchedValues(values, touched) {
15169
15859
  let acc = {};
15170
15860
  if (!touched || typeof touched !== "object") {
@@ -15177,6 +15867,56 @@ function extractTouchedValues(values, touched) {
15177
15867
  });
15178
15868
  return acc;
15179
15869
  }
15870
+ function getChanges(source, comparison) {
15871
+ const changes = {};
15872
+ if (!source) {
15873
+ return {};
15874
+ }
15875
+ if (!comparison) {
15876
+ return source;
15877
+ }
15878
+ const allKeys = Array.from(/* @__PURE__ */ new Set([...Object.keys(source), ...Object.keys(comparison)]));
15879
+ for (const key of allKeys) {
15880
+ const sourceValue = source[key];
15881
+ const comparisonValue = comparison[key];
15882
+ if (equal(sourceValue, comparisonValue)) {
15883
+ continue;
15884
+ }
15885
+ const sourceHasKey = source && typeof source === "object" && Object.prototype.hasOwnProperty.call(source, key);
15886
+ const comparisonHasKey = comparison && typeof comparison === "object" && Object.prototype.hasOwnProperty.call(comparison, key);
15887
+ if (comparisonHasKey && !sourceHasKey) {
15888
+ changes[key] = void 0;
15889
+ } else if (Array.isArray(sourceValue)) {
15890
+ const comparisonArray = Array.isArray(comparisonValue) ? comparisonValue : [];
15891
+ if (sourceValue.length < comparisonArray.length) {
15892
+ changes[key] = sourceValue;
15893
+ continue;
15894
+ }
15895
+ const changedArray = sourceValue.map((item, index) => {
15896
+ const comparisonItem = comparisonArray[index];
15897
+ if (equal(item, comparisonItem)) {
15898
+ return null;
15899
+ }
15900
+ if (isObject(item) && item && isObject(comparisonItem) && comparisonItem) {
15901
+ const nestedChanges = getChanges(item, comparisonItem);
15902
+ return Object.keys(nestedChanges).length > 0 ? nestedChanges : item;
15903
+ }
15904
+ return item;
15905
+ });
15906
+ if (changedArray.some((item) => item !== null) || sourceValue.length > comparisonArray.length) {
15907
+ changes[key] = changedArray;
15908
+ }
15909
+ } else if (isObject(sourceValue) && sourceValue && isObject(comparisonValue) && comparisonValue) {
15910
+ const nestedChanges = getChanges(sourceValue, comparisonValue);
15911
+ if (Object.keys(nestedChanges).length > 0) {
15912
+ changes[key] = nestedChanges;
15913
+ }
15914
+ } else {
15915
+ changes[key] = sourceValue;
15916
+ }
15917
+ }
15918
+ return changes;
15919
+ }
15180
15920
  function EntityForm({
15181
15921
  path,
15182
15922
  fullIdPath,
@@ -15252,6 +15992,12 @@ function EntityForm({
15252
15992
  const [entityIdError, setEntityIdError] = useState(false);
15253
15993
  const [savingError, setSavingError] = useState();
15254
15994
  const autoSave = collection.formAutoSave && !collection.customId;
15995
+ const baseInitialValues = useMemo(() => getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs), [authController, collection, path, status, entity, customizationController.propertyConfigs]);
15996
+ const localChangesDataRaw = useMemo(() => entityId ? getEntityFromCache(path + "/" + entityId) : getEntityFromCache(path + "#new"), [entityId, path]);
15997
+ const [localChangesCleared, setLocalChangesCleared] = useState(false);
15998
+ const localChangesBackup = getLocalChangesBackup(collection);
15999
+ const autoApplyLocalChanges = localChangesBackup === "auto_apply";
16000
+ const manualApplyLocalChanges = localChangesBackup === "manual_apply";
15255
16001
  const onSubmit = (values, formexController) => {
15256
16002
  if (mustSetCustomId && !entityId) {
15257
16003
  console.error("Missing custom Id");
@@ -15282,12 +16028,22 @@ function EntityForm({
15282
16028
  formexController.setSubmitting(false);
15283
16029
  });
15284
16030
  };
15285
- const baseInitialValues = getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs);
15286
- const initialValues = initialDirtyValues ? mergeDeep(baseInitialValues, initialDirtyValues) : baseInitialValues;
15287
- const initialDirty = Boolean(initialDirtyValues) && initialDirtyValues && Object.keys(initialDirtyValues).length > 0;
16031
+ const [initialValues_0, initialDirty_0] = useMemo(() => {
16032
+ const initialValuesWithLocalChanges = autoApplyLocalChanges && localChangesDataRaw ? mergeDeep(baseInitialValues, localChangesDataRaw) : baseInitialValues;
16033
+ const initialValues = initialDirtyValues ? mergeDeep(initialValuesWithLocalChanges, initialDirtyValues) : initialValuesWithLocalChanges;
16034
+ const initialDirty = Boolean(initialDirtyValues) && initialDirtyValues && Object.keys(initialDirtyValues).length > 0;
16035
+ return [initialValues, initialDirty];
16036
+ }, [autoApplyLocalChanges, localChangesDataRaw, baseInitialValues, initialDirtyValues]);
16037
+ const localChangesData = useMemo(() => {
16038
+ if (!localChangesDataRaw) {
16039
+ return void 0;
16040
+ }
16041
+ return getChanges(localChangesDataRaw, initialValues_0);
16042
+ }, [localChangesDataRaw, initialValues_0]);
16043
+ const hasLocalChanges = !localChangesCleared && localChangesData && Object.keys(localChangesData).length > 0;
15288
16044
  const formex = formexProp ?? useCreateFormex({
15289
- initialValues,
15290
- initialDirty,
16045
+ initialValues: initialValues_0,
16046
+ initialDirty: initialDirty_0,
15291
16047
  initialTouched: initialDirtyValues ? flattenKeys(initialDirtyValues).reduce((previousValue, currentValue) => ({
15292
16048
  ...previousValue,
15293
16049
  [currentValue]: true
@@ -15295,7 +16051,7 @@ function EntityForm({
15295
16051
  onSubmit,
15296
16052
  onReset: () => {
15297
16053
  clearDirtyCache();
15298
- onValuesModified?.(false);
16054
+ onValuesModified?.(false, initialValues_0);
15299
16055
  },
15300
16056
  onValuesChangeDeferred: (values_0, controller) => {
15301
16057
  const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
@@ -15354,14 +16110,16 @@ function EntityForm({
15354
16110
  }, [snackbarController]);
15355
16111
  function clearDirtyCache() {
15356
16112
  if (status === "new" || status === "copy") {
16113
+ removeEntityFromMemoryCache(path + "#new");
15357
16114
  removeEntityFromCache(path + "#new");
15358
16115
  } else {
16116
+ removeEntityFromMemoryCache(path + "/" + entityId);
15359
16117
  removeEntityFromCache(path + "/" + entityId);
15360
16118
  }
15361
16119
  }
15362
16120
  const onSaveSuccess = (updatedEntity) => {
15363
16121
  clearDirtyCache();
15364
- onValuesModified?.(false);
16122
+ onValuesModified?.(false, updatedEntity.values);
15365
16123
  if (!autoSave) snackbarController.open({
15366
16124
  type: "success",
15367
16125
  message: `${collection.singularName ?? collection.name}: Saved correctly`
@@ -15521,7 +16279,7 @@ function EntityForm({
15521
16279
  }, [doOnIdUpdate]);
15522
16280
  useEffect(() => {
15523
16281
  if (!autoSave) {
15524
- onValuesModified?.(modified);
16282
+ onValuesModified?.(modified, formex.values);
15525
16283
  }
15526
16284
  }, [formex.dirty]);
15527
16285
  const modified = formex.dirty;
@@ -15618,7 +16376,10 @@ function EntityForm({
15618
16376
  values: baseInitialValues
15619
16377
  }), noValidate: true, className: cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className), children: [
15620
16378
  /* @__PURE__ */ jsx("div", { id: `form_${path}`, className: 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__ */ jsxs("div", { className: cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10"), children: [
15621
- formex.dirty ? /* @__PURE__ */ jsx(Tooltip, { title: "Local unsaved changes", className: "self-end sticky top-4 z-10", children: /* @__PURE__ */ jsx(Chip, { size: "small", colorScheme: "orangeDarker", children: /* @__PURE__ */ jsx(EditIcon, { size: "smallest" }) }) }) : /* @__PURE__ */ jsx(Tooltip, { title: "In sync with the database", className: "self-end sticky top-4 z-10", children: /* @__PURE__ */ jsx(Chip, { size: "small", children: /* @__PURE__ */ jsx(CheckIcon, { size: "smallest" }) }) }),
16379
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-4 self-end sticky top-4 z-10", children: [
16380
+ manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsx(LocalChangesMenu, { cacheKey: status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId, properties: resolvedCollection.properties, localChangesData, formex, onClearLocalChanges: () => setLocalChangesCleared(true) }),
16381
+ formex.dirty ? /* @__PURE__ */ jsx(Tooltip, { title: "There are local unsaved changes", children: /* @__PURE__ */ jsx(Chip, { size: "small", colorScheme: "orangeDarker", children: /* @__PURE__ */ jsx(EditIcon, { size: "smallest" }) }) }) : /* @__PURE__ */ jsx(Tooltip, { title: "The current form is in sync with the database", children: /* @__PURE__ */ jsx(Chip, { size: "small", children: /* @__PURE__ */ jsx(CheckIcon, { size: "smallest" }) }) })
16382
+ ] }),
15622
16383
  formView
15623
16384
  ] }) }),
15624
16385
  dialogActions
@@ -20428,7 +21189,7 @@ const EntityCollectionView = React__default.memo(function EntityCollectionView2(
20428
21189
  /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "So empty..." }),
20429
21190
  /* @__PURE__ */ jsxs(Button, { color: "primary", variant: "outlined", onClick: onNewClick, className: "mt-4", children: [
20430
21191
  /* @__PURE__ */ jsx(AddIcon, {}),
20431
- "Create your first entity"
21192
+ "Create your first entry"
20432
21193
  ] })
20433
21194
  ] }) : /* @__PURE__ */ jsx(Typography, { variant: "label", children: "No results with the applied filter/sort" }), hoverRow, inlineEditing: checkInlineEditing(), AdditionalHeaderWidget: buildAdditionalHeaderWidget, AddColumnComponent: addColumnComponentInternal, getIdColumnWidth, additionalIDHeaderWidget: /* @__PURE__ */ jsx(EntityIdHeaderWidget, { path: fullPath, fullIdPath: fullIdPath ?? fullPath, collection }), openEntityMode }, `collection_table_${fullPath}`),
20434
21195
  popupCell && /* @__PURE__ */ 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}`),
@@ -23087,6 +23848,7 @@ function EntityJsonPreview(t0) {
23087
23848
  function createFormexStub(values) {
23088
23849
  const errorMessage = "You are in a read-only context. You cannot modify the formex controller.";
23089
23850
  return {
23851
+ debugId: "",
23090
23852
  values,
23091
23853
  initialValues: values,
23092
23854
  touched: {},
@@ -23101,6 +23863,9 @@ function createFormexStub(values) {
23101
23863
  setValues: () => {
23102
23864
  throw new Error(errorMessage);
23103
23865
  },
23866
+ setTouched(touched) {
23867
+ throw new Error(errorMessage);
23868
+ },
23104
23869
  setFieldValue: () => {
23105
23870
  throw new Error(errorMessage);
23106
23871
  },
@@ -23160,8 +23925,7 @@ function EntityEditView({
23160
23925
  databaseId: props.databaseId,
23161
23926
  useCache: false
23162
23927
  });
23163
- const enableLocalChangesBackup = props.collection.enableLocalChangesBackup !== void 0 ? props.collection.enableLocalChangesBackup : true;
23164
- const initialDirtyValues = entityId ? getEntityFromCache(props.path + "/" + entityId, enableLocalChangesBackup) : getEntityFromCache(props.path + "#new", enableLocalChangesBackup);
23928
+ const initialDirtyValues = entityId ? getEntityFromMemoryCache(props.path + "/" + entityId) : getEntityFromMemoryCache(props.path + "#new");
23165
23929
  const authController = useAuthController();
23166
23930
  const initialStatus = props.copy ? "copy" : entityId ? "existing" : "new";
23167
23931
  const [status, setStatus] = useState(initialStatus);
@@ -23320,6 +24084,7 @@ function EntityEditViewInner({
23320
24084
  /* @__PURE__ */ jsx("div", { className: "h-16" })
23321
24085
  ] }) }) : null;
23322
24086
  const entityView = /* @__PURE__ */ jsx(EntityForm, { fullIdPath, collection, path, entityId: entityId ?? usedEntity?.id, onValuesModified, entity, initialDirtyValues, openEntityMode: layout, forceActionsAtTheBottom: actionsAtTheBottom, initialStatus: status, className: cls((!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : "", formProps?.className), EntityFormActionsComponent: EntityEditViewFormActions, disabled: !canEdit, ...formProps, onEntityChange: (entity_0) => {
24087
+ console.log("333 EntityEditView onEntityChange:", entity_0);
23323
24088
  setUsedEntity(entity_0);
23324
24089
  formProps?.onEntityChange?.(entity_0);
23325
24090
  }, onStatusChange: (status_0) => {
@@ -23342,7 +24107,12 @@ function EntityEditViewInner({
23342
24107
  const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
23343
24108
  let result = /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col h-full w-full bg-white dark:bg-surface-900", children: [
23344
24109
  shouldShowTopBar && /* @__PURE__ */ jsxs("div", { className: 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", defaultBorderMixin), children: [
23345
- barActions,
24110
+ barActions?.({
24111
+ path: fullIdPath ?? path,
24112
+ entityId,
24113
+ values: formContext?.values ?? usedEntity?.values ?? {},
24114
+ status
24115
+ }),
23346
24116
  /* @__PURE__ */ jsx("div", { className: "flex-grow" }),
23347
24117
  pluginActionsTop,
23348
24118
  globalLoading && /* @__PURE__ */ jsx("div", { className: "self-center", children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" }) }),
@@ -23445,9 +24215,14 @@ function EntitySidePanel(props) {
23445
24215
  if (!props || !collection) {
23446
24216
  return /* @__PURE__ */ jsx("div", { className: "w-full" });
23447
24217
  }
23448
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(EntityEditView, { ...props, fullIdPath, layout: "side_panel", collection, parentCollectionIds, onValuesModified, onSaved: onUpdate, barActions: /* @__PURE__ */ jsxs(Fragment, { children: [
24218
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(EntityEditView, { ...props, fullIdPath, layout: "side_panel", collection, parentCollectionIds, onValuesModified, onSaved: onUpdate, barActions: ({
24219
+ status,
24220
+ values
24221
+ }) => /* @__PURE__ */ jsxs(Fragment, { children: [
23449
24222
  /* @__PURE__ */ jsx(IconButton, { className: "self-center", onClick: onClose, children: /* @__PURE__ */ jsx(CloseIcon, { size: "small" }) }),
23450
24223
  allowFullScreen && /* @__PURE__ */ jsx(IconButton, { className: "self-center", onClick: () => {
24224
+ const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
24225
+ saveEntityToMemoryCache(key, values);
23451
24226
  if (entityId) navigate(location.pathname);
23452
24227
  else navigate(location.pathname + "#new");
23453
24228
  }, children: /* @__PURE__ */ jsx(OpenInFullIcon, { size: "small" }) })
@@ -26228,6 +27003,7 @@ export {
26228
27003
  getIdIcon,
26229
27004
  getLabelOrConfigFrom,
26230
27005
  getLastSegment,
27006
+ getLocalChangesBackup,
26231
27007
  getPropertiesWithPropertiesOrder,
26232
27008
  getPropertyInPath,
26233
27009
  getRandomId,
@@ -26242,6 +27018,7 @@ export {
26242
27018
  isEnumValueDisabled,
26243
27019
  isHidden,
26244
27020
  isObject,
27021
+ isPlainObject,
26245
27022
  isPropertyBuilder,
26246
27023
  isReadOnly,
26247
27024
  isReferenceProperty,