@firecms/core 3.0.0-canary.257 → 3.0.0-canary.259

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 (39) hide show
  1. package/dist/core/EntityEditViewFormActions.d.ts +1 -1
  2. package/dist/form/EntityFormActions.d.ts +4 -2
  3. package/dist/index.es.js +340 -156
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +340 -156
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/types/collections.d.ts +4 -1
  8. package/dist/types/customization_controller.d.ts +8 -0
  9. package/dist/types/entity_actions.d.ts +45 -6
  10. package/dist/types/firecms.d.ts +8 -0
  11. package/dist/types/plugins.d.ts +8 -1
  12. package/dist/util/resolutions.d.ts +2 -1
  13. package/package.json +5 -5
  14. package/src/components/ConfirmationDialog.tsx +1 -0
  15. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -0
  16. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +2 -2
  17. package/src/components/EntityCollectionView/EntityCollectionView.tsx +5 -2
  18. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
  19. package/src/components/HomePage/DefaultHomePage.tsx +48 -33
  20. package/src/components/HomePage/HomePageDnD.tsx +22 -36
  21. package/src/components/HomePage/RenameGroupDialog.tsx +6 -2
  22. package/src/components/UnsavedChangesDialog.tsx +6 -2
  23. package/src/components/common/default_entity_actions.tsx +18 -5
  24. package/src/components/common/useDataSourceTableController.tsx +1 -1
  25. package/src/core/EntityEditView.tsx +35 -12
  26. package/src/core/EntityEditViewFormActions.tsx +154 -29
  27. package/src/core/EntitySidePanel.tsx +1 -1
  28. package/src/core/FireCMS.tsx +2 -0
  29. package/src/form/EntityForm.tsx +32 -6
  30. package/src/form/EntityFormActions.tsx +37 -8
  31. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
  32. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +1 -1
  33. package/src/types/collections.ts +4 -1
  34. package/src/types/customization_controller.tsx +9 -0
  35. package/src/types/entity_actions.tsx +56 -6
  36. package/src/types/firecms.tsx +9 -0
  37. package/src/types/plugins.tsx +9 -1
  38. package/src/util/join_collections.ts +3 -1
  39. package/src/util/resolutions.ts +13 -1
package/dist/index.umd.js CHANGED
@@ -906,6 +906,13 @@
906
906
  return entityView;
907
907
  }
908
908
  }
909
+ function resolveEntityAction(entityAction, contextEntityActions) {
910
+ if (typeof entityAction === "string") {
911
+ return contextEntityActions?.find((entry) => entry.key === entityAction);
912
+ } else {
913
+ return entityAction;
914
+ }
915
+ }
909
916
  function resolvedSelectedEntityView(customViews, customizationController, selectedTab, canEdit) {
910
917
  const resolvedEntityViews = customViews ? customViews.map((e) => resolveEntityView(e, customizationController.entityViews)).filter((e) => Boolean(e)) : [];
911
918
  const selectedEntityView = resolvedEntityViews.find((e) => e.key === selectedTab);
@@ -3571,12 +3578,14 @@
3571
3578
  const sourcePropertiesOrder = getCollectionKeys(source);
3572
3579
  const mergedPropertiesOrder = [.../* @__PURE__ */ new Set([...sourcePropertiesOrder, ...targetPropertiesOrder])];
3573
3580
  const mergedEntityViews = [.../* @__PURE__ */ new Set([...target.entityViews ?? [], ...source.entityViews ?? []])];
3581
+ const mergedEntityActions = [.../* @__PURE__ */ new Set([...target.entityActions ?? [], ...source.entityActions ?? []])];
3574
3582
  let resultCollection = {
3575
3583
  ...mergedCollection,
3576
3584
  subcollections: subcollectionsMerged,
3577
3585
  properties: sortProperties(propertiesMerged, mergedPropertiesOrder),
3578
3586
  propertiesOrder: mergedPropertiesOrder,
3579
- entityViews: mergedEntityViews
3587
+ entityViews: mergedEntityViews,
3588
+ entityActions: mergedEntityActions
3580
3589
  };
3581
3590
  if (modifyCollection) {
3582
3591
  const modifiedCollection = modifyCollection({
@@ -9570,6 +9579,7 @@
9570
9579
  uncollapsedActions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: action.name, asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { onClick: (event_0) => {
9571
9580
  event_0.stopPropagation();
9572
9581
  action.onClick({
9582
+ view: "collection",
9573
9583
  entity,
9574
9584
  fullPath,
9575
9585
  fullIdPath,
@@ -9585,6 +9595,7 @@
9585
9595
  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) => {
9586
9596
  e.stopPropagation();
9587
9597
  action_0.onClick({
9598
+ view: "collection",
9588
9599
  entity,
9589
9600
  fullPath,
9590
9601
  fullIdPath,
@@ -9687,7 +9698,7 @@
9687
9698
  }
9688
9699
  let t9;
9689
9700
  if ($[12] !== actionsStart || $[13] !== sizeSelect || $[14] !== t8) {
9690
- t9 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 md:mr-4 mr-2", children: [
9701
+ t9 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 md:mr-4 mr-2", children: [
9691
9702
  t8,
9692
9703
  sizeSelect,
9693
9704
  actionsStart
@@ -9720,7 +9731,7 @@
9720
9731
  }
9721
9732
  let t12;
9722
9733
  if ($[23] !== actions || $[24] !== t10 || $[25] !== t11) {
9723
- t12 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
9734
+ t12 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
9724
9735
  t10,
9725
9736
  t11,
9726
9737
  actions
@@ -12009,7 +12020,7 @@
12009
12020
  }
12010
12021
  if (encodedValue !== void 0) {
12011
12022
  entries[encodeURIComponent(`${key}_op`)] = encodeURIComponent(op);
12012
- entries[encodeURIComponent(`${key}_value`)] = encodeURIComponent(encodedValue.toString());
12023
+ entries[encodeURIComponent(`${key}_value`)] = encodedValue ? encodeURIComponent(encodedValue.toString()) : "null";
12013
12024
  }
12014
12025
  }
12015
12026
  });
@@ -12389,10 +12400,13 @@
12389
12400
  return JSON.parse(stored);
12390
12401
  }
12391
12402
  const editEntityAction = {
12392
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.EditIcon, {}),
12403
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.EditIcon, { size: "small" }),
12393
12404
  key: "edit",
12394
12405
  name: "Edit",
12395
12406
  collapsed: false,
12407
+ isEnabled: ({
12408
+ entity
12409
+ }) => Boolean(entity),
12396
12410
  onClick({
12397
12411
  entity,
12398
12412
  collection,
@@ -12403,6 +12417,9 @@
12403
12417
  unhighlightEntity,
12404
12418
  openEntityMode
12405
12419
  }) {
12420
+ if (!entity) {
12421
+ throw new Error("INTERNAL: editEntityAction: Entity is undefined");
12422
+ }
12406
12423
  highlightEntity?.(entity);
12407
12424
  context.analyticsController?.onAnalyticsEvent?.("entity_click", {
12408
12425
  path: entity.path,
@@ -12432,9 +12449,12 @@
12432
12449
  }
12433
12450
  };
12434
12451
  const copyEntityAction = {
12435
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FileCopyIcon, {}),
12452
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.FileCopyIcon, { size: "small" }),
12436
12453
  name: "Copy",
12437
12454
  key: "copy",
12455
+ isEnabled: ({
12456
+ entity
12457
+ }) => Boolean(entity),
12438
12458
  onClick({
12439
12459
  entity,
12440
12460
  collection,
@@ -12444,6 +12464,9 @@
12444
12464
  unhighlightEntity,
12445
12465
  openEntityMode
12446
12466
  }) {
12467
+ if (!entity) {
12468
+ throw new Error("INTERNAL: copyEntityAction: Entity is undefined");
12469
+ }
12447
12470
  highlightEntity?.(entity);
12448
12471
  context.analyticsController?.onAnalyticsEvent?.("copy_entity_click", {
12449
12472
  path: entity.path,
@@ -12466,9 +12489,12 @@
12466
12489
  }
12467
12490
  };
12468
12491
  const deleteEntityAction = {
12469
- icon: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, {}),
12492
+ icon: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, { size: "small" }),
12470
12493
  name: "Delete",
12471
12494
  key: "delete",
12495
+ isEnabled: ({
12496
+ entity
12497
+ }) => Boolean(entity),
12472
12498
  onClick({
12473
12499
  entity,
12474
12500
  fullPath,
@@ -12476,8 +12502,11 @@
12476
12502
  context,
12477
12503
  selectionController,
12478
12504
  onCollectionChange,
12479
- sideEntityController
12505
+ navigateBack
12480
12506
  }) {
12507
+ if (!entity) {
12508
+ throw new Error("INTERNAL: deleteEntityAction: Entity is undefined");
12509
+ }
12481
12510
  const {
12482
12511
  closeDialog
12483
12512
  } = context.dialogsController.open({
@@ -12492,7 +12521,7 @@
12492
12521
  });
12493
12522
  selectionController?.setSelectedEntities(selectionController.selectedEntities.filter((e) => e.id !== entity.id));
12494
12523
  onCollectionChange?.();
12495
- sideEntityController?.close();
12524
+ navigateBack?.();
12496
12525
  }, onClose: closeDialog });
12497
12526
  }
12498
12527
  });
