@firecms/core 3.0.0-canary.9 → 3.0.0-canary.91

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 (247) hide show
  1. package/README.md +1 -1
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +17 -0
  4. package/dist/app/Scaffold.d.ts +30 -0
  5. package/dist/app/index.d.ts +4 -0
  6. package/dist/app/useApp.d.ts +16 -0
  7. package/dist/components/CircularProgressCenter.d.ts +1 -1
  8. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  9. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -12
  10. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  12. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  13. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  14. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  15. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  16. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  17. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  18. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -2
  19. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  20. package/dist/components/EntityPreview.d.ts +5 -4
  21. package/dist/components/ErrorView.d.ts +1 -1
  22. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  23. package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
  24. package/dist/components/ReferenceWidget.d.ts +3 -1
  25. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  26. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  27. package/dist/components/VirtualTable/VirtualTableProps.d.ts +15 -12
  28. package/dist/components/VirtualTable/types.d.ts +3 -3
  29. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  30. package/dist/components/common/index.d.ts +1 -0
  31. package/dist/components/common/table_height.d.ts +5 -0
  32. package/dist/components/common/types.d.ts +4 -6
  33. package/dist/components/common/useColumnsIds.d.ts +2 -1
  34. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  35. package/dist/components/index.d.ts +2 -1
  36. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  37. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +6 -9
  38. package/dist/core/DefaultDrawer.d.ts +19 -0
  39. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  40. package/dist/core/EntityEditView.d.ts +17 -3
  41. package/dist/core/FireCMS.d.ts +1 -1
  42. package/dist/core/NavigationRoutes.d.ts +3 -3
  43. package/dist/core/index.d.ts +3 -4
  44. package/dist/form/PropertiesForm.d.ts +8 -0
  45. package/dist/form/components/ErrorFocus.d.ts +1 -1
  46. package/dist/form/components/FieldHelperText.d.ts +3 -3
  47. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  48. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  49. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +2 -4
  50. package/dist/form/index.d.ts +0 -2
  51. package/dist/hooks/data/delete.d.ts +2 -2
  52. package/dist/hooks/data/save.d.ts +2 -3
  53. package/dist/hooks/data/useDataSource.d.ts +1 -1
  54. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  55. package/dist/hooks/index.d.ts +2 -0
  56. package/dist/hooks/useBuildNavigationController.d.ts +1 -2
  57. package/dist/hooks/useProjectLog.d.ts +2 -2
  58. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.es.js +15825 -12165
  61. package/dist/index.es.js.map +1 -1
  62. package/dist/index.umd.js +19684 -7
  63. package/dist/index.umd.js.map +1 -1
  64. package/dist/internal/useBuildDataSource.d.ts +1 -16
  65. package/dist/internal/useRestoreScroll.d.ts +1 -1
  66. package/dist/preview/PropertyPreviewProps.d.ts +6 -4
  67. package/dist/preview/components/ReferencePreview.d.ts +2 -1
  68. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  69. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  70. package/dist/types/auth.d.ts +26 -2
  71. package/dist/types/collections.d.ts +31 -7
  72. package/dist/types/datasource.d.ts +34 -20
  73. package/dist/types/entities.d.ts +5 -1
  74. package/dist/types/entity_actions.d.ts +14 -0
  75. package/dist/types/entity_callbacks.d.ts +2 -2
  76. package/dist/types/fields.d.ts +31 -30
  77. package/dist/types/index.d.ts +0 -1
  78. package/dist/types/navigation.d.ts +5 -5
  79. package/dist/types/plugins.d.ts +16 -6
  80. package/dist/types/properties.d.ts +17 -4
  81. package/dist/types/storage.d.ts +11 -3
  82. package/dist/util/collections.d.ts +1 -1
  83. package/dist/util/entities.d.ts +1 -1
  84. package/dist/util/icon_synonyms.d.ts +1 -97
  85. package/dist/util/icons.d.ts +2 -2
  86. package/dist/util/navigation_utils.d.ts +2 -2
  87. package/dist/util/objects.d.ts +1 -1
  88. package/dist/util/plurals.d.ts +0 -2
  89. package/dist/util/resolutions.d.ts +13 -13
  90. package/dist/util/storage.d.ts +23 -2
  91. package/dist/util/useStorageUploadController.d.ts +1 -1
  92. package/dist/util/useTraceUpdate.d.ts +1 -0
  93. package/package.json +130 -119
  94. package/src/app/AppBar.tsx +18 -0
  95. package/src/app/Drawer.tsx +25 -0
  96. package/src/app/Scaffold.tsx +249 -0
  97. package/src/app/index.ts +4 -0
  98. package/src/app/useApp.tsx +32 -0
  99. package/src/components/CircularProgressCenter.tsx +1 -1
  100. package/src/components/ClearFilterSortButton.tsx +41 -0
  101. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +9 -18
  102. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +21 -20
  103. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
  104. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +38 -34
  105. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  106. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +11 -2
  107. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +14 -6
  108. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  109. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  110. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -5
  111. package/src/components/EntityCollectionView/EntityCollectionView.tsx +69 -45
  112. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  113. package/src/components/EntityCollectionView/useSelectionController.tsx +19 -7
  114. package/src/components/EntityPreview.tsx +15 -9
  115. package/src/components/EntityView.tsx +5 -5
  116. package/src/components/ErrorView.tsx +1 -1
  117. package/src/components/HomePage/DefaultHomePage.tsx +3 -3
  118. package/src/components/HomePage/NavigationCard.tsx +3 -3
  119. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  120. package/src/components/HomePage/SmallNavigationCard.tsx +5 -5
  121. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  122. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +1 -0
  123. package/src/components/ReferenceWidget.tsx +22 -12
  124. package/src/components/SearchIconsView.tsx +5 -5
  125. package/src/components/SelectableTable/SelectableTable.tsx +7 -7
  126. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  127. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  128. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
  129. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  130. package/src/components/VirtualTable/VirtualTable.tsx +70 -37
  131. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -1
  132. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  133. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  134. package/src/components/VirtualTable/VirtualTableProps.tsx +18 -14
  135. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  136. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  137. package/src/components/VirtualTable/types.tsx +2 -3
  138. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
  139. package/src/components/common/index.ts +1 -0
  140. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  141. package/src/components/common/types.tsx +4 -6
  142. package/src/components/common/useColumnsIds.tsx +23 -2
  143. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
  144. package/src/components/common/useTableSearchHelper.ts +52 -12
  145. package/src/components/index.tsx +2 -1
  146. package/src/contexts/AuthControllerContext.tsx +1 -1
  147. package/src/contexts/DialogsProvider.tsx +2 -2
  148. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +52 -37
  149. package/src/core/DefaultDrawer.tsx +177 -0
  150. package/src/core/DrawerNavigationItem.tsx +62 -0
  151. package/src/core/EntityEditView.tsx +711 -139
  152. package/src/core/EntitySidePanel.tsx +1 -2
  153. package/src/core/FireCMS.tsx +39 -44
  154. package/src/core/NavigationRoutes.tsx +7 -8
  155. package/src/core/field_configs.tsx +2 -3
  156. package/src/core/index.tsx +3 -4
  157. package/src/form/PropertiesForm.tsx +81 -0
  158. package/src/form/PropertyFieldBinding.tsx +29 -7
  159. package/src/form/components/FieldHelperText.tsx +3 -3
  160. package/src/form/components/StorageItemPreview.tsx +20 -11
  161. package/src/form/components/StorageUploadProgress.tsx +3 -3
  162. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
  163. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +8 -5
  164. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  165. package/src/form/field_bindings/KeyValueFieldBinding.tsx +44 -39
  166. package/src/form/field_bindings/MapFieldBinding.tsx +11 -3
  167. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  168. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -9
  169. package/src/form/field_bindings/ReferenceFieldBinding.tsx +15 -13
  170. package/src/form/field_bindings/RepeatFieldBinding.tsx +10 -7
  171. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  172. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +22 -43
  173. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -1
  174. package/src/form/index.tsx +4 -4
  175. package/src/form/validation.ts +1 -17
  176. package/src/hooks/data/delete.ts +3 -3
  177. package/src/hooks/data/save.ts +4 -2
  178. package/src/hooks/data/useDataSource.tsx +2 -2
  179. package/src/hooks/data/useEntityFetch.tsx +3 -3
  180. package/src/hooks/index.tsx +3 -0
  181. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  182. package/src/hooks/useBuildModeController.tsx +11 -5
  183. package/src/hooks/useBuildNavigationController.tsx +137 -61
  184. package/src/hooks/useProjectLog.tsx +21 -8
  185. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  186. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  187. package/src/index.ts +1 -0
  188. package/src/internal/useBuildDataSource.ts +56 -49
  189. package/src/internal/useBuildSideEntityController.tsx +88 -21
  190. package/src/preview/PropertyPreview.tsx +9 -16
  191. package/src/preview/PropertyPreviewProps.tsx +4 -8
  192. package/src/preview/components/BooleanPreview.tsx +4 -2
  193. package/src/preview/components/EnumValuesChip.tsx +1 -1
  194. package/src/preview/components/ImagePreview.tsx +21 -33
  195. package/src/preview/components/ReferencePreview.tsx +23 -23
  196. package/src/preview/components/StorageThumbnail.tsx +5 -1
  197. package/src/preview/components/UrlComponentPreview.tsx +44 -11
  198. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  199. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  200. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  201. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  202. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  203. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  204. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  205. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  206. package/src/types/auth.tsx +35 -2
  207. package/src/types/collections.ts +37 -8
  208. package/src/types/customization_controller.tsx +0 -1
  209. package/src/types/datasource.ts +41 -24
  210. package/src/types/entities.ts +9 -1
  211. package/src/types/entity_actions.tsx +16 -3
  212. package/src/types/entity_callbacks.ts +2 -2
  213. package/src/types/fields.tsx +33 -33
  214. package/src/types/index.ts +0 -1
  215. package/src/types/navigation.ts +6 -7
  216. package/src/types/plugins.tsx +18 -8
  217. package/src/types/properties.ts +22 -6
  218. package/src/types/storage.ts +12 -3
  219. package/src/util/collections.ts +1 -1
  220. package/src/util/entities.ts +5 -4
  221. package/src/util/enums.ts +1 -1
  222. package/src/util/icon_list.ts +2 -2
  223. package/src/util/icon_synonyms.ts +3 -99
  224. package/src/util/navigation_utils.ts +6 -6
  225. package/src/util/objects.ts +25 -28
  226. package/src/util/permissions.ts +1 -0
  227. package/src/util/plurals.ts +0 -2
  228. package/src/util/resolutions.ts +32 -31
  229. package/src/util/storage.ts +75 -21
  230. package/src/util/strings.ts +2 -2
  231. package/src/util/useStorageUploadController.tsx +21 -3
  232. package/src/util/useTraceUpdate.tsx +2 -1
  233. package/dist/components/VirtualTable/common.d.ts +0 -2
  234. package/dist/core/Drawer.d.ts +0 -23
  235. package/dist/core/Scaffold.d.ts +0 -55
  236. package/dist/core/SideEntityView.d.ts +0 -7
  237. package/dist/form/EntityForm.d.ts +0 -77
  238. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  239. package/dist/internal/useLocaleConfig.d.ts +0 -1
  240. package/dist/types/appcheck.d.ts +0 -26
  241. package/src/core/Drawer.tsx +0 -191
  242. package/src/core/Scaffold.tsx +0 -281
  243. package/src/core/SideEntityView.tsx +0 -38
  244. package/src/form/EntityForm.tsx +0 -728
  245. package/src/internal/useBuildCustomizationController.tsx +0 -5
  246. package/src/internal/useLocaleConfig.tsx +0 -18
  247. package/src/types/appcheck.ts +0 -29
