@firecms/core 3.0.0-canary.282 → 3.0.0-canary.284

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.umd.js CHANGED
@@ -5848,7 +5848,7 @@
5848
5848
  }
5849
5849
  let t3;
5850
5850
  if ($[11] !== t2) {
5851
- t3 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: t2 });
5851
+ t3 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex flex-col gap-2", children: t2 });
5852
5852
  $[11] = t2;
5853
5853
  $[12] = t3;
5854
5854
  } else {
@@ -6829,7 +6829,7 @@
6829
6829
  if (!user) {
6830
6830
  let t12;
6831
6831
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
6832
- t12 = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-text-secondary dark:text-text-secondary-dark", children: "Select a user" });
6832
+ t12 = /* @__PURE__ */ jsxRuntime.jsx(EmptyValue, {});
6833
6833
  $[0] = t12;
6834
6834
  } else {
6835
6835
  t12 = $[0];
@@ -6905,7 +6905,7 @@
6905
6905
  return t8;
6906
6906
  }
6907
6907
  function UserPreview(t0) {
6908
- const $ = reactCompilerRuntime.c(5);
6908
+ const $ = reactCompilerRuntime.c(8);
6909
6909
  const {
6910
6910
  value
6911
6911
  } = t0;
@@ -6913,25 +6913,46 @@
6913
6913
  getUser
6914
6914
  } = useInternalUserManagementController();
6915
6915
  if (!value) {
6916
- return null;
6916
+ let t12;
6917
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
6918
+ t12 = /* @__PURE__ */ jsxRuntime.jsx(EmptyValue, {});
6919
+ $[0] = t12;
6920
+ } else {
6921
+ t12 = $[0];
6922
+ }
6923
+ return t12;
6917
6924
  }
6918
6925
  let t1;
6919
- if ($[0] !== getUser || $[1] !== value) {
6926
+ if ($[1] !== getUser || $[2] !== value) {
6920
6927
  t1 = getUser(value);
6921
- $[0] = getUser;
6922
- $[1] = value;
6923
- $[2] = t1;
6928
+ $[1] = getUser;
6929
+ $[2] = value;
6930
+ $[3] = t1;
6924
6931
  } else {
6925
- t1 = $[2];
6932
+ t1 = $[3];
6926
6933
  }
6927
6934
  const user = t1;
6935
+ if (!user) {
6936
+ let t22;
6937
+ if ($[4] !== value) {
6938
+ t22 = /* @__PURE__ */ jsxRuntime.jsxs(ui.Typography, { variant: "caption", color: "secondary", children: [
6939
+ "User not found: ",
6940
+ value
6941
+ ] });
6942
+ $[4] = value;
6943
+ $[5] = t22;
6944
+ } else {
6945
+ t22 = $[5];
6946
+ }
6947
+ return t22;
6948
+ }
6928
6949
  let t2;
6929
- if ($[3] !== user) {
6950
+ if ($[6] !== user) {
6930
6951
  t2 = /* @__PURE__ */ jsxRuntime.jsx(UserDisplay, { user });
6931
- $[3] = user;
6932
- $[4] = t2;
6952
+ $[6] = user;
6953
+ $[7] = t2;
6933
6954
  } else {
6934
- t2 = $[4];
6955
+ t2 = $[7];
6935
6956
  }
6936
6957
  return t2;
6937
6958
  }
@@ -14966,61 +14987,60 @@
14966
14987
  ] });
14967
14988
  }