@@ -13359,53 +13388,51 @@
13359
13388
  isDragging
13360
13389
  } = sortable.useSortable(t1);
13361
13390
  let t2;
13362
- let t3;
13363
13391
  if ($[2] !== transform) {
13364
- t3 = transform ? utilities.CSS.Transform.toString(transform) : void 0;
13392
+ t2 = transform ? utilities.CSS.Transform.toString(transform) : void 0;
13365
13393
  $[2] = transform;
13366
- $[3] = t3;
13394
+ $[3] = t2;
13367
13395
  } else {
13368
- t3 = $[3];
13396
+ t2 = $[3];
13369
13397
  }
13370
- const t4 = isDragging ? 0 : 1;
13371
- let t5;
13372
- if ($[4] !== t3 || $[5] !== t4 || $[6] !== transition) {
13373
- t5 = {
13374
- transform: t3,
13398
+ const t3 = isDragging ? 0 : 1;
13399
+ let t4;
13400
+ if ($[4] !== t2 || $[5] !== t3 || $[6] !== transition) {
13401
+ t4 = {
13402
+ transform: t2,
13375
13403
  transition,
13376
- opacity: t4
13404
+ opacity: t3
13377
13405
  };
13378
- $[4] = t3;
13379
- $[5] = t4;
13406
+ $[4] = t2;
13407
+ $[5] = t3;
13380
13408
  $[6] = transition;
13381
- $[7] = t5;
13409
+ $[7] = t4;
13382
13410
  } else {
13383
- t5 = $[7];
13411
+ t4 = $[7];
13384
13412
  }
13385
- t2 = t5;
13386
- const style = t2;
13387
- let t6;
13413
+ const style = t4;
13414
+ let t5;
13388
13415
  if ($[8] !== entry || $[9] !== onClick) {
13389
- t6 = /* @__PURE__ */ jsxRuntime.jsx(NavigationCardBinding, { ...entry, onClick });
13416
+ t5 = /* @__PURE__ */ jsxRuntime.jsx(NavigationCardBinding, { ...entry, onClick });
13390
13417
  $[8] = entry;
13391
13418
  $[9] = onClick;
13392
- $[10] = t6;
13419
+ $[10] = t5;
13393
13420
  } else {
13394
- t6 = $[10];
13421
+ t5 = $[10];
13395
13422
  }
13396
- let t7;
13397
- if ($[11] !== attributes || $[12] !== listeners2 || $[13] !== setNodeRef || $[14] !== style || $[15] !== t6) {
13398
- t7 = /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners2, children: t6 });
13423
+ let t6;
13424
+ if ($[11] !== attributes || $[12] !== listeners2 || $[13] !== setNodeRef || $[14] !== style || $[15] !== t5) {
13425
+ t6 = /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners2, children: t5 });
13399
13426
  $[11] = attributes;
13400
13427
  $[12] = listeners2;
13401
13428
  $[13] = setNodeRef;
13402
13429
  $[14] = style;
13403
- $[15] = t6;
13404
- $[16] = t7;
13430
+ $[15] = t5;
13431
+ $[16] = t6;
13405
13432
  } else {
13406
- t7 = $[16];
13433
+ t6 = $[16];
13407
13434
  }
13408
- return t7;
13435
+ return t6;
13409
13436
  }
13410
13437
  function NavigationGroupDroppable(t0) {
13411
13438
  const $ = reactCompilerRuntime.c(11);
@@ -13488,44 +13515,42 @@
13488
13515
  isDragging
13489
13516
  } = sortable.useSortable(t1);
13490
13517
  let t2;
13491
- let t3;
13492
13518
  if ($[3] !== transform) {
13493
- t3 = transform ? utilities.CSS.Transform.toString(transform) : void 0;
13519
+ t2 = transform ? utilities.CSS.Transform.toString(transform) : void 0;
13494
13520
  $[3] = transform;
13495
- $[4] = t3;
13521
+ $[4] = t2;
13496
13522
  } else {
13497
- t3 = $[4];
13523
+ t2 = $[4];
13498
13524
  }
13499
- const t4 = isDragging ? 0 : 1;
13500
- let t5;
13501
- if ($[5] !== t3 || $[6] !== t4 || $[7] !== transition) {
13502
- t5 = {
13503
- transform: t3,
13525
+ const t3 = isDragging ? 0 : 1;
13526
+ let t4;
13527
+ if ($[5] !== t2 || $[6] !== t3 || $[7] !== transition) {
13528
+ t4 = {
13529
+ transform: t2,
13504
13530
  transition,
13505
- opacity: t4
13531
+ opacity: t3
13506
13532
  };
13507
- $[5] = t3;
13508
- $[6] = t4;
13533
+ $[5] = t2;
13534
+ $[6] = t3;
13509
13535
  $[7] = transition;
13510
- $[8] = t5;
13536
+ $[8] = t4;
13511
13537
  } else {
13512
- t5 = $[8];
13538
+ t4 = $[8];
13513
13539
  }
13514
- t2 = t5;
13515
- const style = t2;
13516
- let t6;
13540
+ const style = t4;
13541
+ let t5;
13517
13542
  if ($[9] !== attributes || $[10] !== children || $[11] !== listeners2 || $[12] !== setNodeRef || $[13] !== style) {
13518
- t6 = /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners2, children });
13543
+ t5 = /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, ...attributes, ...listeners2, children });
13519
13544
  $[9] = attributes;