@@ -1,18 +1,41 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import {
3
+ CMSAnalyticsEvent,
3
4
  Entity,
5
+ EntityAction,
4
6
  EntityCollection,
5
7
  EntityCustomView,
6
8
  EntityStatus,
7
9
  EntityValues,
8
10
  FireCMSPlugin,
9
11
  FormContext,
12
+ PluginFormActionProps,
13
+ PropertyFieldBindingProps,
14
+ ResolvedEntityCollection,
10
15
  User
11
16
  } from "../types";
12
- import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary, } from "../components";
17
+ import equal from "react-fast-compare"
18
+
19
+ import {
20
+ CircularProgressCenter,
21
+ copyEntityAction,
22
+ deleteEntityAction,
23
+ EntityCollectionView,
24
+ EntityView,
25
+ ErrorBoundary,
26
+ getFormFieldKeys,
27
+ } from "../components";
13
28
  import {
29
+ canCreateEntity,
30
+ canDeleteEntity,
14
31
  canEditEntity,
32
+ getDefaultValuesFor,
33
+ getEntityTitlePropertyKey,
34
+ getValueInPath,
35
+ isHidden,
36
+ isReadOnly,
15
37
  removeInitialAndTrailingSlashes,
38
+ resolveCollection,
16
39
  resolveDefaultSelectedView,
17
40
  resolveEntityView,
18
41
  useDebouncedCallback
@@ -28,11 +51,31 @@ import {
28
51
  useSideEntityController,
29
52
  useSnackbarController
30
53
  } from "../hooks";
31
- import { EntityForm } from "../form";
32
- import { CircularProgress, CloseIcon, cn, defaultBorderMixin, IconButton, Tab, Tabs, Typography } from "@firecms/ui";
33
- import { EntityFormSaveParams } from "../form/EntityForm";
34
- import { FORM_CONTAINER_WIDTH } from "../internal/common";
54
+ import {
55
+ Alert,
56
+ Button,
57
+ CircularProgress,
58
+ CloseIcon,
59
+ cls,
60
+ defaultBorderMixin,
61
+ DialogActions,
62
+ IconButton,
63
+ NotesIcon,
64
+ paperMixin,
65
+ Tab,
66
+ Tabs,
67
+ Tooltip,
68
+ Typography
69
+ } from "@firecms/ui";
35
70
  import { useSideDialogContext } from "./index";
71
+ import { Formex, FormexController, getIn, setIn, useCreateFormex } from "@firecms/formex";
72
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
73
+ import { CustomIdField } from "../form/components/CustomIdField";
74
+ import { CustomFieldValidator, getYupEntitySchema } from "../form/validation";
75
+ import { ErrorFocus } from "../form/components/ErrorFocus";
76
+ import { PropertyIdCopyTooltipContent } from "../components/PropertyIdCopyTooltipContent";
77
+ import { LabelWithIcon, PropertyFieldBinding } from "../form";
78
+ import { ValidationError } from "yup";
36
79
 
37
80
  const MAIN_TAB_VALUE = "main_##Q$SC^#S6";
38
81
 
@@ -43,7 +86,6 @@ export interface EntityEditViewProps<M extends Record<string, any>> {
43
86
  copy?: boolean;
44
87
  selectedSubPath?: string;
45
88
  parentCollectionIds: string[];
46
- formWidth?: number | string;
47
89
  onValuesAreModified: (modified: boolean) => void;
48
90
  onUpdate?: (params: { entity: Entity<any> }) => void;
49
91
  onClose?: () => void;
@@ -56,17 +98,51 @@ export interface EntityEditViewProps<M extends Record<string, any>> {
56
98
  * side panel. Instead, you might want to use {@link EntityForm} or {@link EntityCollectionView}
57
99
  */
58
100
  export function EntityEditView<M extends Record<string, any>, UserType extends User>({
59
- path,
60
101
  entityId,
61
- selectedSubPath,
62
- copy,
63
- collection,
64
- parentCollectionIds,
65
- onValuesAreModified,
66
- formWidth,
67
- onUpdate,
68
- onClose,
102
+ ...props
69
103
  }: EntityEditViewProps<M>) {
104
+ const {
105
+ entity,
106
+ dataLoading,
107
+ // eslint-disable-next-line no-unused-vars
108
+ dataLoadingError
109
+ } = useEntityFetch<M, UserType>({
110
+ path: props.path,
111
+ entityId: entityId,
112
+ collection: props.collection,
113
+ useCache: false
114
+ });
115
+
116
+ if (dataLoading) {
117
+ return <CircularProgressCenter/>
118
+ }
119
+
120
+ if (entityId && !entity) {
121
+ console.error(`Entity with id ${entityId} not found in collection ${props.collection.path}`);
122
+ }
123
+
124
+ return <EntityEditViewInner<M> {...props}
125
+ entityId={entityId}
126
+ entity={entity}
127
+ dataLoading={dataLoading}/>;
128
+ }
129
+
130
+ export function EntityEditViewInner<M extends Record<string, any>>({
131
+ path,
132
+ entityId: entityIdProp,
133
+ selectedSubPath: selectedSubPathProp,
134
+ copy,
135
+ collection,
136
+ parentCollectionIds,
137
+ onValuesAreModified,
138
+ onUpdate,
139
+ onClose,
140
+ entity,
141
+ dataLoading,
142
+ }: EntityEditViewProps<M> & {
143
+ entity?: Entity<M>,
144
+ dataLoading: boolean
145
+ }) {
70
146
 
71
147
  if (collection.customId && collection.formAutoSave) {
72
148
  console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
@@ -91,33 +167,63 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
91
167
 
92
168
  // const largeLayout = useLargeLayout();
93
169
  // const largeLayoutTabSelected = useRef(!largeLayout);
170
+ // const resolvedFormWidth: string = typeof formWidth === "number" ? `${formWidth}px` : formWidth ?? FORM_CONTAINER_WIDTH;
94
171
 
95
- const resolvedFormWidth: string = typeof formWidth === "number" ? `${formWidth}px` : formWidth ?? FORM_CONTAINER_WIDTH;
172
+ const inputCollection = collection;
96
173
 
174
+ const authController = useAuthController();
97
175
  const dataSource = useDataSource(collection);
98
176
  const sideDialogContext = useSideDialogContext();
99
177
  const sideEntityController = useSideEntityController();
100
178
  const snackbarController = useSnackbarController();
101
179
  const customizationController = useCustomizationController();
102
180
  const context = useFireCMSContext();
103
- const authController = useAuthController<UserType>();
104
181
 
105
- const [formContext, setFormContext] = useState<FormContext<M> | undefined>(undefined);
182
+ const closeAfterSaveRef = useRef(false);
183
+
184
+ const analyticsController = useAnalyticsController();
185
+
186
+ const initialResolvedCollection = useMemo(() => resolveCollection({
187
+ collection: inputCollection,
188
+ path,
189
+ values: entity?.values,
190
+ fields: customizationController.propertyConfigs
191
+ }), [entity?.values, path, customizationController.propertyConfigs]);
192
+
193
+ const initialStatus = copy ? "copy" : (entityIdProp ? "existing" : "new");
194
+ const [status, setStatus] = useState<EntityStatus>(initialStatus);
195
+ const mustSetCustomId: boolean = (status === "new" || status === "copy") &&
196
+ (Boolean(initialResolvedCollection.customId) && initialResolvedCollection.customId !== "optional");
197
+ const initialEntityId: string | undefined = useMemo((): string | undefined => {
198
+ if (status === "new" || status === "copy") {
199
+ if (mustSetCustomId) {
200
+ return undefined;
201
+ } else {
202
+ return dataSource.generateEntityId(path);
203
+ }
204
+ } else {
205
+ return entityIdProp;
206
+ }
207
+ }, [entityIdProp, status]);
106
208
 
107
- const [status, setStatus] = useState<EntityStatus>(copy ? "copy" : (entityId ? "existing" : "new"));
209
+ const [entityId, setEntityId] = React.useState<string | undefined>(initialEntityId);
108
210
 
109
- const modifiedValuesRef = useRef<EntityValues<M> | undefined>(undefined);
110
- const modifiedValues = modifiedValuesRef.current;
211
+ // const doOnValuesChanges = (values?: EntityValues<M>) => {
212
+ // const initialValues = formex.initialValues;
213
+ // setInternalValues(values);
214
+ // if (onValuesChanged)
215
+ // onValuesChanged(values);
216
+ // if (autoSave && values && !equal(values, initialValues)) {
217
+ // save(values);
218
+ // }
219
+ // };
111
220
 
112
- const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
113
- const subcollectionsCount = subcollections?.length ?? 0;
114
- const customViews = collection.entityViews;
115
- const customViewsCount = customViews?.length ?? 0;
116
- const autoSave = collection.formAutoSave && !collection.customId;
221
+ const [entityIdError, setEntityIdError] = React.useState<boolean>(false);
222
+ const [savingError, setSavingError] = React.useState<Error | undefined>();
117
223
 
118
- const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0;
224
+ const [customIdLoading, setCustomIdLoading] = React.useState<boolean>(false);
119
225
 
120
- const defaultSelectedView = selectedSubPath ?? resolveDefaultSelectedView(
226
+ const defaultSelectedView = selectedSubPathProp ?? resolveDefaultSelectedView(
121
227
  collection ? collection.defaultSelectedView : undefined,
122
228
  {
123
229
  status,
@@ -126,20 +232,23 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
126
232
  );
127
233
 
128
234
  const selectedTabRef = useRef<string>(defaultSelectedView ?? MAIN_TAB_VALUE);
235
+ const baseDataSourceValuesRef = useRef<Partial<EntityValues<M>>>(getDataSourceEntityValues(initialResolvedCollection, status, entity));
129
236
 
130
237
  const mainViewVisible = selectedTabRef.current === MAIN_TAB_VALUE;
131
238
 
132
- const {
133
- entity,
134
- dataLoading,
135
- // eslint-disable-next-line no-unused-vars
136
- dataLoadingError
137
- } = useEntityFetch<M, UserType>({
138
- path,
139
- entityId,
140
- collection,
141
- useCache: false
142
- });
239
+ // const initialValuesRef = useRef<EntityValues<M>>(entity?.values ?? baseDataSourceValues as EntityValues<M>);
240
+ // const [internalValues, setInternalValues] = useState<EntityValues<M> | undefined>(entity?.values ?? baseDataSourceValuesRef.current as EntityValues<M>);
241
+
242
+ // const modifiedValuesRef = useRef<EntityValues<M> | undefined>(undefined);
243
+ // const modifiedValues = modifiedValuesRef.current;
244
+
245
+ const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
246
+ const subcollectionsCount = subcollections?.length ?? 0;
247
+ const customViews = collection.entityViews;
248
+ const customViewsCount = customViews?.length ?? 0;
249
+ const autoSave = collection.formAutoSave && !collection.customId;
250
+
251
+ const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0;
143
252
 
144
253
  const [usedEntity, setUsedEntity] = useState<Entity<M> | undefined>(entity);
145
254
  const [readOnly, setReadOnly] = useState<boolean | undefined>(undefined);
@@ -195,6 +304,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
195
304
  onUpdate({ entity: updatedEntity });
196
305
 
197
306
  if (closeAfterSave) {
307
+ console.log("Closing side dialog")
198
308
  sideDialogContext.setBlocked(false);
199
309
  sideDialogContext.close(true);
200
310
  onClose?.();
@@ -238,7 +348,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
238
348
  closeAfterSave: boolean,
239
349
  }) => {
240
350
  setSaving(true);
241
- saveEntityWithCallbacks({
351
+ return saveEntityWithCallbacks({
242
352
  path,
243
353
  entityId,
244
354
  values,
@@ -269,7 +379,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
269
379
  if (autoSave) {
270
380
  setValuesToBeSaved(values);
271
381
  } else {
272
- saveEntity({
382
+ return saveEntity({
273
383
  collection,
274
384
  path,
275
385
  entityId,
@@ -280,11 +390,115 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
280
390
  }
281
391
  };
282
392
 
393
+ const onSubmit = (values: EntityValues<M>, formexController: FormexController<EntityValues<M>>) => {
394
+
395
+ if (mustSetCustomId && !entityId) {
396
+ console.error("Missing custom Id");
397
+ setEntityIdError(true);
398
+ formexController.setSubmitting(false);
399
+ return;
400
+ }
401
+
402
+ setSavingError(undefined);
403
+ setEntityIdError(false);
404
+
405
+ if (status === "existing") {
406
+ if (!entity?.id) throw Error("Form misconfiguration when saving, no id for existing entity");
407
+ } else if (status === "new" || status === "copy") {
408
+ if (inputCollection.customId) {
409
+ if (inputCollection.customId !== "optional" && !entityId) {
410
+ throw Error("Form misconfiguration when saving, entityId should be set");
411
+ }
412
+ }
413
+ } else {
414
+ throw Error("New FormType added, check EntityForm");
415
+ }
416
+
417
+ return save(values)
418
+ ?.then(_ => {
419
+ formexController.resetForm({
420
+ values,
421
+ submitCount: 0,
422
+ touched: {}
423
+ });
424
+ })
425
+ .finally(() => {
426
+ formexController.setSubmitting(false);
427
+ });
428
+
429
+ };
430
+
431
+ const formex: FormexController<M> = useCreateFormex<M>({
432
+ initialValues: baseDataSourceValuesRef.current as M,
433
+ onSubmit,
434
+ validation: (values) => {
435
+ return validationSchema?.validate(values, { abortEarly: false })
436
+ .then(() => {
437
+ return {};
438
+ })
439
+ .catch((e: any) => {
440
+ const errors: Record<string, string> = {};
441
+ e.inner.forEach((error: any) => {
442
+ errors[error.path] = error.message;
443
+ });
444
+ return yupToFormErrors(e);
445
+ });
446
+ }
447
+ });
448
+
449
+ const resolvedCollection = resolveCollection<M>({
450
+ collection: inputCollection,
451
+ path,
452
+ entityId,
453
+ values: formex.values,
454
+ previousValues: formex.initialValues,
455
+ fields: customizationController.propertyConfigs
456
+ });
457
+
458
+ const lastSavedValues = useRef<EntityValues<M> | undefined>(entity?.values);
459
+
460
+ const save = (values: EntityValues<M>): Promise<void> => {
461
+ lastSavedValues.current = values;
462
+ return onSaveEntityRequest({
463
+ collection: resolvedCollection,
464
+ path,
465
+ entityId,
466
+ values,
467
+ previousValues: entity?.values,
468
+ closeAfterSave: closeAfterSaveRef.current,
469
+ autoSave: autoSave ?? false
470
+ }).then(_ => {
471
+ const eventName: CMSAnalyticsEvent = status === "new"
472
+ ? "new_entity_saved"
473
+ : (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
474
+ analyticsController.onAnalyticsEvent?.(eventName, { path });
475
+ }).catch(e => {
476
+ console.error(e);
477
+ setSavingError(e);
478
+ }).finally(() => {
479
+ closeAfterSaveRef.current = false;
480
+ });
481
+ };
482
+
483
+ const formContext: FormContext<M> = {
484
+ // @ts-ignore
485
+ setFieldValue: useCallback(formex.setFieldValue, []),
486
+ values: formex.values,
487
+ collection: resolvedCollection,
488
+ entityId,
489
+ path,
490
+ save,
491
+ formex
492
+ };
493
+
283
494
  const resolvedEntityViews = customViews ? customViews
284
495
  .map(e => resolveEntityView(e, customizationController.entityViews))
285
496
  .filter(Boolean) as EntityCustomView[]
286
497
  : [];
287
498
 
499
+ const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTabRef.current);
500
+ const shouldShowEntityActions = !autoSave && (selectedTabRef.current === MAIN_TAB_VALUE || selectedEntityView?.includeActions);
501
+
288
502
  const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
289
503
  .map(
290
504
  (customView, colIndex) => {
@@ -298,7 +512,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
298
512
  return null;
299
513
  }
300
514
  return <div
301
- className={cn(defaultBorderMixin,
515
+ className={cls(defaultBorderMixin,
302
516
  "relative flex-grow w-full h-full overflow-auto ")}
303
517
  key={`custom_view_${customView.key}`}
304
518
  role="tabpanel">
@@ -306,7 +520,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
306
520
  {formContext && <Builder
307
521
  collection={collection}
308
522
  entity={usedEntity}
309
- modifiedValues={modifiedValues ?? usedEntity?.values}
523
+ modifiedValues={formex.values ?? usedEntity?.values}
310
524
  formContext={formContext}
311
525
  />}
312
526
  </ErrorBoundary>
@@ -369,11 +583,6 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
369
583
  });
370
584
  };
371
585
 
372
- const onValuesChanged = useCallback((values?: EntityValues<M>) => {
373
- modifiedValuesRef.current = values;
374
- }, []);
375
-
376
- // eslint-disable-next-line n/handle-callback-err
377
586
  const onIdUpdateError = useCallback((error: any) => {
378
587
  snackbarController.open({
379
588
  type: "error",
@@ -390,28 +599,347 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
390
599
  : undefined);
391
600
  }, []);
392
601
 
393
- const onModified = (dirty: boolean) => {
394
- if (!autoSave)
395
- onValuesAreModified(dirty);
602
+ // useEffect(() => {
603
+ // baseDataSourceValuesRef.current = getDataSourceEntityValues(initialResolvedCollection, status, entity);
604
+ // const initialValues = formex.initialValues;
605
+ // if (!formex.isSubmitting && initialValues && status === "existing") {
606
+ // setUnderlyingChanges(
607
+ // Object.entries(resolvedCollection.properties)
608
+ // .map(([key, property]) => {
609
+ // if (isHidden(property)) {
610
+ // return {};
611
+ // }
612
+ // const initialValue = initialValues[key];
613
+ // const latestValue = baseDataSourceValuesRef.current[key];
614
+ // if (!equal(initialValue, latestValue)) {
615
+ // return { [key]: latestValue };
616
+ // }
617
+ // return {};
618
+ // })
619
+ // .reduce((a, b) => ({ ...a, ...b }), {}) as Partial<EntityValues<M>>
620
+ // );
621
+ // } else {
622
+ // setUnderlyingChanges({});
623
+ // }
624
+ // }, [entity, initialResolvedCollection, status]);
625
+
626
+ const pluginActions: React.ReactNode[] = [];
627
+
628
+ const plugins = customizationController.plugins;
629
+
630
+ if (plugins && inputCollection) {
631
+ const actionProps: PluginFormActionProps = {
632
+ entityId,
633
+ path,
634
+ status,
635
+ collection: inputCollection,
636
+ context,
637
+ currentEntityId: entityId,
638
+ formContext
639
+ };
640
+ pluginActions.push(...plugins.map((plugin, i) => (
641
+ plugin.form?.Actions
642
+ ? <plugin.form.Actions
643
+ key={`actions_${plugin.key}`} {...actionProps}/>
644
+ : null
645
+ )).filter(Boolean));
396
646
  }
397
647
 
648
+ const titlePropertyKey = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
649
+ const title = formex.values && titlePropertyKey ? getValueInPath(formex.values, titlePropertyKey) : undefined;
650
+
651
+ const onIdUpdate = inputCollection.callbacks?.onIdUpdate;
652
+
653
+ const doOnIdUpdate = useCallback(async () => {
654
+ if (onIdUpdate && formex.values && (status === "new" || status === "copy")) {
655
+ setCustomIdLoading(true);
656
+ try {
657
+ const updatedId = await onIdUpdate({
658
+ collection: resolvedCollection,
659
+ path,
660
+ entityId,
661
+ values: formex.values,
662
+ context
663
+ });
664
+ setEntityId(updatedId);
665
+ } catch (e) {
666
+ onIdUpdateError && onIdUpdateError(e);
667
+ console.error(e);
668
+ }
669
+ setCustomIdLoading(false);
670
+ }
671
+ }, [entityId, formex.values, status]);
672
+
673
+ useEffect(() => {
674
+ doOnIdUpdate();
675
+ }, [doOnIdUpdate]);
676
+
677
+ const [underlyingChanges, setUnderlyingChanges] = useState<Partial<EntityValues<M>>>({});
678
+
679
+ const uniqueFieldValidator: CustomFieldValidator = useCallback(({
680
+ name,
681
+ value,
682
+ property
683
+ }) => dataSource.checkUniqueField(path, name, value, entityId),
684
+ [dataSource, path, entityId]);
685
+
686
+ const validationSchema = useMemo(() => entityId
687
+ ? getYupEntitySchema(
688
+ entityId,
689
+ resolvedCollection.properties,
690
+ uniqueFieldValidator)
691
+ : undefined,
692
+ [entityId, resolvedCollection.properties, uniqueFieldValidator]);
693
+
694
+ const getActionsForEntity = useCallback(({
695
+ entity,
696
+ customEntityActions
697
+ }: {
698
+ entity?: Entity<M>,
699
+ customEntityActions?: EntityAction[]
700
+ }): EntityAction[] => {
701
+ const createEnabled = canCreateEntity(inputCollection, authController, path, null);
702
+ const deleteEnabled = entity ? canDeleteEntity(inputCollection, authController, path, entity) : true;
703
+ const actions: EntityAction[] = [];
704
+ if (createEnabled)
705
+ actions.push(copyEntityAction);
706
+ if (deleteEnabled)
707
+ actions.push(deleteEntityAction);
708
+ if (customEntityActions)
709
+ actions.push(...customEntityActions);
710
+ return actions;
711
+ }, [authController, inputCollection, path]);
712
+
713
+ const modified = formex.dirty;
714
+ useEffect(() => {
715
+ if (!autoSave) {
716
+ onValuesAreModified(modified);
717
+ } else {
718
+ if (formex.values && !equal(formex.values, lastSavedValues.current)) {
719
+ save(formex.values);
720
+ }
721
+ }
722
+ }, [modified, formex.values]);
723
+
724
+ useEffect(() => {
725
+ if (!autoSave && !formex.isSubmitting && underlyingChanges && entity) {
726
+ // we update the form fields from the Firestore data
727
+ // if they were not touched
728
+ Object.entries(underlyingChanges).forEach(([key, value]) => {
729
+ const formValue = formex.values[key];
730
+ if (!equal(value, formValue) && !formex.touched[key]) {
731
+ console.debug("Updated value from the datasource:", key, value);
732
+ formex.setFieldValue(key, value !== undefined ? value : null);
733
+ }
734
+ });
735
+ }
736
+ }, [formex.isSubmitting, autoSave, underlyingChanges, entity, formex.values, formex.touched, formex.setFieldValue]);
737
+
738
+ const formFields = (
739
+ <>
740
+ {(getFormFieldKeys(resolvedCollection))
741
+ .map((key) => {
742
+
743
+ const property = resolvedCollection.properties[key];
744
+ if (property) {
745
+
746
+ const underlyingValueHasChanged: boolean =
747
+ !!underlyingChanges &&
748
+ Object.keys(underlyingChanges).includes(key) &&
749
+ !!formex.touched[key];
750
+
751
+ const disabled = (!autoSave && formex.isSubmitting) || isReadOnly(property) || Boolean(property.disabled);
752
+ const hidden = isHidden(property);
753
+ if (hidden) return null;
754
+ const cmsFormFieldProps: PropertyFieldBindingProps<any, M> = {
755
+ propertyKey: key,
756
+ disabled,
757
+ property,
758
+ includeDescription: property.description || property.longDescription,
759
+ underlyingValueHasChanged: underlyingValueHasChanged && !autoSave,
760
+ context: formContext,
761
+ tableMode: false,
762
+ partOfArray: false,
763
+ partOfBlock: false,
764
+ autoFocus: false
765
+ };
766
+
767
+ return (
768
+ <div id={`form_field_${key}`}
769
+ key={`field_${resolvedCollection.name}_${key}`}>
770
+ <ErrorBoundary>
771
+ <Tooltip title={<PropertyIdCopyTooltipContent propertyId={key}/>}
772
+ delayDuration={800}
773
+ side={"left"}
774
+ align={"start"}
775
+ sideOffset={16}>
776
+ <PropertyFieldBinding {...cmsFormFieldProps}/>
777
+ </Tooltip>
778
+ </ErrorBoundary>
779
+ </div>
780
+ );
781
+ }
782
+
783
+ const additionalField = resolvedCollection.additionalFields?.find(f => f.key === key);
784
+ if (additionalField && entity) {
785
+ const Builder = additionalField.Builder;
786
+ if (!Builder && !additionalField.value) {
787
+ throw new Error("When using additional fields you need to provide a Builder or a value");
788
+ }
789
+
790
+ const child = Builder
791
+ ? <Builder entity={entity} context={context}/>
792
+ : <>{additionalField.value?.({
793
+ entity,
794
+ context
795
+ })}</>;
796
+ return (
797
+ <div>
798
+ <LabelWithIcon icon={<NotesIcon size={"small"}/>}
799
+ title={additionalField.name}
800
+ className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
801
+ <div
802
+ className={cls(paperMixin, "min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
803
+
804
+ <ErrorBoundary>
805
+ {child}
806
+ </ErrorBoundary>
807
+
808
+ </div>
809
+ </div>
810
+ );
811
+ }
812
+
813
+ console.warn(`Property ${key} not found in collection ${resolvedCollection.name} in properties or additional fields. Skipping.`);
814
+ return null;
815
+ })
816
+ .filter(Boolean)}
817
+
818
+ </>
819
+ );
820
+
821
+ const disabled = formex.isSubmitting || (!modified && status === "existing");
822
+ const formRef = React.useRef<HTMLDivElement>(null);
823
+
824
+ const entityActions = getActionsForEntity({
825
+ entity,
826
+ customEntityActions: inputCollection.entityActions
827
+ });
828
+ const formActions = entityActions.filter(a => a.includeInForm === undefined || a.includeInForm);
829
+
830
+ const dialogActions = <DialogActions position={"absolute"}>
831
+
832
+ {savingError &&
833
+ <div className="text-right">
834
+ <Typography color={"error"}>
835
+ {savingError.message}
836
+ </Typography>
837
+ </div>}
838
+
839
+ {entity && formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
840
+ {formActions.map(action => (
841
+ <IconButton
842
+ key={action.name}
843
+ color="primary"
844
+ onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
845
+ event.stopPropagation();
846
+ if (entity)
847
+ action.onClick({
848
+ entity,
849
+ fullPath: resolvedCollection.path,
850
+ collection: resolvedCollection,
851
+ context,
852
+ sideEntityController
853
+ });
854
+ }}>
855
+ {action.icon}
856
+ </IconButton>
857
+ ))}
858
+ </div>}
859
+ {formex.isSubmitting && <CircularProgress size={"small"}/>}
860
+ <Button
861
+ variant="text"
862
+ disabled={disabled || formex.isSubmitting}
863
+ type="reset">
864
+ {status === "existing" ? "Discard" : "Clear"}
865
+ </Button>
866
+
867
+ <Button
868
+ variant="text"
869
+ color="primary"
870
+ type="submit"
871
+ disabled={disabled || formex.isSubmitting}
872
+ onClick={() => {
873
+ closeAfterSaveRef.current = false;
874
+ }}>
875
+ {status === "existing" && "Save"}
876
+ {status === "copy" && "Create copy"}
877
+ {status === "new" && "Create"}
878
+ </Button>
879
+
880
+ <Button
881
+ variant="filled"
882
+ color="primary"
883
+ type="submit"
884
+ disabled={disabled || formex.isSubmitting}
885
+ onClick={() => {
886
+ closeAfterSaveRef.current = true;
887
+ }}>
888
+ {status === "existing" && "Save and close"}
889
+ {status === "copy" && "Create copy and close"}
890
+ {status === "new" && "Create and close"}
891
+ </Button>
892
+
893
+ </DialogActions>;
894
+
398
895
  function buildForm() {
399
- const plugins = customizationController.plugins;
400
- let form = <EntityForm
401
- status={status}
402
- path={path}
403
- collection={collection}
404
- onEntitySaveRequested={onSaveEntityRequest}
405
- onDiscard={onDiscard}
406
- onValuesChanged={onValuesChanged}
407
- onModified={onModified}
408
- entity={usedEntity}
409
- onIdChange={onIdChange}
410
- onFormContextChange={setFormContext}
411
- hideId={collection.hideIdFromForm}
412
- autoSave={autoSave}
413
- onIdUpdateError={onIdUpdateError}
414
- />;
896
+
897
+ let form = <div className="h-full overflow-auto">
898
+
899
+ {pluginActions.length > 0 && <div
900
+ className={cls("w-full flex justify-end items-center sticky top-0 right-0 left-0 z-10 bg-opacity-60 bg-slate-200 dark:bg-opacity-60 dark:bg-slate-800 backdrop-blur-md")}>
901
+ {pluginActions}
902
+ </div>}
903
+
904
+ <div className="pt-12 pb-16 pl-8 pr-8 md:pl-10 md:pr-10">
905
+ <div
906
+ className={`w-full py-2 flex flex-col items-start mt-${4 + (pluginActions ? 8 : 0)} lg:mt-${8 + (pluginActions ? 8 : 0)} mb-8`}>
907
+
908
+ <Typography
909
+ className={"mt-4 flex-grow line-clamp-1 " + inputCollection.hideIdFromForm ? "mb-2" : "mb-0"}
910
+ variant={"h4"}>{title ?? inputCollection.singularName ?? inputCollection.name}
911
+ </Typography>
912
+ <Alert color={"base"} className={"w-full"} size={"small"}>
913
+ <code className={"text-xs select-all"}>{path}/{entityId}</code>
914
+ </Alert>
915
+ </div>
916
+
917
+ {!collection.hideIdFromForm &&
918
+ <CustomIdField customId={inputCollection.customId}
919
+ entityId={entityId}
920
+ status={status}
921
+ onChange={setEntityId}
922
+ error={entityIdError}
923
+ loading={customIdLoading}
924
+ entity={entity}/>}
925
+
926
+ {entityId && formContext && <>
927
+ <div className="mt-12 flex flex-col gap-8"
928
+ ref={formRef}>
929
+
930
+ {formFields}
931
+
932
+ <ErrorFocus containerRef={formRef}/>
933
+
934
+ </div>
935
+
936
+ <div className="h-14"/>
937
+
938
+ </>}
939
+
940
+ </div>
941
+ </div>;
942
+
415
943
  if (plugins) {
416
944
  plugins.forEach((plugin: FireCMSPlugin) => {
417
945
  if (plugin.form?.provider) {
@@ -421,8 +949,6 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
421
949
  path={path}
422
950
  collection={collection}
423
951
  onDiscard={onDiscard}
424
- onValuesChanged={onValuesChanged}
425
- onModified={onModified}
426
952
  entity={usedEntity}
427
953
  context={context}
428
954
  formContext={formContext}
@@ -436,7 +962,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
436
962
  return <ErrorBoundary>{form}</ErrorBoundary>;
437
963
  }
438
964
 
439
- const form = (readOnly === undefined)
965
+ const entityView = (readOnly === undefined)
440
966
  ? <></>
441
967
  : (!readOnly
442
968
  ? buildForm()
@@ -451,6 +977,7 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
451
977
  entity={usedEntity as Entity<M>}
452
978
  path={path}
453
979
  collection={collection}/>
980
+
454
981
  </>
455
982
  ));
456
983
 
@@ -475,87 +1002,132 @@ export function EntityEditView<M extends Record<string, any>, UserType extends U
475
1002
  </Tab>
476
1003
  );
477
1004
 
1005
+ useEffect(() => {
1006
+ if (entityId && onIdChange)
1007
+ onIdChange(entityId);
1008
+ }, [entityId, onIdChange]);
1009
+
478
1010
  return (
479
- <div
480
- className="flex flex-col h-full w-full transition-width duration-250 ease-in-out">
481
- {
482
- <>
1011
+ <Formex value={formex}>
483
1012
 
484
- <div
485
- className={cn(defaultBorderMixin, "no-scrollbar border-b pl-2 pr-2 pt-1 flex items-end overflow-scroll bg-gray-50 dark:bg-gray-950")}>
486
-
487
- <div
488
- className="pb-1 self-center">
489
- <IconButton
490
- onClick={() => {
491
- onClose?.();
492
- return sideDialogContext.close(false);
493
- }}
494
- size="large">
495
- <CloseIcon/>
496
- </IconButton>
497
- </div>
498
-
499
- <div className={"flex-grow"}/>
500
-
501
- {globalLoading && <div
502
- className="self-center">
503
- <CircularProgress size={"small"}/>
504
- </div>}
505
-
506
- <Tabs
507
- value={selectedTabRef.current}
508
- onValueChange={(value) => {
509
- onSideTabClick(value);
510
- }}
511
- className="pl-4 pr-4 pt-0">
512
-
513
- <Tab
514
- disabled={!hasAdditionalViews}
515
- value={MAIN_TAB_VALUE}
516
- className={`${
517
- !hasAdditionalViews ? "hidden" : ""
518
- } text-sm min-w-[140px]`}
519
- >{collection.singularName ?? collection.name}</Tab>
520
-
521
- {customViewTabs}
522
-
523
- {subcollectionTabs}
524
- </Tabs>
1013
+ <div className="flex flex-col h-full w-full transition-width duration-250 ease-in-out">
525
1014
 
526
- </div>
1015
+ <div
1016
+ className={cls(defaultBorderMixin, "no-scrollbar border-b pl-2 pr-2 pt-1 flex items-end overflow-scroll bg-gray-50 dark:bg-gray-950")}>
527
1017
 
528
1018
  <div
529
- className={"flex-grow h-full flex overflow-auto flex-row w-full "}
530
- style={{
531
- // width: `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedFormWidth})`,
532
- // maxWidth: "100%",
533
- // [`@media (max-width: ${resolvedFormWidth})`]: {
534
- // width: resolvedFormWidth
535
- // }
536
- }}>
1019
+ className="pb-1 self-center">
1020
+ <IconButton
1021
+ onClick={() => {
1022
+ onClose?.();
1023
+ return sideDialogContext.close(false);
1024
+ }}>
1025
+ <CloseIcon size={"small"}/>
1026
+ </IconButton>
1027
+ </div>
1028
+
1029
+ <div className={"flex-grow"}/>
1030
+
1031
+ {globalLoading && <div
1032
+ className="self-center">
1033
+ <CircularProgress size={"small"}/>
1034
+ </div>}
537
1035
 
538
- <div
539
- role="tabpanel"
540
- hidden={!mainViewVisible}
541
- id={`form_${path}`}
542
- className={" w-full"}>
1036
+ <Tabs
1037
+ value={selectedTabRef.current}
1038
+ onValueChange={(value) => {
1039
+ onSideTabClick(value);
1040
+ }}
1041
+ className="pl-4 pr-4 pt-0">
543
1042
 
544
- {globalLoading
545
- ? <CircularProgressCenter/>
546
- : form}
1043
+ <Tab
1044
+ disabled={!hasAdditionalViews}
1045
+ value={MAIN_TAB_VALUE}
1046
+ className={`${
1047
+ !hasAdditionalViews ? "hidden" : ""
1048
+ } text-sm min-w-[140px]`}
1049
+ >{collection.singularName ?? collection.name}</Tab>
547
1050
 
548
- </div>
1051
+ {customViewTabs}
549
1052
 
550
- {customViewsView}
1053
+ {subcollectionTabs}
1054
+ </Tabs>
551
1055
 
552
- {subCollectionsViews}
1056
+ </div>
1057
+
1058
+ <form
1059
+ onSubmit={formex.handleSubmit}
1060
+ onReset={() => {
1061
+ formex.resetForm();
1062
+ return onDiscard && onDiscard();
1063
+ }}
1064
+ noValidate
1065
+ className={"flex-grow h-full flex overflow-auto flex-col w-full"}>
1066
+
1067
+ <div
1068
+ role="tabpanel"
1069
+ hidden={!mainViewVisible}
1070
+ id={`form_${path}`}
1071
+ className={" w-full"}>
1072
+
1073
+ {globalLoading
1074
+ ? <CircularProgressCenter/>
1075
+ : entityView}
553
1076
 
554
1077
  </div>
555
1078
 
556
- </>
557
- }
1079
+ {customViewsView}
1080
+
1081
+ {subCollectionsViews}
1082
+
1083
+ {shouldShowEntityActions && dialogActions}
558
1084
 
559
- </div>
1085
+ </form>
1086
+
1087
+ </div>
1088
+ </Formex>
560
1089
  );
561
1090
  }
1091
+
1092
+ function getDataSourceEntityValues<M extends object>(initialResolvedCollection: ResolvedEntityCollection,
1093
+ status: "new" | "existing" | "copy",
1094
+ entity: Entity<M> | undefined): Partial<EntityValues<M>> {
1095
+
1096
+ const properties = initialResolvedCollection.properties;
1097
+ if ((status === "existing" || status === "copy") && entity) {
1098
+ return entity.values ?? getDefaultValuesFor(properties);
1099
+ } else if (status === "new") {
1100
+ return getDefaultValuesFor(properties);
1101
+ } else {
1102
+ console.error({
1103
+ status,
1104
+ entity
1105
+ });
1106
+ throw new Error("Form has not been initialised with the correct parameters");
1107
+ }
1108
+ }
1109
+
1110
+ export type EntityFormSaveParams<M extends Record<string, any>> = {
1111
+ collection: ResolvedEntityCollection<M>,
1112
+ path: string,
1113
+ entityId: string | undefined,
1114
+ values: EntityValues<M>,
1115
+ previousValues?: EntityValues<M>,
1116
+ closeAfterSave: boolean,
1117
+ autoSave: boolean
1118
+ };
1119
+
1120
+ export function yupToFormErrors(yupError: ValidationError): Record<string, any> {
1121
+ let errors: Record<string, any> = {};
1122
+ if (yupError.inner) {
1123
+ if (yupError.inner.length === 0) {
1124
+ return setIn(errors, yupError.path!, yupError.message);
1125
+ }
1126
+ for (const err of yupError.inner) {
1127
+ if (!getIn(errors, err.path!)) {
1128
+ errors = setIn(errors, err.path!, err.message);
1129
+ }
1130
+ }
1131
+ }
1132
+ return errors;
1133
+ }