14968
14989
  const ErrorFocus = (t0) => {
14969
- const $ = reactCompilerRuntime.c(6);
14990
+ const $ = reactCompilerRuntime.c(10);
14970
14991
  const {
14971
14992
  containerRef
14972
14993
  } = t0;
14973
14994
  const {
14974
- isSubmitting,
14975
14995
  isValidating,
14976
- errors
14996
+ errors,
14997
+ version
14977
14998
  } = formex.useFormex();
14999
+ const prevVersion = React.useRef(version);
14978
15000
  let t1;
14979
- let t2;
14980
- if ($[0] !== containerRef || $[1] !== errors || $[2] !== isSubmitting || $[3] !== isValidating) {
15001
+ if ($[0] !== containerRef?.current || $[1] !== errors || $[2] !== isValidating || $[3] !== version) {
14981
15002
  t1 = () => {
15003
+ if (version === prevVersion.current) {
15004
+ return;
15005
+ }
14982
15006
  const keys = Object.keys(errors);
14983
- if (keys.length > 0 && isSubmitting && !isValidating) {
15007
+ if (!isValidating && keys.length > 0) {
14984
15008
  const errorElement = containerRef?.current?.querySelector(`#form_field_${keys[0]}`);
14985
- if (errorElement && containerRef?.current) {
14986
- const scrollableParent = getScrollableParent(containerRef.current);
14987
- if (scrollableParent) {
14988
- const top = errorElement.getBoundingClientRect().top;
14989
- scrollableParent.scrollTo({
14990
- top: scrollableParent.scrollTop + top - 196,
14991
- behavior: "smooth"
14992
- });
14993
- }
15009
+ if (errorElement) {
15010
+ errorElement.scrollIntoView({
15011
+ behavior: "smooth",
15012
+ block: "center"
15013
+ });
14994
15014
  const input = errorElement.querySelector("input");
14995
15015
  if (input) {
14996
15016
  input.focus();
14997
15017
  }
14998
15018
  }
15019
+ prevVersion.current = version;
14999
15020
  }
15000
15021
  };
15001
- t2 = [isSubmitting, isValidating, errors, containerRef];
15002
- $[0] = containerRef;
15022
+ $[0] = containerRef?.current;
15003
15023
  $[1] = errors;
15004
- $[2] = isSubmitting;
15005
- $[3] = isValidating;
15024
+ $[2] = isValidating;
15025
+ $[3] = version;
15006
15026
  $[4] = t1;
15007
- $[5] = t2;
15008
15027
  } else {
15009
15028
  t1 = $[4];
15010
- t2 = $[5];
15029
+ }
15030
+ let t2;
15031
+ if ($[5] !== containerRef || $[6] !== errors || $[7] !== isValidating || $[8] !== version) {
15032
+ t2 = [isValidating, errors, containerRef, version];
15033
+ $[5] = containerRef;
15034
+ $[6] = errors;
15035
+ $[7] = isValidating;
15036
+ $[8] = version;
15037
+ $[9] = t2;
15038
+ } else {
15039
+ t2 = $[9];
15011
15040
  }
15012
15041
  React.useEffect(t1, t2);
15013
15042
  return null;
15014
15043
  };
15015
- const isScrollable = (ele) => {
15016
- const hasScrollableContent = ele && ele.scrollHeight > ele.clientHeight;
15017
- const overflowYStyle = ele ? window.getComputedStyle(ele).overflowY : null;
15018
- const isOverflowHidden = overflowYStyle && overflowYStyle.indexOf("hidden") !== -1;
15019
- return hasScrollableContent && !isOverflowHidden;
15020
- };
15021
- const getScrollableParent = (ele) => {
15022
- return !ele || ele === document.body ? document.body : isScrollable(ele) ? ele : getScrollableParent(ele.parentNode);
15023
- };
15024
15044
  function EntityFormActions(t0) {
15025
15045
  const $ = reactCompilerRuntime.c(16);
15026
15046
  const {
@@ -15041,7 +15061,7 @@
15041
15061
  const context = useFireCMSContext();
15042
15062
  const sideEntityController = useSideEntityController();
15043
15063
  let t1;
15044
- 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) {
15064
+ 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) {
15045
15065
  t1 = layout === "bottom" ? buildBottomActions$1({
15046
15066
  fullPath,
15047
15067
  fullIdPath,
@@ -15050,13 +15070,13 @@
15050
15070
  collection,
15051
15071
  context,
15052
15072
  sideEntityController,
15053
- isSubmitting: formex2.isSubmitting,
15054
15073
  disabled,
15055
15074
  status,
15056
15075
  pluginActions,
15057
15076
  openEntityMode,
15058
15077
  navigateBack,
15059
- formContext
15078
+ formContext,
15079
+ formex: formex2
15060
15080
  }) : buildSideActions$1({
15061
15081
  fullPath,
15062
15082
  fullIdPath,
@@ -15065,18 +15085,18 @@
15065
15085
  collection,
15066
15086
  context,
15067
15087
  sideEntityController,
15068
- isSubmitting: formex2.isSubmitting,
15069
15088
  disabled,
15070
15089
  status,
15071
15090
  pluginActions,
15072
- openEntityMode
15091
+ openEntityMode,
15092
+ formex: formex2
15073
15093
  });
15074
15094
  $[0] = collection;
15075
15095
  $[1] = context;
15076
15096
  $[2] = disabled;
15077
15097
  $[3] = entity;
15078
15098
  $[4] = formContext;
15079
- $[5] = formex2.isSubmitting;
15099
+ $[5] = formex2;
15080
15100
  $[6] = fullIdPath;
15081
15101
  $[7] = fullPath;
15082
15102
  $[8] = layout;
@@ -15101,14 +15121,15 @@
15101
15121
  collection,
15102
15122
  context,
15103
15123
  sideEntityController,
15104
- isSubmitting,
15105
15124
  disabled,
15106
15125
  status,
15107
15126
  pluginActions,
15108
15127
  openEntityMode,
15109
15128
  navigateBack,
15110
- formContext
15129
+ formContext,
15130
+ formex: formex2
15111
15131
  }) {
15132
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
15112
15133
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { position: "absolute", children: [
15113
15134
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) }),
15114
15135
  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) => {
@@ -15127,8 +15148,8 @@
15127
15148
  });
15128
15149
  }, children: action.icon }, action.name)) }),