13520
13545
  $[10] = children;
13521
13546
  $[11] = listeners2;
13522
13547
  $[12] = setNodeRef;
13523
13548
  $[13] = style;
13524
- $[14] = t6;
13549
+ $[14] = t5;
13525
13550
  } else {
13526
- t6 = $[14];
13551
+ t5 = $[14];
13527
13552
  }
13528
- return t6;
13553
+ return t5;
13529
13554
  }
13530
13555
  function useHomePageDnd({
13531
13556
  items: dndItems,
@@ -13559,8 +13584,8 @@
13559
13584
  }
13560
13585
  });
13561
13586
  const keyboardSensor = core.useSensor(core.KeyboardSensor);
13562
- const sensors = core.useSensors(...disabled ? [] : [mouseSensor, touchSensor, keyboardSensor]);
13563
- const dndContainers = React.useMemo(() => dndItems.map((g) => g.name), [dndItems]);
13587
+ const sensors = core.useSensors(mouseSensor, touchSensor, keyboardSensor);
13588
+ const dndContainers = dndItems.map((g) => g.name);
13564
13589
  const findDndContainer = React.useCallback((id) => {
13565
13590
  if (!id) return void 0;
13566
13591
  const group = dndItems.find((g_0) => g_0.name === id);
@@ -13771,14 +13796,8 @@
13771
13796
  return updated;
13772
13797
  });
13773
13798
  };
13774
- const activeItemForOverlay = React.useMemo(() => {
13775
- if (disabled || !activeId || activeIsGroup) return null;
13776
- return dndItems.flatMap((g_19) => g_19.entries).find((e_7) => e_7.url === activeId) || null;
13777
- }, [activeId, dndItems, disabled, activeIsGroup]);
13778
- const activeGroupData = React.useMemo(() => {
13779
- if (disabled || !activeId || !activeIsGroup) return null;
13780
- return dndItems.find((g_20) => g_20.name === activeId) || null;
13781
- }, [activeId, dndItems, disabled, activeIsGroup]);
13799
+ const activeItemForOverlay = disabled || !activeId || activeIsGroup ? null : dndItems.flatMap((g_19) => g_19.entries).find((e_7) => e_7.url === activeId) || null;
13800
+ const activeGroupData = disabled || !activeId || !activeIsGroup ? null : dndItems.find((g_20) => g_20.name === activeId) || null;
13782
13801
  return {
13783
13802
  sensors,
13784
13803
  collisionDetection,
@@ -14059,7 +14078,7 @@
14059
14078
  }
14060
14079
  let t12;
14061
14080
  if ($[28] !== onClose) {
14062
- t12 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onClose, variant: "text", children: "Cancel" });
14081
+ t12 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onClose, color: "primary", variant: "text", children: "Cancel" });
14063
14082
  $[28] = onClose;
14064
14083
  $[29] = t12;
14065
14084
  } else {
@@ -14068,7 +14087,7 @@
14068
14087
  const t13 = !!error || !name.trim();
14069
14088
  let t14;
14070
14089
  if ($[30] !== handleSave || $[31] !== t13) {
14071
- t14 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, disabled: t13, children: "Save" });
14090
+ t14 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, color: "primary", disabled: t13, children: "Save" });
14072
14091
  $[30] = handleSave;
14073
14092
  $[31] = t13;
14074
14093
  $[32] = t14;
@@ -14124,26 +14143,32 @@
14124
14143
  const fuse = React.useRef(null);
14125
14144
  const [filteredUrls, setFilteredUrls] = React.useState(null);
14126
14145
  const performingSearch = Boolean(filteredUrls);
14127
- const filteredNavigationEntries = filteredUrls ? rawNavigationEntries.filter((e) => filteredUrls.includes(e.url)) : rawNavigationEntries;
14146
+ const filteredNavigationEntries = React.useMemo(() => {
14147
+ return filteredUrls ? rawNavigationEntries.filter((e) => filteredUrls.includes(e.url)) : rawNavigationEntries;
14148
+ }, [filteredUrls, rawNavigationEntries]);
14128
14149
  React.useEffect(() => {
14129
14150
  fuse.current = new Fuse(rawNavigationEntries, {
14130
14151
  keys: ["name", "description", "group", "path"]
14131
14152
  });
14132
14153
  }, [rawNavigationEntries]);
14133
14154
  const updateSearch = React.useCallback((v) => {
14134
- if (!v?.trim()) return setFilteredUrls(null);
14135
- const r = fuse.current?.search(v.trim());
14136
- setFilteredUrls(r ? r.map((x) => x.item.url) : []);
14155
+ if (!v?.trim()) {
14156
+ setFilteredUrls(null);
14157
+ return;
14158
+ }
14159
+ const results = fuse.current?.search(v.trim());
14160
+ setFilteredUrls(results ? results.map((x) => x.item.url) : []);
14137
14161
  }, []);
14138
14162
  const [items, setItems] = React.useState([]);
14139
14163
  const [adminGroupData, setAdminGroupData] = React.useState(null);
14140
- React.useEffect(() => {
14141
- const src = performingSearch ? filteredNavigationEntries : rawNavigationEntries;
14164
+ const processedGroups = React.useMemo(() => {
14165
+ const src = filteredNavigationEntries;
14142
14166
  const entriesByGroup = {};
14143
14167
  src.forEach((e_0) => {
14144
14168
  const g = e_0.type === "admin" ? ADMIN_GROUP_NAME : e_0.group ?? DEFAULT_GROUP_NAME;
14145
14169
  (entriesByGroup[g] ??= []).push(e_0);
14146
14170
  });
14171
+ const hasPluginAdditionalCards = customizationController.plugins?.some((p) => p.homePage?.AdditionalCards);
14147
14172
  let allProcessed;
14148
14173
  if (performingSearch) {
14149
14174
  const ordered = [...new Set(src.map((e_1) => e_1.group ?? DEFAULT_GROUP_NAME))];
@@ -14162,24 +14187,31 @@
14162
14187
  entries: entriesByGroup[g_2]
14163
14188
  });
14164
14189
  });
14165
- allProcessed = allProcessed.filter((g_3) => g_3.entries.length || groupOrderFromNavController.includes(g_3.name));
14166
- }
14167
- const admin = allProcessed.find((g_4) => g_4.name === ADMIN_GROUP_NAME);
14168
- if (admin) {
14169
- setAdminGroupData(admin);
14170
- setItems(allProcessed.filter((g_5) => g_5.name !== ADMIN_GROUP_NAME));
14171
- } else {
14172
- setAdminGroupData(null);
14173
- setItems(allProcessed);
14190
+ if (hasPluginAdditionalCards && !allProcessed.some((g_3) => g_3.name === DEFAULT_GROUP_NAME)) {
14191
+ allProcessed.push({
14192
+ name: DEFAULT_GROUP_NAME,
14193
+ entries: []
14194
+ });
14195
+ }
14196
+ allProcessed = allProcessed.filter((g_4) => g_4.entries.length || groupOrderFromNavController.includes(g_4.name) || g_4.name === DEFAULT_GROUP_NAME && hasPluginAdditionalCards);
14174
14197
  }
