@firecms/core 3.0.0-beta.14 → 3.0.0-beta.15

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 (85) hide show
  1. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  2. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  3. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  4. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  5. package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
  6. package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
  7. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  8. package/dist/core/EntityEditView.d.ts +3 -0
  9. package/dist/core/EntityEditViewFormActions.d.ts +1 -1
  10. package/dist/core/field_configs.d.ts +1 -1
  11. package/dist/form/EntityForm.d.ts +2 -1
  12. package/dist/form/EntityFormActions.d.ts +6 -2
  13. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  14. package/dist/form/index.d.ts +1 -0
  15. package/dist/hooks/useBuildNavigationController.d.ts +51 -2
  16. package/dist/index.es.js +2184 -1052
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.umd.js +2181 -1049
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/types/analytics.d.ts +1 -1
  21. package/dist/types/collections.d.ts +8 -2
  22. package/dist/types/customization_controller.d.ts +8 -0
  23. package/dist/types/entity_actions.d.ts +46 -6
  24. package/dist/types/firecms.d.ts +8 -0
  25. package/dist/types/navigation.d.ts +21 -5
  26. package/dist/types/plugins.d.ts +20 -1
  27. package/dist/types/properties.d.ts +7 -0
  28. package/dist/types/property_config.d.ts +1 -1
  29. package/dist/types/side_entity_controller.d.ts +4 -0
  30. package/dist/util/icons.d.ts +2 -2
  31. package/dist/util/navigation_from_path.d.ts +4 -0
  32. package/dist/util/resolutions.d.ts +2 -1
  33. package/package.json +5 -5
  34. package/src/components/ConfirmationDialog.tsx +1 -0
  35. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
  36. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +25 -3
  37. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +2 -2
  38. package/src/components/EntityCollectionView/EntityCollectionView.tsx +7 -4
  39. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
  40. package/src/components/FireCMSLogo.tsx +7 -51
  41. package/src/components/HomePage/DefaultHomePage.tsx +491 -157
  42. package/src/components/HomePage/FavouritesView.tsx +3 -3
  43. package/src/components/HomePage/HomePageDnD.tsx +599 -0
  44. package/src/components/HomePage/NavigationCard.tsx +47 -38
  45. package/src/components/HomePage/NavigationCardBinding.tsx +10 -6
  46. package/src/components/HomePage/NavigationGroup.tsx +63 -29
  47. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  48. package/src/components/UnsavedChangesDialog.tsx +6 -2
  49. package/src/components/common/default_entity_actions.tsx +25 -9
  50. package/src/components/common/useDataSourceTableController.tsx +2 -2
  51. package/src/core/DefaultDrawer.tsx +8 -8
  52. package/src/core/DrawerNavigationItem.tsx +1 -1
  53. package/src/core/EntityEditView.tsx +41 -6
  54. package/src/core/EntityEditViewFormActions.tsx +154 -29
  55. package/src/core/EntitySidePanel.tsx +5 -2
  56. package/src/core/FireCMS.tsx +2 -0
  57. package/src/core/field_configs.tsx +15 -1
  58. package/src/form/EntityForm.tsx +36 -4
  59. package/src/form/EntityFormActions.tsx +51 -9
  60. package/src/form/components/StorageItemPreview.tsx +1 -1
  61. package/src/form/components/StorageUploadProgress.tsx +3 -3
  62. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
  63. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  64. package/src/form/field_bindings/RepeatFieldBinding.tsx +0 -1
  65. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +12 -17
  66. package/src/form/index.tsx +1 -0
  67. package/src/hooks/useBuildNavigationController.tsx +273 -84
  68. package/src/internal/useBuildSideEntityController.tsx +7 -4
  69. package/src/preview/PropertyPreview.tsx +14 -0
  70. package/src/routes/FireCMSRoute.tsx +3 -3
  71. package/src/types/analytics.ts +3 -0
  72. package/src/types/collections.ts +8 -2
  73. package/src/types/customization_controller.tsx +9 -0
  74. package/src/types/entity_actions.tsx +57 -6
  75. package/src/types/firecms.tsx +9 -0
  76. package/src/types/navigation.ts +28 -6
  77. package/src/types/plugins.tsx +24 -1
  78. package/src/types/properties.ts +8 -0
  79. package/src/types/property_config.tsx +1 -0
  80. package/src/types/side_entity_controller.tsx +5 -0
  81. package/src/util/icons.tsx +22 -7
  82. package/src/util/join_collections.ts +3 -1
  83. package/src/util/navigation_from_path.ts +15 -5
  84. package/src/util/navigation_utils.ts +2 -2
  85. package/src/util/resolutions.ts +13 -1