15129
15150
  pluginActions,
15130
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || isSubmitting, color: "primary", type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15131
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "filled", color: "primary", type: "submit", disabled: disabled || isSubmitting, children: [
15151
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || formex2.isSubmitting, color: "primary", type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15152
+ /* @__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: [
15132
15153
  status === "existing" && "Save",
15133
15154
  status === "copy" && "Create copy",
15134
15155
  status === "new" && "Create"
@@ -15145,22 +15166,35 @@
15145
15166
  collection,
15146
15167
  context,
15147
15168
  sideEntityController,
15148
- isSubmitting,
15149
15169
  disabled,
15150
15170
  status,
15151
- pluginActions
15171
+ pluginActions,
15172
+ formex: formex2
15152
15173
  }) {
15174
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
15153
15175
  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: [
15154
- /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", disabled: disabled || isSubmitting, children: [
15176
+ /* @__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: [
15155
15177
  status === "existing" && "Save",
15156
15178
  status === "copy" && "Create copy",
15157
15179
  status === "new" && "Create"
15158
15180
  ] }),
15159
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15181
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || formex2.isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
15160
15182
  pluginActions,
15161
15183
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) })
15162
15184
  ] });
15163
15185
  }
15186
+ function extractTouchedValues(values, touched) {
15187
+ let acc = {};
15188
+ if (!touched || typeof touched !== "object") {
15189
+ return acc;
15190
+ }
15191
+ Object.entries(touched).forEach(([key, value]) => {
15192
+ if (value) {
15193
+ acc = formex.setIn(acc, key, formex.getIn(values, key));
15194
+ }
15195
+ });
15196
+ return acc;
15197
+ }
15164
15198
  function EntityForm({
15165
15199
  path,
15166
15200
  fullIdPath,
@@ -15218,7 +15252,7 @@
15218
15252
  const customizationController = useCustomizationController();
15219
15253
  const context = useFireCMSContext();
15220
15254
  const analyticsController = useAnalyticsController();
15221
- const [underlyingChanges, setUnderlyingChanges] = React.useState({});
15255
+ const [underlyingChanges] = React.useState({});
15222
15256
  const [customIdLoading, setCustomIdLoading] = React.useState(false);
15223
15257
  const mustSetCustomId = (status === "new" || status === "copy") && Boolean(collection.customId) && collection.customId !== "optional";
15224
15258
  const initialEntityId = React.useMemo(() => {
@@ -15266,16 +15300,30 @@
15266
15300
  formexController.setSubmitting(false);
15267
15301
  });
15268
15302
  };
15303
+ const baseInitialValues = getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs);
15304
+ const initialValues = initialDirtyValues ? mergeDeep(baseInitialValues, initialDirtyValues) : baseInitialValues;
15305
+ const initialDirty = Boolean(initialDirtyValues) && initialDirtyValues && Object.keys(initialDirtyValues).length > 0;
15269
15306
  const formex$1 = formexProp ?? formex.useCreateFormex({
15270
- initialValues: initialDirtyValues ?? getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs),
15271
- initialDirty: Boolean(initialDirtyValues),
15307
+ initialValues,
15308
+ initialDirty,
15309
+ initialTouched: initialDirtyValues ? formex.flattenKeys(initialDirtyValues).reduce((previousValue, currentValue) => ({
15310
+ ...previousValue,
15311
+ [currentValue]: true
15312
+ }), {}) : {},
15272
15313
  onSubmit,
15273
15314
  onReset: () => {
15274
15315
  clearDirtyCache();
15275
15316
  onValuesModified?.(false);
15276
15317
  },
15277
- validation: (values_0) => {
15278
- return validationSchema?.validate(values_0, {
15318
+ onValuesChangeDeferred: (values_0, controller) => {
15319
+ const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
15320
+ if (controller.dirty) {
15321
+ const touchedValues = extractTouchedValues(values_0, controller.touched);
15322
+ saveEntityToCache(key, touchedValues);
15323
+ }
15324
+ },
15325
+ validation: (values_1) => {
15326
+ return validationSchema?.validate(values_1, {
15279
15327
  abortEarly: false
15280
15328
  }).then(() => {
15281
15329
  return {};
@@ -15358,7 +15406,7 @@
15358
15406
  console.error(e_3);
15359
15407
  }, [entityId, path, snackbarController]);
15360
15408
  const saveEntity = ({
15361
- values: values_1,
15409
+ values: values_2,
15362
15410
  previousValues,
15363
15411
  entityId: entityId_0,
15364
15412
  collection: collection_0,
@@ -15367,7 +15415,7 @@
15367
15415
  return saveEntityWithCallbacks({
15368
15416
  path: path_0,
15369
15417
  entityId: entityId_0,
15370
- values: values_1,
15418
+ values: values_2,
15371
15419
  previousValues,
15372
15420
  collection: collection_0,
15373
15421
  status,
@@ -15383,34 +15431,34 @@
15383
15431
  collection: collection_1,
15384
15432
  path: path_1,
15385
15433
  entityId: entityId_1,
15386
- values: values_2,
15434
+ values: values_3,
15387
15435
  previousValues: previousValues_0,
15388
15436
  autoSave: autoSave_0
15389
15437
  }) => {
15390
15438
  if (!status) return;
15391
15439
  if (autoSave_0) {
15392
- setValuesToBeSaved(values_2);
15440
+ setValuesToBeSaved(values_3);
15393
15441
  } else {
15394
15442
  return saveEntity({
15395
15443
  collection: collection_1,
15396
15444
  path: path_1,
15397
15445
  entityId: entityId_1,
15398
- values: values_2,
15446
+ values: values_3,
15399
15447
  previousValues: previousValues_0
15400
15448
  });
15401
15449
  }
15402
15450
  };
15403
15451
  const lastSavedValues = React.useRef(entity?.values);
15404
- const save = (values_3) => {
15405
- lastSavedValues.current = values_3;
15452
+ const save = (values_4) => {
15453
+ lastSavedValues.current = values_4;
15406
15454
  return onSaveEntityRequest({
15407
15455
  collection: resolvedCollection,
15408
15456
  path,
15409
15457
  entityId,
15410
- values: values_3,
15458
+ values: values_4,
15411
15459
  previousValues: entity?.values,
15412
15460
  autoSave: autoSave ?? false
15413
- }).then((res) => {
15461
+ }).then(() => {
15414
15462
  const eventName = status === "new" ? "new_entity_saved" : status === "copy" ? "entity_copied" : status === "existing" ? "entity_edited" : "unmapped_event";
15415
15463
  analyticsController.onAnalyticsEvent?.(eventName, {
15416
15464
  path
@@ -15444,7 +15492,8 @@
15444
15492
  type: "error",
15445
15493
  message: "Error updating id, check the console"
15446
15494
  });
15447
- }, []);
15495
+ console.error(error);
15496
+ }, [snackbarController]);
15448
15497
  const pluginActions = [];
15449
15498
  const plugins = customizationController.plugins;
15450
15499
  const actionsDisabled = disabled || formex$1.isSubmitting || status === "existing" && !formex$1.dirty || Boolean(disabledProp);
@@ -15493,20 +15542,12 @@
15493
15542
  onValuesModified?.(modified);
15494
15543
  }
15495
15544
  }, [formex$1.dirty]);
15496
- const deferredValues = React.useDeferredValue(formex$1.values);
15497
15545
  const modified = formex$1.dirty;
15498
15546
  const uniqueFieldValidator = React.useCallback(({
15499
15547
  name,
15500
- value,
15501
- property
15548
+ value
15502
15549
  }) => dataSource.checkUniqueField(path, name, value, entityId, collection), [dataSource, path, entityId]);
15503
15550
  const validationSchema = React.useMemo(() => entityId ? getYupEntitySchema(entityId, resolvedCollection.properties, uniqueFieldValidator) : void 0, [entityId, resolvedCollection.properties, uniqueFieldValidator]);
15504
- React.useEffect(() => {
15505
- const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
15506
- if (modified) {
15507
- saveEntityToCache(key, deferredValues);
15508
- }
15509
- }, [deferredValues, modified, path, entityId, status]);
15510
15551
  useOnAutoSave(autoSave, formex$1, lastSavedValues, save);
15511
15552
  React.useEffect(() => {
15512
15553
  if (!autoSave && !formex$1.isSubmitting && underlyingChanges && entity) {
@@ -15525,18 +15566,18 @@
15525
15566
  return /* @__PURE__ */ jsxRuntime.jsx(Builder, { collection, entity, modifiedValues: formex$1.values, formContext });
15526
15567
  }