14175
- }, [performingSearch, filteredNavigationEntries, rawNavigationEntries, groupOrderFromNavController]);
14198
+ const admin = allProcessed.find((g_5) => g_5.name === ADMIN_GROUP_NAME);
14199
+ return {
14200
+ adminGroupData: admin || null,
14201
+ items: allProcessed.filter((g_6) => g_6.name !== ADMIN_GROUP_NAME)
14202
+ };
14203
+ }, [filteredNavigationEntries, performingSearch, groupOrderFromNavController, customizationController.plugins]);
14204
+ React.useEffect(() => {
14205
+ setAdminGroupData(processedGroups.adminGroupData);
14206
+ setItems(processedGroups.items);
14207
+ }, [processedGroups]);
14176
14208
  const updateItems = (updater) => {
14177
14209
  setItems(updater);
14178
14210
  };
14179
14211
  const persistNavigationGroups = (latest) => {
14180
- const draggable = latest.map((g_6) => ({
14181
- name: g_6.name,
14182
- entries: g_6.entries.map((e_2) => e_2.path)
14212
+ const draggable = latest.map((g_7) => ({
14213
+ name: g_7.name,
14214
+ entries: g_7.entries.map((e_2) => e_2.path)
14183
14215
  }));
14184
14216
  const all = adminGroupData ? [...draggable, {
14185
14217
  name: adminGroupData.name,
@@ -14212,8 +14244,8 @@
14212
14244
  disabled: !allowDragAndDrop || performingSearch,
14213
14245
  onPersist: persistNavigationGroups,
14214
14246
  // ——► persistence here
14215
- onGroupMoved: (g_7) => context.analyticsController?.onAnalyticsEvent?.("home_move_group", {
14216
- name: g_7
14247
+ onGroupMoved: (g_8) => context.analyticsController?.onAnalyticsEvent?.("home_move_group", {
14248
+ name: g_8
14217
14249
  }),
14218
14250
  onCardMovedBetweenGroups: (card) => context.analyticsController?.onAnalyticsEvent?.("home_move_card", {
14219
14251
  id: card.id
@@ -14225,10 +14257,7 @@
14225
14257
  direction
14226
14258
  } = useRestoreScroll();
14227
14259
  const dndDisabled = !allowDragAndDrop || performingSearch;
14228
- const dndModifiers = React.useMemo(() => {
14229
- if (dndKitActiveNode?.data.current?.type === "group") return [modifiers.restrictToVerticalAxis, modifiers.restrictToWindowEdges];
14230
- return [modifiers.restrictToWindowEdges];
14231
- }, [dndKitActiveNode]);
14260
+ const dndModifiers = dndKitActiveNode?.data.current?.type === "group" ? [modifiers.restrictToVerticalAxis, modifiers.restrictToWindowEdges] : [modifiers.restrictToWindowEdges];
14232
14261
  let additionalPluginChildrenStart;
14233
14262
  let additionalPluginChildrenEnd;
14234
14263
  let additionalPluginSections;
@@ -14236,12 +14265,12 @@
14236
14265
  const sectionProps = {
14237
14266
  context
14238
14267
  };
14239
- additionalPluginSections = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: customizationController.plugins.filter((p) => p.homePage?.includeSection).map((plugin) => {
14268
+ additionalPluginSections = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: customizationController.plugins.filter((p_0) => p_0.homePage?.includeSection).map((plugin) => {
14240
14269
  const section = plugin.homePage.includeSection(sectionProps);
14241
14270
  return /* @__PURE__ */ jsxRuntime.jsx(NavigationGroup, { group: section.title, children: section.children }, `plugin_section_${plugin.key}`);
14242
14271
  }) });
14243
- additionalPluginChildrenStart = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: customizationController.plugins.filter((p_0) => p_0.homePage?.additionalChildrenStart).map((plugin_0, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: plugin_0.homePage.additionalChildrenStart }, `plugin_children_start_${i}`)) });
14244
- additionalPluginChildrenEnd = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: customizationController.plugins.filter((p_1) => p_1.homePage?.additionalChildrenEnd).map((plugin_1, i_0) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: plugin_1.homePage.additionalChildrenEnd }, `plugin_children_end_${i_0}`)) });
14272
+ additionalPluginChildrenStart = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: customizationController.plugins.filter((p_1) => p_1.homePage?.additionalChildrenStart).map((plugin_0, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: plugin_0.homePage.additionalChildrenStart }, `plugin_children_start_${i}`)) });
14273
+ additionalPluginChildrenEnd = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: customizationController.plugins.filter((p_2) => p_2.homePage?.additionalChildrenEnd).map((plugin_1, i_0) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: plugin_1.homePage.additionalChildrenEnd }, `plugin_children_end_${i_0}`)) });
14245
14274
  }