@@ -1,5 +1,13 @@
1
1
  import React, { useEffect, useMemo, useState } from "react";
2
- import { Entity, EntityCollection, EntityStatus, FireCMSPlugin, FormContext, User } from "../types";
2
+ import {
3
+ Entity,
4
+ EntityCollection,
5
+ EntityStatus,
6
+ FireCMSPlugin,
7
+ FormContext,
8
+ PluginFormActionProps,
9
+ User
10
+ } from "../types";
3
11
 
4
12
  import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary } from "../components";
5
13
  import {
@@ -41,9 +49,13 @@ export type OnTabChangeParams<M extends Record<string, any>> = {
41
49
  entityId?: string;
42
50
  selectedTab?: string;
43
51
  collection: EntityCollection<M>;
52
+
44
53
  };
45
54
 
46
55
  export interface EntityEditViewProps<M extends Record<string, any>> {
56
+ /**
57
+ * The database path of the entity, e.g. "users" or "products".
58
+ */
47
59
  path: string;
48
60
  /**
49
61
  * The navigation path to the entity.
@@ -158,12 +170,33 @@ export function EntityEditViewInner<M extends Record<string, any>>({
158
170
  setUsedEntity(entity);
159
171
  }, [entity]);
160
172
 
161
- // Instead of using a ref (which does not trigger re-render), we use state for the form context.
162
173
  const [formContext, setFormContext] = useState<FormContext<M> | undefined>(undefined);
163
174
 
164
175
  const largeLayout = useLargeLayout();
165
176
 
166
177
  const customizationController = useCustomizationController();
178
+ const plugins = customizationController.plugins;
179
+ const pluginActionsTop: React.ReactNode[] = [];
180
+
181
+ if (plugins && collection) {
182
+ const actionProps: PluginFormActionProps = {
183
+ entityId,
184
+ parentCollectionIds,
185
+ path,
186
+ status,
187
+ collection,
188
+ context,
189
+ formContext,
190
+ openEntityMode: layout,
191
+ disabled: false
192
+ };
193
+ pluginActionsTop.push(...plugins.map((plugin) => (
194
+ plugin.form?.ActionsTop
195
+ ? <plugin.form.ActionsTop
196
+ key={`actions_${plugin.key}`} {...actionProps} />
197
+ : null
198
+ )).filter(Boolean));
199
+ }
167
200
 
168
201
  const defaultSelectedView = useMemo(() => resolveDefaultSelectedView(
169
202
  collection ? collection.defaultSelectedView : undefined,
@@ -187,8 +220,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
187
220
  const includeJsonView = collection.includeJsonView === undefined ? true : collection.includeJsonView;
188
221
  const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
189
222
 
190
- const plugins = customizationController.plugins;
191
-
192
223
  const {
193
224
  resolvedEntityViews,
194
225
  selectedEntityView,
@@ -279,7 +310,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
279
310
 
280
311
  const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
281
312
  const subcollectionId = subcollection.id ?? subcollection.path;
282
- const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
313
+ const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection.path)}` : undefined;
283
314
  const newFullIdPath = fullIdPath ? `${fullIdPath}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
284
315
 
285
316
  if (selectedTab !== subcollectionId) return null;
@@ -342,6 +373,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
342
373
  </div> : null;
343
374
 
344
375
  const entityView = <EntityForm<M>
376
+ fullIdPath={fullIdPath}
345
377
  collection={collection}
346
378
  path={path}
347
379
  entityId={entityId ?? usedEntity?.id}
@@ -411,17 +443,20 @@ export function EntityEditViewInner<M extends Record<string, any>>({
411
443
  let result = <div className="relative flex flex-col h-full w-full bg-white dark:bg-surface-900">
412
444
 
413
445
  {shouldShowTopBar && <div
414
- className={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", defaultBorderMixin)}>
446
+ 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)}>
415
447
 
416
448
  {barActions}
417
449
 
418
450
  <div className={"flex-grow"}/>
419
451
 
452
+ {pluginActionsTop}
453
+
420
454
  {globalLoading && <div className="self-center">
421
455
  <CircularProgress size={"small"}/>
422
456
  </div>}
423
457
 
424
458
  {hasAdditionalViews && <Tabs
459
+ className={"self-end"}
425
460
  value={selectedTab}
426
461
  onValueChange={(value) => {
427
462
  onSideTabClick(value);
@@ -1,10 +1,34 @@
1
1
  import React, { useMemo } from "react";
2
- import { Entity, EntityAction, FireCMSContext, ResolvedEntityCollection, SideEntityController } from "../types";
2
+ import {
3
+ Entity,
4
+ EntityAction,
5
+ EntityActionClickProps,
6
+ FireCMSContext,
7
+ FormContext,
8
+ ResolvedEntityCollection,
9
+ SideEntityController
10
+ } from "../types";
3
11
 
4
12
  import { copyEntityAction, deleteEntityAction } from "../components";
5
- import { canCreateEntity, canDeleteEntity, mergeEntityActions } from "../util";
6
- import { Button, cls, defaultBorderMixin, DialogActions, IconButton, LoadingButton, Typography } from "@firecms/ui";
7
- import { useAuthController, useFireCMSContext, useSideEntityController } from "../hooks";
13
+ import { canCreateEntity, canDeleteEntity, mergeEntityActions, resolveEntityAction } from "../util";
14
+ import {
15
+ Button,
16
+ CircularProgress,
17
+ cls,
18
+ defaultBorderMixin,
19
+ DialogActions,
20
+ IconButton,
21
+ LoadingButton,
22
+ Tooltip,
23
+ Typography
24
+ } from "@firecms/ui";
25
+ import {
26
+ useAuthController,
27
+ useCustomizationController,
28
+ useFireCMSContext,
29
+ useSideEntityController,
30
+ useSnackbarController
31
+ } from "../hooks";
8
32
  import { EntityFormActionsProps } from "../form/EntityFormActions";
9
33
  import { SideDialogController, useSideDialogContext } from "./SideDialogs";
10
34
 
@@ -19,16 +43,21 @@ export function EntityEditViewFormActions({
19
43
  status,
20
44
  pluginActions,
21
45
  openEntityMode,
22
- showDefaultActions = true
46
+ showDefaultActions = true,
47
+ navigateBack,
48
+ formContext
23
49
  }: EntityFormActionsProps) {
24
50
 
25
51
  const authController = useAuthController();
26
52
  const context = useFireCMSContext();
27
53
  const sideEntityController = useSideEntityController();
28
54
  const sideDialogContext = useSideDialogContext();
55
+ const customizationController = useCustomizationController();
29
56
 
30
57
  const entityActions = useMemo((): EntityAction[] => {
31
- const customEntityActions = collection.entityActions
58
+ const customEntityActions = (collection.entityActions ?? [])
59
+ .map(action => resolveEntityAction(action, customizationController.entityActions))
60
+ .filter(Boolean) as EntityAction[];
32
61
  const createEnabled = canCreateEntity(collection, authController, path, null);
33
62
  const deleteEnabled = entity ? canDeleteEntity(collection, authController, path, entity) : false;
34
63
  const actions: EntityAction[] = [];
@@ -39,7 +68,7 @@ export function EntityEditViewFormActions({
39
68
  if (customEntityActions)
40
69
  return mergeEntityActions(actions, customEntityActions);
41
70
  return actions;
42
- }, [authController, collection, path]);
71
+ }, [authController, collection, path, customizationController.entityActions?.length]);
43
72
 
44
73
  const formActions = showDefaultActions ? entityActions.filter(a => a.includeInForm === undefined || a.includeInForm) : [];
45
74
 
@@ -57,6 +86,8 @@ export function EntityEditViewFormActions({
57
86
  sideDialogContext,
58
87
  pluginActions,
59
88
  openEntityMode,
89
+ navigateBack,
90
+ formContext
60
91
  })
61
92
  : buildSideActions({
62
93
  savingError,
@@ -71,6 +102,8 @@ export function EntityEditViewFormActions({
71
102
  status,
72
103
  pluginActions,
73
104
  openEntityMode,
105
+ navigateBack,
106
+ formContext
74
107
  });
75
108
  }
76
109
 
@@ -87,6 +120,8 @@ type ActionsViewProps<M extends object> = {
87
120
  sideDialogContext: SideDialogController,
88
121
  pluginActions?: React.ReactNode[],
89
122
  openEntityMode: "side_panel" | "full_screen";
123
+ navigateBack: () => void;
124
+ formContext: FormContext
90
125
  };
91
126
 
92
127
  function buildBottomActions<M extends object>({
@@ -102,37 +137,48 @@ function buildBottomActions<M extends object>({
102
137
  sideDialogContext,
103
138
  pluginActions,
104
139
  openEntityMode,
140
+ navigateBack,
141
+ formContext
105
142
  }: ActionsViewProps<M>) {
106
143
 
107
144
  const canClose = openEntityMode === "side_panel";
108
- return <DialogActions position={"absolute"}>
145
+ return <DialogActions
146
+ position={"absolute"}>
109
147
  {savingError &&
110
148
  <div className="text-right">
111
149
  <Typography color={"error"}>{savingError.message}</Typography>
112
150
  </div>
113
151
  }
114
- {entity && formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
115
- {formActions.map(action => (
116
- <IconButton
117
- key={action.name}
118
- color="primary"
119
- onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
120
- event.stopPropagation();
121
- if (entity)
122
- action.onClick({
123
- entity,
124
- fullPath: collection.path,
125
- collection: collection,
126
- context,
127
- sideEntityController,
128
- openEntityMode: openEntityMode
129
- });
130
- }}>
131
- {action.icon}
132
- </IconButton>
133
- ))}
152
+
153
+ {formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
154
+ {formActions.map(action => {
155
+
156
+ const props = {
157
+ view: "form",
158
+ entity,
159
+ fullPath: collection.path,
160
+ collection: collection,
161
+ context,
162
+ sideEntityController,
163
+ openEntityMode,
164
+ navigateBack,
165
+ formContext
166
+ } satisfies EntityActionClickProps<any>;
167
+
168
+ const isEnabled = !action.isEnabled || action.isEnabled(props);
169
+ return (
170
+ <EntityActionButton
171
+ key={action.key}
172
+ action={action}
173
+ enabled={isEnabled}
174
+ props={props}
175
+ />
176
+ );
177
+ })}
134
178
  </div>}
179
+
135
180
  {pluginActions}
181
+
136
182
  <Button variant="text" disabled={disabled || isSubmitting} type="reset">
137
183
  {status === "existing" ? "Discard" : "Clear"}
138
184
  </Button>
@@ -171,7 +217,10 @@ function buildSideActions<M extends object>({
171
217
  disabled,
172
218
  status,
173
219
  sideDialogContext,
174
- pluginActions
220
+ pluginActions,
221
+ openEntityMode,
222
+ navigateBack,
223
+ formContext
175
224
  }: ActionsViewProps<M>) {
176
225
 
177
226
  return <div
@@ -184,12 +233,33 @@ function buildSideActions<M extends object>({
184
233
  {status === "copy" && "Create copy"}
185
234
  {status === "new" && "Create"}
186
235
  </LoadingButton>
236
+
187
237
  <Button fullWidth={true} variant="text" disabled={disabled || isSubmitting} type="reset">
188
238
  {status === "existing" ? "Discard" : "Clear"}
189
239
  </Button>
190
240
 
191
241
  {pluginActions}
192
242
 
243
+ {formActions.length > 0 && <div className="flex flex-row flex-wrap mt-2">
244
+ {formActions.map(action => {
245
+ const props = {
246
+ view: "form",
247
+ entity,
248
+ fullPath: collection.path,
249
+ collection: collection,
250
+ context,
251
+ sideEntityController,
252
+ openEntityMode,
253
+ navigateBack,
254
+ formContext
255
+ } satisfies EntityActionClickProps<any>;
256
+ const isEnabled = !action.isEnabled || action.isEnabled(props);
257
+ return (
258
+ <EntityActionButton key={action.key} action={action} enabled={isEnabled} props={props}/>
259
+ );
260
+ })}
261
+ </div>}
262
+
193
263
  {savingError &&
194
264
  <div className="text-right">
195
265
  <Typography color={"error"}>{savingError.message}</Typography>
@@ -197,3 +267,58 @@ function buildSideActions<M extends object>({
197
267
  }
198
268
  </div>;
199
269
  }
270
+
271
+ function EntityActionButton({
272
+ action,
273
+ enabled,
274
+ props
275
+ }: {
276
+ action: EntityAction,
277
+ enabled: boolean,
278
+ props: EntityActionClickProps<any, any>
279
+ }) {
280
+ const snackbarController = useSnackbarController();
281
+ const [loading, setLoading] = React.useState(false);
282
+ return <Tooltip
283
+ title={action.name}>
284
+ <IconButton
285
+ color="primary"
286
+ disabled={!enabled}
287
+ onClick={(event) => {
288
+ console.debug("Executing action", action.key, props);
289
+ try {
290
+ event.stopPropagation();
291
+ if (props.entity) {
292
+ const onClick = action.onClick(props);
293
+ // If the action returns a promise, we can handle it
294
+ if (onClick instanceof Promise) {
295
+ setLoading(true);
296
+ onClick
297
+ .catch((error) => {
298
+ console.error("Error executing action", action.key, error);
299
+ snackbarController.open({
300
+ message: `Error executing action: ${error.message}`,
301
+ type: "error"
302
+ })
303
+ })
304
+ .finally(() => setLoading(false));
305
+ } else {
306
+ snackbarController.open({
307
+ message: `Action ${action.name} executed successfully`,
308
+ type: "success"
309
+ });
310
+ }
311
+
312
+ }
313
+ } catch (e: any) {
314
+ console.error("Error executing action", action.key, e);
315
+ snackbarController.open({
316
+ message: `Error executing action: ${e.message}`,
317
+ type: "error"
318
+ });
319
+ }
320
+ }}>
321
+ {loading ? <CircularProgress size={"smallest"}/> : action.icon}
322
+ </IconButton>
323
+ </Tooltip>;
324
+ }
@@ -23,6 +23,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
23
23
  allowFullScreen = true,
24
24
  path,
25
25
  entityId,
26
+ fullIdPath,
26
27
  formProps,
27
28
  } = props;
28
29
 
@@ -57,6 +58,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
57
58
  sideEntityController.replace({
58
59
  path: params.path,
59
60
  entityId: params.entityId,
61
+ fullIdPath: props.fullIdPath,
60
62
  selectedTab: params.selectedTab,
61
63
  updateUrl: true,
62
64
  collection: params.collection,
@@ -74,7 +76,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
74
76
  return navigationController.getParentCollectionIds(path);
75
77
  }, [navigationController, path]);
76
78
 
77
- const collection = props.collection ?? navigationController.getCollection(path);
79
+ const collection = navigationController.getCollection(fullIdPath ?? path) ?? props.collection;
78
80
 
79
81
  useEffect(() => {
80
82
  function beforeunload(e: any) {
@@ -110,6 +112,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
110
112
  <ErrorBoundary>
111
113
  <EntityEditView
112
114
  {...props}
115
+ fullIdPath={fullIdPath}
113
116
  layout={"side_panel"}
114
117
  collection={collection}
115
118
  parentCollectionIds={parentCollectionIds}
@@ -133,7 +136,6 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
133
136
  </IconButton>}
134
137
  </>}
135
138
  onTabChange={({
136
- path,
137
139
  entityId,
138
140
  selectedTab,
139
141
  collection,
@@ -141,6 +143,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
141
143
  sideEntityController.replace({
142
144
  path,
143
145
  entityId,
146
+ fullIdPath: props.fullIdPath,
144
147
  selectedTab,
145
148
  updateUrl: true,
146
149
  collection,
@@ -48,6 +48,7 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
48
48
  onAnalyticsEvent,
49
49
  propertyConfigs,
50
50
  entityViews,
51
+ entityActions,
51
52
  components,
52
53
  navigationController,
53
54
  apiKey
@@ -72,6 +73,7 @@ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
72
73
  entityLinkBuilder,
73
74
  plugins,
74
75
  entityViews: entityViews ?? [],
76
+ entityActions: entityActions ?? [],
75
77
  propertyConfigs: propertyConfigs ?? {},
76
78
  components
77
79
  };
@@ -10,6 +10,7 @@ import {
10
10
  MapFieldBinding,
11
11
  MarkdownEditorFieldBinding,
12
12
  MultiSelectFieldBinding,
13
+ ReferenceAsStringFieldBinding,
13
14
  ReferenceFieldBinding,
14
15
  RepeatFieldBinding,
15
16
  SelectFieldBinding,
@@ -211,10 +212,21 @@ export const DEFAULT_FIELD_CONFIGS: Record<string, PropertyConfig<any>> = {
211
212
  Field: StorageUploadFieldBinding
212
213
  }
213
214
  },
215
+ reference_as_string: {
216
+ key: "reference_as_string",
217
+ name: "Reference (as string)",
218
+ description: "The value refers to a different collection (it is saved as a string)",
219
+ Icon: LinkIcon,
220
+ color: "#154fb3",
221
+ property: {
222
+ dataType: "string",
223
+ Field: ReferenceAsStringFieldBinding
224
+ }
225
+ },
214
226
  reference: {
215
227
  key: "reference",
216
228
  name: "Reference",
217
- description: "The value refers to a different collection",
229
+ description: "The value refers to a different collection (it is saved as a reference)",
218
230
  Icon: LinkIcon,
219
231
  color: "#ff0042",
220
232
  property: {
@@ -348,6 +360,8 @@ export function getDefaultFieldId(property: Property | ResolvedProperty) {
348
360
  return "email";
349
361
  } else if (property.enumValues) {
350
362
  return "select";
363
+ } else if (property.reference) {
364
+ return "reference_as_string";
351
365
  } else {
352
366
  return "text_field";
353
367
  }
@@ -32,7 +32,8 @@ import {
32
32
  useAuthController,
33
33
  useCustomizationController,
34
34
  useDataSource,
35
- useFireCMSContext,
35
+ useFireCMSContext, useNavigationController,
36
+ useSideEntityController,
36
37
  useSnackbarController
37
38
  } from "../hooks";
38
39
  import { Alert, CheckIcon, Chip, cls, EditIcon, NotesIcon, paperMixin, Tooltip, Typography } from "@firecms/ui";
@@ -57,6 +58,7 @@ export type OnUpdateParams = {
57
58
 
58
59
  export type EntityFormProps<M extends Record<string, any>> = {
59
60
  path: string;
61
+ fullIdPath?: string;
60
62
  collection: EntityCollection<M>;
61
63
  entityId?: string;
62
64
  entity?: Entity<M>;
@@ -96,6 +98,7 @@ export type EntityFormProps<M extends Record<string, any>> = {
96
98
 
97
99
  export function EntityForm<M extends Record<string, any>>({
98
100
  path,
101
+ fullIdPath,
99
102
  entityId: entityIdProp,
100
103
  collection,
101
104
  onValuesModified,
@@ -123,6 +126,19 @@ export function EntityForm<M extends Record<string, any>>({
123
126
  console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
124
127
  }
125
128
 
129
+
130
+ const sideEntityController = useSideEntityController();
131
+ const navigationController = useNavigationController();
132
+
133
+ const navigateBack = useCallback(() => {
134
+ if (openEntityMode === "side_panel") {
135
+ // If we are in side panel mode, we close the side panel
136
+ sideEntityController.close();
137
+ } else {
138
+ window.history.back();
139
+ }
140
+ }, []);
141
+
126
142
  const authController = useAuthController();
127
143
  const [status, setStatus] = useState<EntityStatus>(initialStatus);
128
144
 
@@ -433,14 +449,16 @@ export function EntityForm<M extends Record<string, any>>({
433
449
  const plugins = customizationController.plugins;
434
450
 
435
451
  const actionsDisabled = disabled || formex.isSubmitting || (status === "existing" && !formex.dirty) || Boolean(disabledProp);
452
+ const parentCollectionIds = navigationController.getParentCollectionIds(path);
453
+
436
454
  if (plugins && collection) {
437
455
  const actionProps: PluginFormActionProps = {
438
456
  entityId,
457
+ parentCollectionIds,
439
458
  path,
440
459
  status,
441
- collection: collection,
460
+ collection,
442
461
  context,
443
- currentEntityId: entityId,
444
462
  formContext,
445
463
  openEntityMode,
446
464
  disabled: actionsDisabled,
@@ -625,7 +643,13 @@ export function EntityForm<M extends Record<string, any>>({
625
643
  variant={"h4"}>
626
644
  {title ?? collection.singularName ?? collection.name}
627
645
  </Typography>
628
- {showEntityPath && <Alert color={"base"} className={"w-full"} size={"small"}>
646
+
647
+ {!entity?.values && initialStatus === "existing" &&
648
+ <Alert color={"warning"} size={"small"} outerClassName={"w-full mb-4 text-xs"}>
649
+ This entity does not exist in the database
650
+ </Alert>}
651
+
652
+ {showEntityPath && <Alert color={"base"} outerClassName={"w-full"} size={"small"}>
629
653
  <code
630
654
  className={"text-xs select-all text-text-secondary dark:text-text-secondary-dark"}>
631
655
  {entity?.path ?? path}/{entityId}
@@ -635,6 +659,10 @@ export function EntityForm<M extends Record<string, any>>({
635
659
 
636
660
  {children}
637
661
 
662
+ {initialEntityId && !entity && initialStatus !== "new" && <Alert color={"info"} size={"small"}>
663
+ This entity does not exist in the database
664
+ </Alert>}
665
+
638
666
  {!Builder && !collection.hideIdFromForm &&
639
667
  <CustomIdField customId={collection.customId}
640
668
  entityId={entityId}
@@ -668,6 +696,8 @@ export function EntityForm<M extends Record<string, any>>({
668
696
  const dialogActions = <EntityFormActionsComponent
669
697
  collection={resolvedCollection}
670
698
  path={path}
699
+ fullPath={path}
700
+ fullIdPath={fullIdPath}
671
701
  entity={entity}
672
702
  layout={forceActionsAtTheBottom ? "bottom" : "side"}
673
703
  savingError={savingError}
@@ -677,6 +707,8 @@ export function EntityForm<M extends Record<string, any>>({
677
707
  pluginActions={pluginActions ?? []}
678
708
  openEntityMode={openEntityMode}
679
709
  showDefaultActions={showDefaultActions}
710
+ navigateBack={navigateBack}
711
+ formContext={formContext}
680
712
  />;
681
713
 
682
714
  return (