15527
15568
  return /* @__PURE__ */ jsxRuntime.jsx(FormLayout, { children: formFieldKeys.map((key_1) => {
15528
- const property_0 = resolvedCollection.properties[key_1];
15529
- if (property_0) {
15569
+ const property = resolvedCollection.properties[key_1];
15570
+ if (property) {
15530
15571
  const underlyingValueHasChanged = !!underlyingChanges && Object.keys(underlyingChanges).includes(key_1) && formex$1.touched[key_1];
15531
- const disabled_0 = disabledProp || !autoSave && formex$1.isSubmitting || isReadOnly(property_0) || Boolean(property_0.disabled);
15532
- const hidden = isHidden(property_0);
15572
+ const disabled_0 = disabledProp || !autoSave && formex$1.isSubmitting || isReadOnly(property) || Boolean(property.disabled);
15573
+ const hidden = isHidden(property);
15533
15574
  if (hidden) return null;
15534
- const widthPercentage = property_0.widthPercentage ?? 100;
15575
+ const widthPercentage = property.widthPercentage ?? 100;
15535
15576
  const cmsFormFieldProps = {
15536
15577
  propertyKey: key_1,
15537
15578
  disabled: disabled_0,
15538
- property: property_0,
15539
- includeDescription: property_0.description || property_0.longDescription,
15579
+ property,
15580
+ includeDescription: property.description || property.longDescription,
15540
15581
  underlyingValueHasChanged: underlyingValueHasChanged && !autoSave,
15541
15582
  context: formContext,
15542
15583
  partOfArray: false,
@@ -15592,7 +15633,7 @@
15592
15633
  }
15593
15634
  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 });
15594
15635
  return /* @__PURE__ */ jsxRuntime.jsx(formex.Formex, { value: formex$1, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: formex$1.handleSubmit, onReset: () => formex$1.resetForm({
15595
- values: getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs)
15636
+ values: baseInitialValues
15596
15637
  }), noValidate: true, className: ui.cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className), children: [
15597
15638
  /* @__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: [
15598
15639
  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" }) }) }),
@@ -17776,6 +17817,13 @@
17776
17817
  } = t0;
17777
17818
  const authController = useAuthController();
17778
17819
  const customizationController = useCustomizationController();
17820
+ if (propertyKey === "created_by") {
17821
+ console.log("Rendering field for created_by", {
17822
+ propertyKey,
17823
+ property,
17824
+ context
17825
+ });
17826
+ }
17779
17827
  let t1;
17780
17828
  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) {
17781
17829
  t1 = (fieldProps) => {
@@ -20640,7 +20688,7 @@
20640
20688
  propertyConfig,
20641
20689
  disabled
20642
20690
  } = t0;
20643
- const classes = "h-8 w-8 p-1 rounded-full shadow text-white " + (disabled ? "bg-surface-400 dark:bg-surface-600" : "");
20691
+ const classes = "h-8 w-8 flex items-center justify-center rounded-full shadow text-white " + (disabled ? "bg-surface-400 dark:bg-surface-600" : "");
20644
20692
  let t1;
20645
20693
  if ($[0] !== classes || $[1] !== disabled || $[2] !== propertyConfig) {
20646
20694
  const defaultPropertyConfig = typeof propertyConfig?.property === "object" ? getDefaultFieldConfig(propertyConfig.property) : void 0;
@@ -20655,7 +20703,7 @@
20655
20703
  } else {
20656
20704
  t3 = $[5];
20657
20705
  }
20658
- t1 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes, style: t3, children: propertyConfig?.Icon ? getIconForWidget(propertyConfig, "medium") : getIconForWidget(defaultPropertyConfig, "medium") });
20706
+ t1 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes, style: t3, children: propertyConfig?.Icon ? getIconForWidget(propertyConfig, "small") : getIconForWidget(defaultPropertyConfig, "small") });
20659
20707
  $[0] = classes;
20660
20708
  $[1] = disabled;
20661
20709
  $[2] = propertyConfig;
@@ -21991,7 +22039,8 @@
21991
22039
  }, []);
21992
22040
  const isUrlCollectionPath = React.useCallback((path_1) => removeInitialAndTrailingSlashes(path_1 + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"), [fullCollectionPath]);
21993
22041
  const urlPathToDataPath = React.useCallback((path_2) => {
21994
- if (path_2.startsWith(fullCollectionPath)) return path_2.replace(fullCollectionPath, "");
22042
+ const decodedPath = decodeURIComponent(path_2);
22043
+ if (decodedPath.startsWith(fullCollectionPath)) return decodedPath.replace(fullCollectionPath, "");
21995
22044
  throw Error("Expected path starting with " + fullCollectionPath);
21996
22045
  }, [fullCollectionPath]);
21997
22046
  const resolveIdsFrom = React.useCallback((path_3) => {
@@ -22797,14 +22846,14 @@
22797
22846
  collection,
22798
22847
  context,
22799
22848
  sideEntityController,
22800
- isSubmitting: formex2.isSubmitting,
22801
22849
  disabled,
22802
22850
  status,
22803
22851
  sideDialogContext,
22804
22852
  pluginActions,
22805
22853
  openEntityMode,
22806
22854
  navigateBack,
22807
- formContext
22855
+ formContext,
22856
+ formex: formex2
22808
22857
  }) : buildSideActions({
22809
22858
  savingError,
22810
22859
  entity,
@@ -22812,14 +22861,14 @@
22812
22861
  collection,
22813
22862
  context,
22814
22863
  sideEntityController,
22815
- isSubmitting: formex2.isSubmitting,
22816
22864
  sideDialogContext,
22817
22865
  disabled,
22818
22866
  status,
22819
22867
  pluginActions,
22820
22868
  openEntityMode,
22821
22869
  navigateBack,
22822
- formContext
22870
+ formContext,
22871
+ formex: formex2
22823
22872
  });
22824
22873
  }
22825
22874
  function buildBottomActions({
@@ -22829,15 +22878,16 @@
22829
22878
  collection,
22830
22879
  context,
22831
22880
  sideEntityController,
22832
- isSubmitting,
22833
22881
  disabled,
22834
22882
  status,
22835
22883
  sideDialogContext,
22836
22884
  pluginActions,
22837
22885
  openEntityMode,
22838
22886
  navigateBack,
22839
- formContext
22887
+ formContext,
22888
+ formex: formex2
22840
22889
  }) {
22890
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
22841
22891
  const canClose = openEntityMode === "side_panel";
22842
22892
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { position: "absolute", children: [
22843
22893
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) }),
@@ -22857,15 +22907,16 @@
22857
22907
  return /* @__PURE__ */ jsxRuntime.jsx(EntityActionButton, { action, enabled: isEnabled, props }, action.key);
22858
22908
  }) }),
22859
22909
  pluginActions,
22860
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", color: "primary", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22861
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: canClose ? "text" : "filled", color: "primary", type: "submit", disabled: disabled || isSubmitting, onClick: () => {
22910
+ hasErrors ? /* @__PURE__ */ jsxRuntime.jsx(ErrorTooltip, { title: "This form has errors", children: /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, { className: "ml-4", color: "error", size: "smallest" }) }) : null,
22911
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", color: "primary", disabled: disabled || formex2.isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22912
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: canClose ? "text" : "filled", color: "primary", type: "submit", disabled: disabled || formex2.isSubmitting, onClick: () => {
22862
22913
  sideDialogContext.setPendingClose(false);
22863
22914
  }, children: [
22864
22915
  status === "existing" && "Save",
22865
22916
  status === "copy" && "Create copy",
22866
22917
  status === "new" && "Create"
22867
22918
  ] }),