14246
14275
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: "py-2 overflow-auto h-full w-full", children: [
14247
14276
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { maxWidth: "6xl", children: [
@@ -14264,8 +14293,8 @@
14264
14293
  const groupKey = groupData.name;
14265
14294
  const entriesInGroup = groupData.entries;
14266
14295
  const AdditionalCards = [];
14267
- customizationController.plugins?.forEach((p_2) => {
14268
- if (p_2.homePage?.AdditionalCards) AdditionalCards.push(...toArray(p_2.homePage.AdditionalCards));
14296
+ customizationController.plugins?.forEach((p_3) => {
14297
+ if (p_3.homePage?.AdditionalCards) AdditionalCards.push(...toArray(p_3.homePage.AdditionalCards));
14269
14298
  });
14270
14299
  const actionProps = {
14271
14300
  group: groupKey === DEFAULT_GROUP_NAME ? void 0 : groupKey,
@@ -14307,7 +14336,7 @@
14307
14336
  additionalPluginChildrenEnd,
14308
14337
  additionalChildrenEnd
14309
14338
  ] }),
14310
- dialogOpenForGroup && /* @__PURE__ */ jsxRuntime.jsx(RenameGroupDialog, { open: true, initialName: dialogOpenForGroup, existingGroupNames: items.map((g_8) => g_8.name).filter((n) => n !== dialogOpenForGroup), onClose: () => setDialogOpenForGroup(null), onRename: (newName) => {
14339
+ 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) => {
14311
14340
  handleRenameGroup(dialogOpenForGroup, newName);
14312
14341
  setDialogOpenForGroup(null);
14313
14342
  } })
@@ -14372,11 +14401,11 @@
14372
14401
  if (selectionEnabled) {
14373
14402
  let t42;
14374
14403
  if ($[12] !== largeLayout || $[13] !== multipleDeleteEnabled || $[14] !== onMultipleDeleteClick || $[15] !== selectedEntities?.length) {
14375
- t42 = largeLayout ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "text", disabled: !selectedEntities?.length || !multipleDeleteEnabled, startIcon: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, {}), onClick: onMultipleDeleteClick, color: "primary", className: "lg:w-20", children: [
14404
+ t42 = largeLayout ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "text", disabled: !selectedEntities?.length || !multipleDeleteEnabled, startIcon: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, { size: "small" }), onClick: onMultipleDeleteClick, color: "primary", className: "lg:w-20", children: [
14376
14405
  "(",
14377
14406
  selectedEntities?.length,
14378
14407
  ")"
14379
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { color: "primary", disabled: !selectedEntities?.length || !multipleDeleteEnabled, onClick: onMultipleDeleteClick, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, {}) });
14408
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", color: "primary", disabled: !selectedEntities?.length || !multipleDeleteEnabled, onClick: onMultipleDeleteClick, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DeleteIcon, { size: "small" }) });
14380
14409
  $[12] = largeLayout;
14381
14410
  $[13] = multipleDeleteEnabled;
14382
14411
  $[14] = onMultipleDeleteClick;
@@ -14688,7 +14717,7 @@
14688
14717
  return !ele || ele === document.body ? document.body : isScrollable(ele) ? ele : getScrollableParent(ele.parentNode);
14689
14718
  };
14690
14719
  function EntityFormActions(t0) {
14691
- const $ = reactCompilerRuntime.c(14);
14720
+ const $ = reactCompilerRuntime.c(16);
14692
14721
  const {
14693
14722
  fullPath,
14694
14723
  fullIdPath,
@@ -14700,12 +14729,14 @@
14700
14729
  disabled,
14701
14730
  status,
14702
14731
  pluginActions,
14703
- openEntityMode
14732
+ openEntityMode,
14733
+ navigateBack,
14734
+ formContext
14704
14735
  } = t0;
14705
14736
  const context = useFireCMSContext();
14706
14737
  const sideEntityController = useSideEntityController();
14707
14738
  let t1;
14708
- if ($[0] !== collection || $[1] !== context || $[2] !== disabled || $[3] !== entity || $[4] !== formex2.isSubmitting || $[5] !== fullIdPath || $[6] !== fullPath || $[7] !== layout || $[8] !== openEntityMode || $[9] !== pluginActions || $[10] !== savingError || $[11] !== sideEntityController || $[12] !== status) {
14739
+ 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) {
14709
14740
  t1 = layout === "bottom" ? buildBottomActions$1({
14710
14741
  fullPath,
14711
14742
  fullIdPath,
@@ -14718,7 +14749,9 @@
14718
14749
  disabled,
14719
14750
  status,
14720
14751
  pluginActions,
14721
- openEntityMode
14752
+ openEntityMode,
14753
+ navigateBack,
14754
+ formContext
14722
14755
  }) : buildSideActions$1({
14723
14756
  fullPath,
14724
14757
  fullIdPath,
@@ -14731,24 +14764,28 @@
14731
14764
  disabled,
14732
14765
  status,
14733
14766
  pluginActions,
14734
- openEntityMode
14767
+ openEntityMode,
14768
+ navigateBack,
14769
+ formContext
14735
14770
  });
14736
14771
  $[0] = collection;
14737
14772
  $[1] = context;
14738
14773
  $[2] = disabled;
14739
14774
  $[3] = entity;
14740
- $[4] = formex2.isSubmitting;
14741
- $[5] = fullIdPath;
14742
- $[6] = fullPath;
14743
- $[7] = layout;
14744
- $[8] = openEntityMode;
14745
- $[9] = pluginActions;
14746
- $[10] = savingError;
14747
- $[11] = sideEntityController;
14748
- $[12] = status;
14749
- $[13] = t1;
14775
+ $[4] = formContext;
14776
+ $[5] = formex2.isSubmitting;
14777
+ $[6] = fullIdPath;
14778
+ $[7] = fullPath;
14779
+ $[8] = layout;
14780
+ $[9] = navigateBack;
14781
+ $[10] = openEntityMode;
14782
+ $[11] = pluginActions;
14783
+ $[12] = savingError;
14784
+ $[13] = sideEntityController;
14785
+ $[14] = status;
14786
+ $[15] = t1;
14750
14787
  } else {
14751
- t1 = $[13];
14788
+ t1 = $[15];
14752
14789
  }
14753
14790
  return t1;
14754
14791
  }
@@ -14765,24 +14802,29 @@
14765
14802
  disabled,
14766
14803
  status,
14767
14804
  pluginActions,
14768
- openEntityMode
14805
+ openEntityMode,
14806
+ navigateBack,
14807
+ formContext
14769
14808
  }) {
14770
14809
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { position: "absolute", children: [
14771
14810
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) }),
14772
14811
  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) => {
14773
14812
  event.stopPropagation();
14774
14813
  if (entity) action.onClick({
14814
+ view: "form",
14775
14815
  entity,
14776
14816
  fullPath: fullPath ?? collection.path,
14777
14817
  fullIdPath: fullIdPath ?? collection.id,
14778
14818
  collection,
14779
14819
  context,
14780
14820
  sideEntityController,
14781
- openEntityMode
14821
+ openEntityMode,
14822
+ navigateBack,
14823
+ formContext
14782
14824
  });
14783
14825
  }, children: action.icon }, action.name)) }),
14784
14826
  pluginActions,
14785
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
14827
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || isSubmitting, color: "primary", type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
14786
14828
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "filled", color: "primary", type: "submit", disabled: disabled || isSubmitting, children: [
14787
14829
  status === "existing" && "Save",
14788
14830
  status === "copy" && "Create copy",
@@ -14794,6 +14836,9 @@
14794
14836
  savingError,
14795
14837
  entity,
14796
14838
  formActions,
14839
+ fullPath,
14840
+ fullIdPath,
14841
+ openEntityMode,
14797
14842
  collection,
14798
14843
  context,
14799
14844
  sideEntityController,
@@ -14841,6 +14886,15 @@
14841
14886
  if (collection.customId && collection.formAutoSave) {
14842
14887
  console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
14843
14888
  }
14889
+ const sideEntityController = useSideEntityController();
14890
+ const navigationController = useNavigationController();
14891
+ const navigateBack = React.useCallback(() => {
14892
+ if (openEntityMode === "side_panel") {
14893
+ sideEntityController.close();
14894
+ } else {
14895
+ window.history.back();
14896
+ }
14897
+ }, []);
14844
14898
  const authController = useAuthController();
14845
14899
  const [status, setStatus] = React.useState(initialStatus);
14846
14900
  const updateStatus = (status_0) => {
@@ -15091,14 +15145,15 @@
15091
15145
  const pluginActions = [];
15092
15146
  const plugins = customizationController.plugins;
15093
15147
  const actionsDisabled = disabled || formex$1.isSubmitting || status === "existing" && !formex$1.dirty || Boolean(disabledProp);
15148
+ const parentCollectionIds = navigationController.getParentCollectionIds(path);
15094
15149
  if (plugins && collection) {
15095
15150
  const actionProps = {
15096
15151
  entityId,
15152
+ parentCollectionIds,
15097
15153
  path,
15098
15154
  status,
15099
15155
  collection,
15100
15156
  context,
15101
- currentEntityId: entityId,
15102
15157
  formContext,
15103
15158
  openEntityMode,
15104
15159
  disabled: actionsDisabled
@@ -15210,13 +15265,15 @@
15210
15265
  const formView = /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15211
15266
  !Builder && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full py-2 flex flex-col items-start my-4 lg:my-6", children: [
15212
15267
  /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { className: "my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : ""), variant: "h4", children: title ?? collection.singularName ?? collection.name }),
15213
- showEntityPath && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { color: "base", className: "w-full", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs("code", { className: "text-xs select-all text-text-secondary dark:text-text-secondary-dark", children: [
15268
+ !entity?.values && initialStatus === "existing" && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { color: "warning", size: "small", outerClassName: "w-full mb-4 text-xs", children: "This entity does not exist in the database" }),
15269
+ showEntityPath && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { color: "base", outerClassName: "w-full", size: "small", children: /* @__PURE__ */ jsxRuntime.jsxs("code", { className: "text-xs select-all text-text-secondary dark:text-text-secondary-dark", children: [
15214
15270
  entity?.path ?? path,
15215
15271
  "/",
15216
15272
  entityId
15217
15273
  ] }) })
15218
15274
  ] }),
15219
15275
  children,
15276
+ initialEntityId && !entity && initialStatus !== "new" && /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { color: "info", size: "small", children: "This entity does not exist in the database" }),
15220
15277
  !Builder && !collection.hideIdFromForm && /* @__PURE__ */ jsxRuntime.jsx(CustomIdField, { customId: collection.customId, entityId, status, onChange: setEntityId, error: entityIdError, loading: customIdLoading, entity }),
15221
15278
  entityId && formContext && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-12 flex flex-col gap-8", ref: formRef, children: [
15222
15279
  formFields(),
@@ -15230,7 +15287,7 @@
15230
15287
  if (!resolvedCollection || !path) {
15231
15288
  throw Error("INTERNAL: Collection and path must be defined in form context");
15232
15289
  }
15233
- 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, openEntityMode, showDefaultActions });
15290
+ 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, openEntityMode, showDefaultActions, navigateBack, formContext });
15234
15291
  return /* @__PURE__ */ jsxRuntime.jsx(formex.Formex, { value: formex$1, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: formex$1.handleSubmit, onReset: () => formex$1.resetForm({
15235
15292
  values: getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs)
15236
15293
  }), noValidate: true, className: ui.cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className), children: [
@@ -16085,7 +16142,7 @@
16085
16142
  const resolvedProperty = t6;
16086
16143
  let t7;
16087
16144
  if ($[21] !== minimalistView || $[22] !== property || $[23] !== propertyKey) {
16088
- t7 = !minimalistView && /* @__PURE__ */ jsxRuntime.jsx(LabelWithIconAndTooltip, { propertyKey, icon: getIconForProperty(property, "small"), required: property.validation?.required, title: property.name, className: "h-8text-text-secondary dark:text-text-secondary-dark ml-3.5" });
16145
+ t7 = !minimalistView && /* @__PURE__ */ jsxRuntime.jsx(LabelWithIconAndTooltip, { propertyKey, icon: getIconForProperty(property, "small"), required: property.validation?.required, title: property.name, className: "h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5" });
16089
16146
  $[21] = minimalistView;
16090
16147
  $[22] = property;
16091
16148
  $[23] = propertyKey;
@@ -18991,6 +19048,9 @@
18991
19048
  const [fieldVersion, setFieldVersion] = React.useState(0);
18992
19049
  const internalValue = React.useRef(value);
18993
19050
  const onContentChange = React.useCallback((content) => {
19051
+ if (content === value || value === null && content === "") {
19052
+ return;
19053
+ }
18994
19054
  internalValue.current = content;
18995
19055
  setValue(content);
18996
19056
  }, [setValue]);
@@ -19990,9 +20050,10 @@
19990
20050
  frozen
19991
20051
  }) => {
19992
20052
  const isSelected = Boolean(usedSelectionController.selectedEntities.find((e_1) => e_1.id == entity_6.id && e_1.path == entity_6.path));
20053
+ const customEntityActions_0 = (collection.entityActions ?? []).map((action) => resolveEntityAction(action, customizationController.entityActions)).filter(Boolean);
19993
20054
  const actions_0 = getActionsForEntity({
19994
20055
  entity: entity_6,
19995
- customEntityActions: collection.entityActions
20056
+ customEntityActions: customEntityActions_0
19996
20057
  });
19997
20058
  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 });
19998
20059
  }, [updateLastDeleteTimestamp, usedSelectionController]);
@@ -20352,7 +20413,7 @@
20352
20413
  }
20353
20414
  let t4;
20354
20415
  if ($[6] !== onCancel) {
20355
- t4 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", onClick: onCancel, autoFocus: true, children: "Cancel" });
20416
+ t4 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { color: "primary", variant: "text", onClick: onCancel, autoFocus: true, children: "Cancel" });
20356
20417
  $[6] = onCancel;
20357
20418
  $[7] = t4;
20358
20419
  } else {
@@ -22255,7 +22316,7 @@
22255
22316
  }
22256
22317
  let t5;
22257
22318
  if ($[7] !== handleCancel) {
22258
- t5 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", onClick: handleCancel, autoFocus: true, children: " Cancel " });
22319
+ t5 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", color: "primary", onClick: handleCancel, autoFocus: true, children: " Cancel " });
22259
22320
  $[7] = handleCancel;
22260
22321
  $[8] = t5;
22261
22322
  } else {
@@ -22263,7 +22324,7 @@
22263
22324
  }
22264
22325
  let t6;
22265
22326
  if ($[9] !== handleOk) {
22266
- t6 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleOk, children: " Ok " });
22327
+ t6 = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { color: "primary", onClick: handleOk, children: " Ok " });
22267
22328
  $[9] = handleOk;
22268
22329
  $[10] = t6;
22269
22330
  } else {
@@ -22407,14 +22468,17 @@
22407
22468
  status,
22408
22469
  pluginActions,
22409
22470
  openEntityMode,
22410
- showDefaultActions = true
22471
+ showDefaultActions = true,
22472
+ navigateBack,
22473
+ formContext
22411
22474
  }) {
22412
22475
  const authController = useAuthController();
22413
22476
  const context = useFireCMSContext();
22414
22477
  const sideEntityController = useSideEntityController();
22415
22478
  const sideDialogContext = useSideDialogContext();
22479
+ const customizationController = useCustomizationController();
22416
22480
  const entityActions = React.useMemo(() => {
22417
- const customEntityActions = collection.entityActions;
22481
+ const customEntityActions = (collection.entityActions ?? []).map((action) => resolveEntityAction(action, customizationController.entityActions)).filter(Boolean);
22418
22482
  const createEnabled = canCreateEntity(collection, authController, path, null);
22419
22483
  const deleteEnabled = entity ? canDeleteEntity(collection, authController, path, entity) : false;
22420
22484
  const actions = [];
@@ -22422,7 +22486,7 @@
22422
22486
  if (deleteEnabled) actions.push(deleteEntityAction);
22423
22487
  if (customEntityActions) return mergeEntityActions(actions, customEntityActions);
22424
22488
  return actions;
22425
- }, [authController, collection, path]);
22489
+ }, [authController, collection, path, customizationController.entityActions?.length]);
22426
22490
  const formActions = showDefaultActions ? entityActions.filter((a) => a.includeInForm === void 0 || a.includeInForm) : [];