22868
- canClose && /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { variant: "filled", color: "primary", type: "submit", loading: isSubmitting, disabled, onClick: () => {
22919
+ canClose && /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { variant: "filled", color: "primary", type: "submit", loading: formex2.isSubmitting, disabled, onClick: () => {
22869
22920
  sideDialogContext.setPendingClose?.(true);
22870
22921
  }, children: [
22871
22922
  status === "existing" && "Save and close",
@@ -22881,24 +22932,25 @@
22881
22932
  collection,
22882
22933
  context,
22883
22934
  sideEntityController,
22884
- isSubmitting,
22885
22935
  disabled,
22886
22936
  status,
22887
22937
  sideDialogContext,
22888
22938
  pluginActions,
22889
22939
  openEntityMode,
22890
22940
  navigateBack,
22891
- formContext
22941
+ formContext,
22942
+ formex: formex2
22892
22943
  }) {
22944
+ const hasErrors = Object.keys(formex2.errors).length > 0 && formex2.submitCount > 0;
22893
22945
  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: [
22894
- /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", disabled: disabled || isSubmitting, onClick: () => {
22946
+ /* @__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: () => {
22895
22947
  sideDialogContext.setPendingClose?.(false);
22896
22948
  }, children: [
22897
22949
  status === "existing" && "Save",
22898
22950
  status === "copy" && "Create copy",
22899
22951
  status === "new" && "Create"
22900
22952
  ] }),
22901
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22953
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || formex2.isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22902
22954
  pluginActions,
22903
22955
  formActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row flex-wrap mt-2", children: formActions.map((action) => {
22904
22956
  const props = {
@@ -23126,7 +23178,8 @@
23126
23178
  databaseId: props.databaseId,
23127
23179
  useCache: false
23128
23180
  });
23129
- const cachedValues = entityId ? getEntityFromCache(props.path + "/" + entityId) : getEntityFromCache(props.path + "#new");
23181
+ const enableLocalChangesBackup = props.collection.enableLocalChangesBackup !== void 0 ? props.collection.enableLocalChangesBackup : true;
23182
+ const initialDirtyValues = enableLocalChangesBackup ? entityId ? getEntityFromCache(props.path + "/" + entityId) : getEntityFromCache(props.path + "#new") : void 0;
23130
23183
  const authController = useAuthController();
23131
23184
  const initialStatus = props.copy ? "copy" : entityId ? "existing" : "new";
23132
23185
  const [status, setStatus] = React.useState(initialStatus);
@@ -23137,13 +23190,13 @@
23137
23190
  return entity ? canEditEntity(props.collection, authController, props.path, entity ?? null) : void 0;
23138
23191
  }
23139
23192
  }, [authController, entity, status]);
23140
- if (dataLoading && !cachedValues || (!entity || canEdit === void 0) && (status === "existing" || status === "copy")) {
23193
+ if (dataLoading && !initialDirtyValues || (!entity || canEdit === void 0) && (status === "existing" || status === "copy")) {
23141
23194
  return /* @__PURE__ */ jsxRuntime.jsx(CircularProgressCenter, {});
23142
23195
  }
23143
- if (entityId && !entity && !cachedValues) {
23196
+ if (entityId && !entity && !initialDirtyValues) {
23144
23197
  console.error(`Entity with id ${entityId} not found in collection ${props.path}`);
23145
23198
  }
23146
- return /* @__PURE__ */ jsxRuntime.jsx(EntityEditViewInner, { ...props, entityId, entity, cachedDirtyValues: cachedValues, dataLoading, status, setStatus, canEdit });
23199
+ return /* @__PURE__ */ jsxRuntime.jsx(EntityEditViewInner, { ...props, entityId, entity, initialDirtyValues, dataLoading, status, setStatus, canEdit });
23147
23200
  }
23148
23201
  function EntityEditViewInner({
23149
23202
  path,
@@ -23156,7 +23209,7 @@
23156
23209
  onSaved,
23157
23210
  onTabChange,
23158
23211
  entity,
23159
- cachedDirtyValues,
23212
+ initialDirtyValues,
23160
23213
  dataLoading,
23161
23214
  layout = "side_panel",
23162
23215
  barActions,
@@ -23284,7 +23337,7 @@
23284
23337
  /* @__PURE__ */ jsxRuntime.jsx(EntityView, { className: "px-8 h-full overflow-auto", entity, path, collection }),
23285
23338
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-16" })
23286
23339
  ] }) }) : null;
23287
- 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) => {
23340
+ 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) => {
23288
23341
  setUsedEntity(entity_0);
23289
23342
  formProps?.onEntityChange?.(entity_0);
23290
23343
  }, onStatusChange: (status_0) => {