22427
22491
  return layout === "bottom" ? buildBottomActions({
22428
22492
  savingError,
@@ -22436,7 +22500,9 @@
22436
22500
  status,
22437
22501
  sideDialogContext,
22438
22502
  pluginActions,
22439
- openEntityMode
22503
+ openEntityMode,
22504
+ navigateBack,
22505
+ formContext
22440
22506
  }) : buildSideActions({
22441
22507
  savingError,
22442
22508
  entity,
@@ -22449,7 +22515,9 @@
22449
22515
  disabled,
22450
22516
  status,
22451
22517
  pluginActions,
22452
- openEntityMode
22518
+ openEntityMode,
22519
+ navigateBack,
22520
+ formContext
22453
22521
  });
22454
22522
  }
22455
22523
  function buildBottomActions({
@@ -22464,22 +22532,28 @@
22464
22532
  status,
22465
22533
  sideDialogContext,
22466
22534
  pluginActions,
22467
- openEntityMode
22535
+ openEntityMode,
22536
+ navigateBack,
22537
+ formContext
22468
22538
  }) {
22469
22539
  const canClose = openEntityMode === "side_panel";
22470
22540
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.DialogActions, { position: "absolute", children: [
22471
22541
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) }),
22472
- 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) => {
22473
- event.stopPropagation();
22474
- if (entity) action.onClick({
22542
+ formActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow flex overflow-auto no-scrollbar", children: formActions.map((action) => {
22543
+ const props = {
22544
+ view: "form",
22475
22545
  entity,
22476
22546
  fullPath: collection.path,
22477
22547
  collection,
22478
22548
  context,
22479
22549
  sideEntityController,
22480
- openEntityMode
22481
- });
22482
- }, children: action.icon }, action.name)) }),
22550
+ openEntityMode,
22551
+ navigateBack,
22552
+ formContext
22553
+ };
22554
+ const isEnabled = !action.isEnabled || action.isEnabled(props);
22555
+ return /* @__PURE__ */ jsxRuntime.jsx(EntityActionButton, { action, enabled: isEnabled, props }, action.key);
22556
+ }) }),
22483
22557
  pluginActions,
22484
22558
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22485
22559
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: canClose ? "text" : "filled", color: "primary", type: "submit", disabled: disabled || isSubmitting, onClick: () => {
@@ -22509,7 +22583,10 @@
22509
22583
  disabled,
22510
22584
  status,
22511
22585
  sideDialogContext,
22512
- pluginActions
22586
+ pluginActions,
22587
+ openEntityMode,
22588
+ navigateBack,
22589
+ formContext
22513
22590
  }) {
22514
22591
  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: [
22515
22592
  /* @__PURE__ */ jsxRuntime.jsxs(ui.LoadingButton, { fullWidth: true, variant: "filled", color: "primary", type: "submit", size: "large", disabled: disabled || isSubmitting, onClick: () => {
@@ -22521,9 +22598,104 @@
22521
22598
  ] }),
22522
22599
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { fullWidth: true, variant: "text", disabled: disabled || isSubmitting, type: "reset", children: status === "existing" ? "Discard" : "Clear" }),
22523
22600
  pluginActions,
22601
+ formActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row flex-wrap mt-2", children: formActions.map((action) => {
22602
+ const props = {
22603
+ view: "form",
22604
+ entity,
22605
+ fullPath: collection.path,
22606
+ collection,
22607
+ context,
22608
+ sideEntityController,
22609
+ openEntityMode,
22610
+ navigateBack,
22611
+ formContext
22612
+ };
22613
+ const isEnabled = !action.isEnabled || action.isEnabled(props);
22614
+ return /* @__PURE__ */ jsxRuntime.jsx(EntityActionButton, { action, enabled: isEnabled, props }, action.key);
22615
+ }) }),
22524
22616
  savingError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { color: "error", children: savingError.message }) })
22525
22617
  ] });
22526
22618
  }
22619
+ function EntityActionButton(t0) {
22620
+ const $ = reactCompilerRuntime.c(14);
22621
+ const {
22622
+ action,
22623
+ enabled,
22624
+ props
22625
+ } = t0;
22626
+ const snackbarController = useSnackbarController();
22627
+ const [loading, setLoading] = React.useState(false);
22628
+ const t1 = !enabled;
22629
+ let t2;
22630
+ if ($[0] !== action || $[1] !== props || $[2] !== snackbarController) {
22631
+ t2 = (event) => {
22632
+ console.debug("Executing action", action.key, props);
22633
+ try {
22634
+ event.stopPropagation();
22635
+ if (props.entity) {
22636
+ const onClick = action.onClick(props);
22637
+ if (onClick instanceof Promise) {
22638
+ setLoading(true);
22639
+ onClick.catch((error) => {
22640
+ console.error("Error executing action", action.key, error);
22641
+ snackbarController.open({
22642
+ message: `Error executing action: ${error.message}`,
22643
+ type: "error"
22644
+ });
22645
+ }).finally(() => setLoading(false));
22646
+ } else {
22647
+ snackbarController.open({
22648
+ message: `Action ${action.name} executed successfully`,
22649
+ type: "success"
22650
+ });
22651
+ }
22652
+ }
22653
+ } catch (t32) {
22654
+ const e = t32;
22655
+ console.error("Error executing action", action.key, e);
22656
+ snackbarController.open({
22657
+ message: `Error executing action: ${e.message}`,
22658
+ type: "error"
22659
+ });
22660
+ }
22661
+ };
22662
+ $[0] = action;
22663
+ $[1] = props;
22664
+ $[2] = snackbarController;
22665
+ $[3] = t2;
22666
+ } else {
22667
+ t2 = $[3];
22668
+ }
22669
+ let t3;
22670
+ if ($[4] !== action.icon || $[5] !== loading) {
22671
+ t3 = loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.CircularProgress, { size: "smallest" }) : action.icon;
22672
+ $[4] = action.icon;
22673
+ $[5] = loading;
22674
+ $[6] = t3;
22675
+ } else {
22676
+ t3 = $[6];
22677
+ }
22678
+ let t4;
22679
+ if ($[7] !== t1 || $[8] !== t2 || $[9] !== t3) {
22680
+ t4 = /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { color: "primary", disabled: t1, onClick: t2, children: t3 });
22681
+ $[7] = t1;
22682
+ $[8] = t2;
22683
+ $[9] = t3;
22684
+ $[10] = t4;
22685
+ } else {
22686
+ t4 = $[10];
22687
+ }
22688
+ let t5;
22689
+ if ($[11] !== action.name || $[12] !== t4) {
22690
+ t5 = /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: action.name, children: t4 });
22691
+ $[11] = action.name;
22692
+ $[12] = t4;
22693
+ $[13] = t5;
22694
+ } else {
22695
+ t5 = $[13];
22696
+ }
22697
+ return t5;
22698
+ }
22527
22699
  function EntityJsonPreview(t0) {
22528
22700
  const $ = reactCompilerRuntime.c(6);
22529
22701
  const {
@@ -22699,6 +22871,22 @@
22699
22871
  const [formContext, setFormContext] = React.useState(void 0);
22700
22872
  const largeLayout = useLargeLayout();
22701
22873
  const customizationController = useCustomizationController();
22874
+ const plugins = customizationController.plugins;
22875
+ const pluginActionsTop = [];
22876
+ if (plugins && collection) {
22877
+ const actionProps = {
22878
+ entityId,
22879
+ parentCollectionIds,
22880
+ path,
22881
+ status,
22882
+ collection,
22883
+ context,
22884
+ formContext,
22885
+ openEntityMode: layout,
22886
+ disabled: false
22887
+ };
22888
+ pluginActionsTop.push(...plugins.map((plugin) => plugin.form?.ActionsTop ? /* @__PURE__ */ jsxRuntime.jsx(plugin.form.ActionsTop, { ...actionProps }, `actions_${plugin.key}`) : null).filter(Boolean));
22889
+ }
22702
22890
  const defaultSelectedView = React.useMemo(() => resolveDefaultSelectedView(collection ? collection.defaultSelectedView : void 0, {
22703
22891
  status,
22704
22892
  entityId
@@ -22715,7 +22903,6 @@
22715
22903
  const customViewsCount = customViews?.length ?? 0;
22716
22904
  const includeJsonView = collection.includeJsonView === void 0 ? true : collection.includeJsonView;
22717
22905
  const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
22718
- const plugins = customizationController.plugins;
22719
22906
  const {
22720
22907
  resolvedEntityViews,
22721
22908
  selectedEntityView,
@@ -22773,13 +22960,6 @@
22773
22960
  const subcollectionId = subcollection.id ?? subcollection.path;
22774
22961
  const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection.path)}` : void 0;
22775
22962
  const newFullIdPath = fullIdPath ? `${fullIdPath}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : void 0;
22776
- console.debug("Rendering subcollection", {
22777
- subcollectionId,
22778
- fullIdPath,
22779
- newFullPath,
22780
- newFullIdPath,
22781
- selectedTab
22782
- });
22783
22963
  if (selectedTab !== subcollectionId) return null;
22784
22964
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 h-full overflow-auto w-full", role: "tabpanel", children: [
22785
22965
  globalLoading && /* @__PURE__ */ jsxRuntime.jsx(CircularProgressCenter, {}),
@@ -22824,11 +23004,12 @@
22824
23004
  const customViewTabsEnd = resolvedEntityViews.filter((view_1) => !view_1.position || view_1.position === "end").map((view_2) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tab, { className: !view_2.tabComponent ? "text-sm min-w-[120px]" : void 0, value: view_2.key, children: view_2.tabComponent ?? view_2.name }, `entity_detail_collection_tab_${view_2.name}`));
22825
23005
  const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
22826
23006
  let result = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col h-full w-full bg-white dark:bg-surface-900", children: [
22827
- shouldShowTopBar && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cls("h-14 flex overflow-visible overflow-x-scroll w-full no-scrollbar h-14 border-b pl-2 pr-2 pt-1 flex items-end bg-surface-50 dark:bg-surface-900", ui.defaultBorderMixin), children: [
23007
+ 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: [
22828
23008
  barActions,
22829
23009
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow" }),
23010
+ pluginActionsTop,
22830
23011
  globalLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "self-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CircularProgress, { size: "small" }) }),
22831
- hasAdditionalViews && /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { value: selectedTab, onValueChange: (value_1) => {
23012
+ hasAdditionalViews && /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { className: "self-end", value: selectedTab, onValueChange: (value_1) => {
22832
23013
  onSideTabClick(value_1);
22833
23014
  }, children: [
22834
23015
  includeJsonView && /* @__PURE__ */ jsxRuntime.jsx(ui.Tab, { disabled: !hasAdditionalViews, value: JSON_TAB_VALUE, className: "text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CodeIcon, { size: "small" }) }),
@@ -22847,9 +23028,9 @@
22847
23028
  subCollectionsViews
22848
23029
  ] });
22849
23030
  if (plugins) {
22850
- plugins.forEach((plugin) => {
22851
- if (plugin.form?.provider) {
22852
- result = /* @__PURE__ */ jsxRuntime.jsx(plugin.form.provider.Component, { status, path, collection, entity: usedEntity, context, formContext, ...plugin.form.provider.props, children: result });
23031
+ plugins.forEach((plugin_0) => {
23032
+ if (plugin_0.form?.provider) {
23033
+ result = /* @__PURE__ */ jsxRuntime.jsx(plugin_0.form.provider.Component, { status, path, collection, entity: usedEntity, context, formContext, ...plugin_0.form.provider.props, children: result });
22853
23034
  }
22854
23035
  });
22855
23036
  }
@@ -22903,7 +23084,7 @@
22903
23084
  const parentCollectionIds = React.useMemo(() => {
22904
23085
  return navigationController.getParentCollectionIds(path);
22905
23086
  }, [navigationController, path]);
22906
- const collection = props.collection ?? navigationController.getCollection(path);
23087
+ const collection = navigationController.getCollection(fullIdPath ?? path) ?? props.collection;
22907
23088
  React.useEffect(() => {
22908
23089
  function beforeunload(e) {
22909
23090
  if (blocked && collection) {
@@ -23685,6 +23866,7 @@
23685
23866
  onAnalyticsEvent,
23686
23867
  propertyConfigs,
23687
23868
  entityViews,
23869
+ entityActions,
23688
23870
  components,
23689
23871
  navigationController,
23690
23872
  apiKey
@@ -23703,6 +23885,7 @@
23703
23885
  entityLinkBuilder,
23704
23886
  plugins,
23705
23887
  entityViews: entityViews ?? [],
23888
+ entityActions: entityActions ?? [],
23706
23889
  propertyConfigs: propertyConfigs ?? {},
23707
23890
  components
23708
23891
  };
@@ -25577,6 +25760,7 @@
25577
25760
  exports2.resolveCollection = resolveCollection;
25578
25761
  exports2.resolveCollectionPathIds = resolveCollectionPathIds;
25579
25762
  exports2.resolveDefaultSelectedView = resolveDefaultSelectedView;
25763
+ exports2.resolveEntityAction = resolveEntityAction;
25580
25764
  exports2.resolveEntityView = resolveEntityView;
25581
25765
  exports2.resolveEnumValues = resolveEnumValues;
25582
25766
  exports2.resolveNavigationFrom = resolveNavigationFrom;