@firecms/core 3.0.0-canary.7 → 3.0.0-canary.70

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 (271) hide show
  1. package/README.md +2 -2
  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/ClearFilterSortButton.d.ts +5 -0
  8. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  9. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  10. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  11. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
  12. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  13. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  14. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  15. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  16. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  17. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
  18. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  19. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  20. package/dist/components/EntityPreview.d.ts +26 -7
  21. package/dist/components/EntityView.d.ts +11 -0
  22. package/dist/components/FieldCaption.d.ts +5 -0
  23. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  24. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
  25. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  26. package/dist/components/HomePage/index.d.ts +3 -1
  27. package/dist/components/ReferenceWidget.d.ts +3 -1
  28. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  29. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  30. package/dist/components/VirtualTable/VirtualTableProps.d.ts +6 -7
  31. package/dist/components/VirtualTable/types.d.ts +3 -3
  32. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  33. package/dist/components/common/index.d.ts +1 -0
  34. package/dist/components/common/table_height.d.ts +5 -0
  35. package/dist/components/common/types.d.ts +4 -6
  36. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  37. package/dist/components/index.d.ts +6 -3
  38. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  39. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +5 -8
  40. package/dist/core/DefaultDrawer.d.ts +19 -0
  41. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  42. package/dist/core/EntityEditView.d.ts +36 -0
  43. package/dist/core/NavigationRoutes.d.ts +2 -2
  44. package/dist/core/index.d.ts +3 -4
  45. package/dist/form/PropertiesForm.d.ts +8 -0
  46. package/dist/form/components/ErrorFocus.d.ts +1 -1
  47. package/dist/form/components/FieldHelperText.d.ts +3 -3
  48. package/dist/form/components/StorageItemPreview.d.ts +2 -3
  49. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  50. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  51. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  52. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -4
  53. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  54. package/dist/form/index.d.ts +0 -2
  55. package/dist/form/validation.d.ts +1 -1
  56. package/dist/hooks/data/delete.d.ts +2 -2
  57. package/dist/hooks/data/save.d.ts +2 -3
  58. package/dist/hooks/data/useDataSource.d.ts +2 -2
  59. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  60. package/dist/hooks/index.d.ts +2 -0
  61. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  62. package/dist/hooks/useProjectLog.d.ts +6 -2
  63. package/dist/hooks/useStorageSource.d.ts +2 -2
  64. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  65. package/dist/index.d.ts +1 -0
  66. package/dist/index.es.js +10512 -9997
  67. package/dist/index.es.js.map +1 -1
  68. package/dist/index.umd.js +5 -5
  69. package/dist/index.umd.js.map +1 -1
  70. package/dist/internal/useBuildDataSource.d.ts +1 -16
  71. package/dist/preview/PropertyPreview.d.ts +1 -1
  72. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  73. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  74. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  75. package/dist/preview/components/ReferencePreview.d.ts +3 -8
  76. package/dist/types/analytics.d.ts +1 -1
  77. package/dist/types/auth.d.ts +37 -1
  78. package/dist/types/collections.d.ts +44 -6
  79. package/dist/types/datasource.d.ts +21 -14
  80. package/dist/types/entities.d.ts +5 -1
  81. package/dist/types/entity_actions.d.ts +14 -0
  82. package/dist/types/entity_callbacks.d.ts +2 -2
  83. package/dist/types/entity_overrides.d.ts +6 -0
  84. package/dist/types/fields.d.ts +31 -30
  85. package/dist/types/index.d.ts +2 -1
  86. package/dist/types/navigation.d.ts +15 -14
  87. package/dist/types/permissions.d.ts +5 -1
  88. package/dist/types/plugins.d.ts +22 -22
  89. package/dist/types/properties.d.ts +13 -5
  90. package/dist/types/property_config.d.ts +2 -2
  91. package/dist/types/roles.d.ts +31 -0
  92. package/dist/types/storage.d.ts +11 -3
  93. package/dist/types/user.d.ts +5 -0
  94. package/dist/util/collections.d.ts +9 -1
  95. package/dist/util/entities.d.ts +1 -1
  96. package/dist/util/icon_synonyms.d.ts +1 -97
  97. package/dist/util/icons.d.ts +8 -2
  98. package/dist/util/navigation_utils.d.ts +2 -2
  99. package/dist/util/objects.d.ts +1 -1
  100. package/dist/util/permissions.d.ts +4 -4
  101. package/dist/util/references.d.ts +4 -2
  102. package/dist/util/resolutions.d.ts +14 -14
  103. package/dist/util/storage.d.ts +23 -2
  104. package/dist/util/useStorageUploadController.d.ts +1 -1
  105. package/dist/util/useTraceUpdate.d.ts +1 -0
  106. package/package.json +139 -119
  107. package/src/app/AppBar.tsx +18 -0
  108. package/src/app/Drawer.tsx +25 -0
  109. package/src/app/Scaffold.tsx +249 -0
  110. package/src/app/index.ts +4 -0
  111. package/src/app/useApp.tsx +32 -0
  112. package/src/components/ClearFilterSortButton.tsx +41 -0
  113. package/src/components/DeleteEntityDialog.tsx +4 -4
  114. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +4 -4
  115. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +276 -279
  116. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
  117. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +48 -45
  118. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  119. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +18 -17
  120. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +5 -5
  121. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  122. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  123. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -5
  124. package/src/components/EntityCollectionView/EntityCollectionView.tsx +73 -72
  125. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  126. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  127. package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
  128. package/src/components/EntityPreview.tsx +209 -70
  129. package/src/components/EntityView.tsx +84 -0
  130. package/src/components/FieldCaption.tsx +14 -0
  131. package/src/components/HomePage/DefaultHomePage.tsx +15 -11
  132. package/src/components/HomePage/NavigationCard.tsx +69 -0
  133. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  134. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  135. package/src/components/HomePage/index.tsx +3 -1
  136. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  137. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
  138. package/src/components/ReferenceWidget.tsx +22 -12
  139. package/src/components/SearchIconsView.tsx +5 -5
  140. package/src/components/SelectableTable/SelectableTable.tsx +5 -3
  141. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  142. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  143. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -24
  144. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  145. package/src/components/VirtualTable/VirtualTable.tsx +38 -29
  146. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  147. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  148. package/src/components/VirtualTable/VirtualTableProps.tsx +7 -7
  149. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  150. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  151. package/src/components/VirtualTable/types.tsx +2 -3
  152. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +11 -7
  153. package/src/components/common/index.ts +1 -0
  154. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  155. package/src/components/common/types.tsx +4 -6
  156. package/src/components/common/useColumnsIds.tsx +10 -2
  157. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
  158. package/src/components/common/useTableSearchHelper.ts +39 -9
  159. package/src/components/index.tsx +6 -3
  160. package/src/contexts/AuthControllerContext.tsx +1 -1
  161. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +51 -34
  162. package/src/core/DefaultDrawer.tsx +177 -0
  163. package/src/core/DrawerNavigationItem.tsx +62 -0
  164. package/src/core/EntityEditView.tsx +1101 -0
  165. package/src/core/EntitySidePanel.tsx +3 -4
  166. package/src/core/FireCMS.tsx +54 -43
  167. package/src/core/NavigationRoutes.tsx +14 -7
  168. package/src/core/field_configs.tsx +2 -3
  169. package/src/core/index.tsx +3 -4
  170. package/src/form/PropertiesForm.tsx +81 -0
  171. package/src/form/PropertyFieldBinding.tsx +29 -7
  172. package/src/form/components/FieldHelperText.tsx +3 -3
  173. package/src/form/components/StorageItemPreview.tsx +5 -7
  174. package/src/form/components/StorageUploadProgress.tsx +9 -8
  175. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +10 -12
  176. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  177. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  178. package/src/form/field_bindings/KeyValueFieldBinding.tsx +19 -19
  179. package/src/form/field_bindings/MapFieldBinding.tsx +25 -17
  180. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  181. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -9
  182. package/src/form/field_bindings/ReferenceFieldBinding.tsx +16 -13
  183. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  184. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +14 -35
  185. package/src/form/field_bindings/TextFieldBinding.tsx +7 -5
  186. package/src/form/index.tsx +4 -4
  187. package/src/form/validation.ts +4 -21
  188. package/src/hooks/data/delete.ts +3 -3
  189. package/src/hooks/data/save.ts +4 -2
  190. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  191. package/src/hooks/data/useDataSource.tsx +8 -3
  192. package/src/hooks/data/useEntityFetch.tsx +4 -4
  193. package/src/hooks/index.tsx +3 -0
  194. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  195. package/src/hooks/useBuildModeController.tsx +11 -5
  196. package/src/hooks/useBuildNavigationController.tsx +200 -83
  197. package/src/hooks/useProjectLog.tsx +17 -7
  198. package/src/hooks/useReferenceDialog.tsx +2 -2
  199. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  200. package/src/hooks/useStorageSource.tsx +7 -2
  201. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  202. package/src/index.ts +1 -0
  203. package/src/internal/useBuildDataSource.ts +54 -47
  204. package/src/internal/useBuildSideEntityController.tsx +88 -21
  205. package/src/preview/PropertyPreview.tsx +5 -15
  206. package/src/preview/PropertyPreviewProps.tsx +1 -11
  207. package/src/preview/components/BooleanPreview.tsx +19 -4
  208. package/src/preview/components/EnumValuesChip.tsx +2 -2
  209. package/src/preview/components/ReferencePreview.tsx +72 -165
  210. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  211. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  212. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  213. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  214. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  215. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  216. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  217. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  218. package/src/types/analytics.ts +1 -0
  219. package/src/types/auth.tsx +50 -1
  220. package/src/types/collections.ts +51 -6
  221. package/src/types/customization_controller.tsx +0 -1
  222. package/src/types/datasource.ts +24 -17
  223. package/src/types/entities.ts +9 -1
  224. package/src/types/entity_actions.tsx +17 -0
  225. package/src/types/entity_callbacks.ts +2 -2
  226. package/src/types/entity_overrides.tsx +7 -0
  227. package/src/types/fields.tsx +33 -33
  228. package/src/types/firecms.tsx +0 -1
  229. package/src/types/index.ts +2 -1
  230. package/src/types/navigation.ts +17 -17
  231. package/src/types/permissions.ts +6 -1
  232. package/src/types/plugins.tsx +28 -30
  233. package/src/types/properties.ts +19 -7
  234. package/src/types/property_config.tsx +2 -2
  235. package/src/types/roles.ts +41 -0
  236. package/src/types/side_entity_controller.tsx +1 -0
  237. package/src/types/storage.ts +12 -3
  238. package/src/types/user.ts +7 -0
  239. package/src/util/collections.ts +22 -0
  240. package/src/util/entities.ts +2 -1
  241. package/src/util/enums.ts +1 -1
  242. package/src/util/icon_list.ts +2 -2
  243. package/src/util/icon_synonyms.ts +3 -99
  244. package/src/util/icons.tsx +11 -3
  245. package/src/util/navigation_utils.ts +6 -6
  246. package/src/util/objects.ts +8 -21
  247. package/src/util/permissions.ts +12 -8
  248. package/src/util/references.ts +36 -5
  249. package/src/util/resolutions.ts +32 -31
  250. package/src/util/storage.ts +75 -21
  251. package/src/util/strings.ts +2 -2
  252. package/src/util/useStorageUploadController.tsx +21 -3
  253. package/src/util/useTraceUpdate.tsx +2 -1
  254. package/dist/components/VirtualTable/common.d.ts +0 -2
  255. package/dist/core/Drawer.d.ts +0 -23
  256. package/dist/core/EntityView.d.ts +0 -22
  257. package/dist/core/Scaffold.d.ts +0 -55
  258. package/dist/core/SideEntityView.d.ts +0 -7
  259. package/dist/form/EntityForm.d.ts +0 -77
  260. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  261. package/dist/internal/useLocaleConfig.d.ts +0 -1
  262. package/dist/types/appcheck.d.ts +0 -26
  263. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  264. package/src/core/Drawer.tsx +0 -164
  265. package/src/core/EntityView.tsx +0 -578
  266. package/src/core/Scaffold.tsx +0 -281
  267. package/src/core/SideEntityView.tsx +0 -38
  268. package/src/form/EntityForm.tsx +0 -720
  269. package/src/internal/useBuildCustomizationController.tsx +0 -5
  270. package/src/internal/useLocaleConfig.tsx +0 -18
  271. package/src/types/appcheck.ts +0 -29
@@ -1,720 +0,0 @@
1
- import React, { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
-
3
- import {
4
- CMSAnalyticsEvent,
5
- Entity,
6
- EntityAction,
7
- EntityCollection,
8
- EntityStatus,
9
- EntityValues,
10
- FormContext,
11
- PluginFormActionProps,
12
- PropertyFieldBindingProps,
13
- ResolvedEntityCollection
14
- } from "../types";
15
- import { Formex, FormexController, getIn, setIn, useCreateFormex } from "@firecms/formex";
16
- import { PropertyFieldBinding } from "./PropertyFieldBinding";
17
- import { CustomFieldValidator, getYupEntitySchema } from "./validation";
18
- import equal from "react-fast-compare"
19
- import {
20
- canCreateEntity,
21
- canDeleteEntity,
22
- fullPathToCollectionSegments,
23
- getDefaultValuesFor,
24
- isHidden,
25
- isReadOnly,
26
- resolveCollection
27
- } from "../util";
28
- import {
29
- useAuthController,
30
- useCustomizationController,
31
- useDataSource,
32
- useFireCMSContext,
33
- useSideEntityController
34
- } from "../hooks";
35
- import { ErrorFocus } from "./components/ErrorFocus";
36
- import { CustomIdField } from "./components/CustomIdField";
37
- import { Alert, Button, cn, DialogActions, IconButton, Tooltip, Typography } from "@firecms/ui";
38
- import { ErrorBoundary } from "../components";
39
- import {
40
- copyEntityAction,
41
- deleteEntityAction
42
- } from "../components/EntityCollectionTable/internal/default_entity_actions";
43
- import { useAnalyticsController } from "../hooks/useAnalyticsController";
44
- import { ValidationError } from "yup";
45
- import { PropertyIdCopyTooltipContent } from "../components/PropertyIdCopyTooltipContent";
46
-
47
- /**
48
- * @group Components
49
- */
50
- export interface EntityFormProps<M extends Record<string, any>> {
51
-
52
- /**
53
- * New or existing status
54
- */
55
- status: EntityStatus;
56
-
57
- /**
58
- * Path of the collection this entity is located
59
- */
60
- path: string;
61
-
62
- /**
63
- * The collection is used to build the fields of the form
64
- */
65
- collection: EntityCollection<M>
66
-
67
- /**
68
- * The updated entity is passed from the parent component when the underlying data
69
- * has changed in the datasource
70
- */
71
- entity?: Entity<M>;
72
-
73
- /**
74
- * The callback function called when Save is clicked and validation is correct
75
- */
76
- onEntitySaveRequested: (
77
- props: EntityFormSaveParams<M>
78
- ) => Promise<void>;
79
-
80
- /**
81
- * The callback function called when discard is clicked
82
- */
83
- onDiscard?: () => void;
84
-
85
- /**
86
- * The callback function when the form is dirty, so the values are different
87
- * from the original ones
88
- */
89
- onModified?: (dirty: boolean) => void;
90
-
91
- /**
92
- * The callback function when the form original values have been modified
93
- */
94
- onValuesChanged?: (values?: EntityValues<M>) => void;
95
-
96
- /**
97
- *
98
- * @param id
99
- */
100
- onIdChange?: (id: string) => void;
101
-
102
- currentEntityId?: string;
103
-
104
- onFormContextChange?: (formContext: FormContext<M>) => void;
105
-
106
- hideId?: boolean;
107
-
108
- autoSave?: boolean;
109
-
110
- onIdUpdateError?: (error: any) => void;
111
-
112
- }
113
-
114
- export type EntityFormSaveParams<M extends Record<string, any>> = {
115
- collection: ResolvedEntityCollection<M>,
116
- path: string,
117
- entityId: string | undefined,
118
- values: EntityValues<M>,
119
- previousValues?: EntityValues<M>,
120
- closeAfterSave: boolean,
121
- autoSave: boolean
122
- };
123
-
124
- /**
125
- * This is the form used internally by the CMS
126
- * @param status
127
- * @param path
128
- * @param collection
129
- * @param entity
130
- * @param onEntitySave
131
- * @param onDiscard
132
- * @param onModified
133
- * @param onValuesChanged
134
- * @constructor
135
- * @group Components
136
- */
137
- export const EntityForm = React.memo<EntityFormProps<any>>(EntityFormInternal,
138
- (a: EntityFormProps<any>, b: EntityFormProps<any>) => {
139
- return a.status === b.status &&
140
- a.path === b.path &&
141
- equal(a.entity?.values, b.entity?.values);
142
- }) as typeof EntityFormInternal;
143
-
144
- function getDataSourceEntityValues<M extends object>(initialResolvedCollection: ResolvedEntityCollection,
145
- status: "new" | "existing" | "copy",
146
- entity: Entity<M> | undefined): Partial<EntityValues<M>> {
147
- const properties = initialResolvedCollection.properties;
148
- if ((status === "existing" || status === "copy") && entity) {
149
- return entity.values ?? getDefaultValuesFor(properties);
150
- } else if (status === "new") {
151
- return getDefaultValuesFor(properties);
152
- } else {
153
- console.error({
154
- status,
155
- entity
156
- });
157
- throw new Error("Form has not been initialised with the correct parameters");
158
- }
159
- }
160
-
161
- function EntityFormInternal<M extends Record<string, any>>({
162
- status,
163
- path,
164
- collection: inputCollection,
165
- entity,
166
- onEntitySaveRequested,
167
- onDiscard,
168
- onModified,
169
- onValuesChanged,
170
- onIdChange,
171
- onFormContextChange,
172
- hideId,
173
- autoSave,
174
- onIdUpdateError
175
- }: EntityFormProps<M>) {
176
-
177
- const analyticsController = useAnalyticsController();
178
-
179
- const customizationController = useCustomizationController();
180
-
181
- const context = useFireCMSContext();
182
- const dataSource = useDataSource();
183
- const plugins = customizationController.plugins;
184
-
185
- const initialResolvedCollection = useMemo(() => resolveCollection({
186
- collection: inputCollection,
187
- path,
188
- values: entity?.values,
189
- fields: customizationController.propertyConfigs
190
- }), [entity?.values, path]);
191
-
192
- const mustSetCustomId: boolean = (status === "new" || status === "copy") &&
193
- (Boolean(initialResolvedCollection.customId) && initialResolvedCollection.customId !== "optional");
194
-
195
- const initialEntityId = useMemo(() => {
196
- if (status === "new" || status === "copy") {
197
- if (mustSetCustomId) {
198
- return undefined;
199
- } else {
200
- return dataSource.generateEntityId(path);
201
- }
202
- } else {
203
- return entity?.id;
204
- }
205
- }, []);
206
-
207
- const closeAfterSaveRef = useRef(false);
208
-
209
- const baseDataSourceValuesRef = useRef<Partial<EntityValues<M>>>(getDataSourceEntityValues(initialResolvedCollection, status, entity));
210
-
211
- const [entityId, setEntityId] = React.useState<string | undefined>(initialEntityId);
212
- const [entityIdError, setEntityIdError] = React.useState<boolean>(false);
213
- const [savingError, setSavingError] = React.useState<Error | undefined>();
214
-
215
- const [customIdLoading, setCustomIdLoading] = React.useState<boolean>(false);
216
-
217
- // const initialValuesRef = useRef<EntityValues<M>>(entity?.values ?? baseDataSourceValues as EntityValues<M>);
218
- const [internalValues, setInternalValues] = useState<EntityValues<M> | undefined>(entity?.values ?? baseDataSourceValuesRef.current as EntityValues<M>);
219
-
220
- const save = (values: EntityValues<M>): Promise<void> => {
221
- return onEntitySaveRequested({
222
- collection: resolvedCollection,
223
- path,
224
- entityId,
225
- values,
226
- previousValues: entity?.values,
227
- closeAfterSave: closeAfterSaveRef.current,
228
- autoSave: autoSave ?? false
229
- }).then(_ => {
230
- const eventName: CMSAnalyticsEvent = status === "new"
231
- ? "new_entity_saved"
232
- : (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
233
- analyticsController.onAnalyticsEvent?.(eventName, { path });
234
- }).catch(e => {
235
- console.error(e);
236
- setSavingError(e);
237
- }).finally(() => {
238
- closeAfterSaveRef.current = false;
239
- });
240
- };
241
-
242
- const onSubmit = (values: EntityValues<M>, formexController: FormexController<EntityValues<M>>) => {
243
-
244
- if (mustSetCustomId && !entityId) {
245
- console.error("Missing custom Id");
246
- setEntityIdError(true);
247
- formexController.setSubmitting(false);
248
- return;
249
- }
250
-
251
- setSavingError(undefined);
252
- setEntityIdError(false);
253
-
254
- if (status === "existing") {
255
- if (!entity?.id) throw Error("Form misconfiguration when saving, no id for existing entity");
256
- } else if (status === "new" || status === "copy") {
257
- if (inputCollection.customId) {
258
- if (inputCollection.customId !== "optional" && !entityId) {
259
- throw Error("Form misconfiguration when saving, entityId should be set");
260
- }
261
- }
262
- } else {
263
- throw Error("New FormType added, check EntityForm");
264
- }
265
-
266
- return save(values)
267
- ?.then(_ => {
268
- formexController.resetForm({
269
- values,
270
- submitCount: 0,
271
- touched: {}
272
- });
273
- })
274
- .finally(() => {
275
- formexController.setSubmitting(false);
276
- });
277
-
278
- };
279
-
280
- const formex: FormexController<M> = useCreateFormex<M>({
281
- initialValues: baseDataSourceValuesRef.current as M,
282
- onSubmit,
283
- validation: (values) => {
284
- return validationSchema?.validate(values, { abortEarly: false })
285
- .then(() => {
286
- return {};
287
- })
288
- .catch((e) => {
289
-
290
- const errors: Record<string, string> = {};
291
- e.inner.forEach((error: any) => {
292
- errors[error.path] = error.message;
293
- });
294
- return yupToFormErrors(e);
295
- });
296
- }
297
- });
298
-
299
- useEffect(() => {
300
- baseDataSourceValuesRef.current = getDataSourceEntityValues(initialResolvedCollection, status, entity);
301
- const initialValues = formex.initialValues;
302
- if (!formex.isSubmitting && initialValues && status === "existing") {
303
- setUnderlyingChanges(
304
- Object.entries(resolvedCollection.properties)
305
- .map(([key, property]) => {
306
- if (isHidden(property)) {
307
- return {};
308
- }
309
- const initialValue = initialValues[key];
310
- const latestValue = baseDataSourceValuesRef.current[key];
311
- if (!equal(initialValue, latestValue)) {
312
- return { [key]: latestValue };
313
- }
314
- return {};
315
- })
316
- .reduce((a, b) => ({ ...a, ...b }), {}) as Partial<EntityValues<M>>
317
- );
318
- } else {
319
- setUnderlyingChanges({});
320
- }
321
- }, [entity, initialResolvedCollection, status]);
322
-
323
- const doOnValuesChanges = (values?: EntityValues<M>) => {
324
- const initialValues = formex.initialValues;
325
- setInternalValues(values);
326
- if (onValuesChanged)
327
- onValuesChanged(values);
328
- if (autoSave && values && !equal(values, initialValues)) {
329
- save(values);
330
- }
331
- };
332
-
333
- useEffect(() => {
334
- if (entityId && onIdChange)
335
- onIdChange(entityId);
336
- }, [entityId, onIdChange]);
337
-
338
- const resolvedCollection = resolveCollection<M>({
339
- collection: inputCollection,
340
- path,
341
- entityId,
342
- values: internalValues,
343
- previousValues: formex.initialValues,
344
- fields: customizationController.propertyConfigs
345
- });
346
-
347
- const onIdUpdate = inputCollection.callbacks?.onIdUpdate;
348
-
349
- const doOnIdUpdate = useCallback(async () => {
350
- if (onIdUpdate && internalValues && (status === "new" || status === "copy")) {
351
- setCustomIdLoading(true);
352
- try {
353
- const updatedId = await onIdUpdate({
354
- collection: resolvedCollection,
355
- path,
356
- entityId,
357
- values: internalValues,
358
- context
359
- });
360
- setEntityId(updatedId);
361
- } catch (e) {
362
- onIdUpdateError && onIdUpdateError(e);
363
- console.error(e);
364
- }
365
- setCustomIdLoading(false);
366
- }
367
- }, [entityId, internalValues, status]);
368
-
369
- useEffect(() => {
370
- doOnIdUpdate();
371
- }, [doOnIdUpdate]);
372
-
373
- const [underlyingChanges, setUnderlyingChanges] = useState<Partial<EntityValues<M>>>({});
374
-
375
- const uniqueFieldValidator: CustomFieldValidator = useCallback(({
376
- name,
377
- value,
378
- property
379
- }) => dataSource.checkUniqueField(path, name, value, entityId),
380
- [dataSource, path, entityId]);
381
-
382
- const validationSchema = useMemo(() => entityId
383
- ? getYupEntitySchema(
384
- entityId,
385
- resolvedCollection.properties,
386
- uniqueFieldValidator)
387
- : undefined,
388
- [entityId, resolvedCollection.properties, uniqueFieldValidator]);
389
-
390
- const authController = useAuthController();
391
-
392
- const getActionsForEntity = useCallback(({ entity, customEntityActions }: {
393
- entity?: Entity<M>,
394
- customEntityActions?: EntityAction[]
395
- }): EntityAction[] => {
396
- const createEnabled = canCreateEntity(inputCollection, authController, fullPathToCollectionSegments(path), null);
397
- const deleteEnabled = entity ? canDeleteEntity(inputCollection, authController, fullPathToCollectionSegments(path), entity) : true;
398
- const actions: EntityAction[] = [];
399
- if (createEnabled)
400
- actions.push(copyEntityAction);
401
- if (deleteEnabled)
402
- actions.push(deleteEntityAction);
403
- if (customEntityActions)
404
- actions.push(...customEntityActions);
405
- return actions;
406
- }, [authController, inputCollection, path]);
407
-
408
- const pluginActions: React.ReactNode[] = [];
409
-
410
- const formContext: FormContext<M> = {
411
- setFieldValue: formex.setFieldValue,
412
- values: formex.values,
413
- collection: resolvedCollection,
414
- entityId,
415
- path,
416
- save
417
- };
418
-
419
- // eslint-disable-next-line react-hooks/rules-of-hooks
420
- useEffect(() => {
421
- if (onFormContextChange) {
422
- onFormContextChange(formContext);
423
- }
424
- }, [onFormContextChange, formContext]);
425
-
426
- if (plugins && inputCollection) {
427
- const actionProps: PluginFormActionProps = {
428
- entityId,
429
- path,
430
- status,
431
- collection: inputCollection,
432
- context,
433
- currentEntityId: entityId,
434
- formContext
435
- };
436
- pluginActions.push(...plugins.map((plugin, i) => (
437
- plugin.form?.Actions
438
- ? <plugin.form.Actions
439
- key={`actions_${plugin.name}`} {...actionProps}/>
440
- : null
441
- )).filter(Boolean));
442
- }
443
-
444
- return <Formex value={formex}>
445
- <div className="h-full overflow-auto">
446
-
447
- {pluginActions.length > 0 && <div
448
- className={cn("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")}>
449
- {pluginActions}
450
- </div>}
451
-
452
- <div className="pt-12 pb-16 pl-8 pr-8 md:pl-10 md:pr-10">
453
- <div
454
- className={`w-full py-2 flex flex-col items-start mt-${4 + (pluginActions ? 8 : 0)} lg:mt-${8 + (pluginActions ? 8 : 0)} mb-8`}>
455
-
456
- <Typography
457
- className={"mt-4 flex-grow " + inputCollection.hideIdFromForm ? "mb-2" : "mb-0"}
458
- variant={"h4"}>{inputCollection.singularName ?? inputCollection.name}
459
- </Typography>
460
- <Alert color={"base"} className={"w-full"} size={"small"}>
461
- <code className={"text-xs select-all"}>{path}/{entityId}</code>
462
- </Alert>
463
- </div>
464
-
465
- {!hideId &&
466
- <CustomIdField customId={inputCollection.customId}
467
- entityId={entityId}
468
- status={status}
469
- onChange={setEntityId}
470
- error={entityIdError}
471
- loading={customIdLoading}
472
- entity={entity}/>}
473
-
474
- {entityId && <InnerForm
475
- {...formex}
476
- initialValues={formex.initialValues}
477
- onModified={onModified}
478
- onDiscard={onDiscard}
479
- onValuesChanged={doOnValuesChanges}
480
- underlyingChanges={underlyingChanges}
481
- entity={entity}
482
- resolvedCollection={resolvedCollection}
483
- formContext={formContext}
484
- status={status}
485
- savingError={savingError}
486
- closeAfterSaveRef={closeAfterSaveRef}
487
- autoSave={autoSave}
488
- entityActions={getActionsForEntity({
489
- entity,
490
- customEntityActions: inputCollection.entityActions
491
- })}/>}
492
-
493
- </div>
494
- </div>
495
- </Formex>
496
- }
497
-
498
- function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
499
- initialValues: EntityValues<M>,
500
- onModified: ((modified: boolean) => void) | undefined,
501
- onValuesChanged?: (changedValues?: EntityValues<M>) => void,
502
- underlyingChanges: Partial<M>,
503
- entity: Entity<M> | undefined,
504
- resolvedCollection: ResolvedEntityCollection<M>,
505
- formContext: FormContext<M>,
506
- onDiscard?: () => void,
507
- status: "new" | "existing" | "copy",
508
- savingError?: Error,
509
- closeAfterSaveRef: MutableRefObject<boolean>,
510
- autoSave?: boolean,
511
- entityActions: EntityAction[]
512
- }) {
513
-
514
- const {
515
- values,
516
- onDiscard,
517
- onModified,
518
- onValuesChanged,
519
- underlyingChanges,
520
- formContext,
521
- entity,
522
- touched,
523
- setFieldValue,
524
- resolvedCollection,
525
- isSubmitting,
526
- status,
527
- handleSubmit,
528
- resetForm,
529
- savingError,
530
- dirty,
531
- closeAfterSaveRef,
532
- autoSave,
533
- entityActions
534
- } = props;
535
-
536
- const context = useFireCMSContext();
537
- const formActions = entityActions.filter(a => a.includeInForm === undefined || a.includeInForm);
538
- const sideEntityController = useSideEntityController();
539
-
540
- const modified = dirty;
541
- useEffect(() => {
542
- if (onModified)
543
- onModified(modified);
544
- if (onValuesChanged)
545
- onValuesChanged(values);
546
- }, [modified, values]);
547
-
548
- useEffect(() => {
549
- if (!autoSave && !isSubmitting && underlyingChanges && entity) {
550
- // we update the form fields from the Firestore data
551
- // if they were not touched
552
- Object.entries(underlyingChanges).forEach(([key, value]) => {
553
- const formValue = values[key];
554
- if (!equal(value, formValue) && !touched[key]) {
555
- console.debug("Updated value from the datasource:", key, value);
556
- setFieldValue(key, value !== undefined ? value : null);
557
- }
558
- });
559
- }
560
- }, [isSubmitting, autoSave, underlyingChanges, entity, values, touched, setFieldValue]);
561
-
562
- const formFields = (
563
- <div className={"flex flex-col gap-8"}>
564
- {(resolvedCollection.propertiesOrder ?? Object.keys(resolvedCollection.properties))
565
- .map((key) => {
566
-
567
- const property = resolvedCollection.properties[key];
568
- if (!property) {
569
- console.warn(`Property ${key} not found in collection ${resolvedCollection.name}`);
570
- return null;
571
- }
572
-
573
- const underlyingValueHasChanged: boolean =
574
- !!underlyingChanges &&
575
- Object.keys(underlyingChanges).includes(key) &&
576
- !!touched[key];
577
-
578
- const disabled = (!autoSave && isSubmitting) || isReadOnly(property) || Boolean(property.disabled);
579
- const hidden = isHidden(property);
580
- if (hidden) return null;
581
- const cmsFormFieldProps: PropertyFieldBindingProps<any, M> = {
582
- propertyKey: key,
583
- disabled,
584
- property,
585
- includeDescription: property.description || property.longDescription,
586
- underlyingValueHasChanged: underlyingValueHasChanged && !autoSave,
587
- context: formContext,
588
- tableMode: false,
589
- partOfArray: false,
590
- partOfBlock: false,
591
- autoFocus: false
592
- };
593
-
594
- return (
595
- <div id={`form_field_${key}`}
596
- key={`field_${resolvedCollection.name}_${key}`}>
597
- <ErrorBoundary>
598
- <Tooltip title={<PropertyIdCopyTooltipContent propertyId={key}/>}
599
- delayDuration={800}
600
- side={"left"}
601
- sideOffset={16}>
602
- <PropertyFieldBinding {...cmsFormFieldProps}/>
603
- </Tooltip>
604
- </ErrorBoundary>
605
- </div>
606
- );
607
- })
608
- .filter(Boolean)}
609
-
610
- </div>
611
- );
612
-
613
- const disabled = isSubmitting || (!modified && status === "existing");
614
- const formRef = React.useRef<HTMLDivElement>(null);
615
-
616
- return (
617
-
618
- <form onSubmit={handleSubmit}
619
- onReset={() => {
620
- console.debug("Resetting form")
621
- resetForm();
622
- return onDiscard && onDiscard();
623
- }}
624
- noValidate>
625
- <div className="mt-12"
626
- ref={formRef}>
627
-
628
- {formFields}
629
-
630
- <ErrorFocus containerRef={formRef}/>
631
-
632
- </div>
633
-
634
- <div className="h-14"/>
635
-
636
- {!autoSave && <DialogActions position={"absolute"}>
637
-
638
- {savingError &&
639
- <div className="text-right">
640
- <Typography color={"error"}>
641
- {savingError.message}
642
- </Typography>
643
- </div>}
644
-
645
- {entity && formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
646
- {formActions.map(action => (
647
- <IconButton
648
- key={action.name}
649
- color="primary"
650
- onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
651
- event.stopPropagation();
652
- if (entity)
653
- action.onClick({
654
- entity,
655
- fullPath: resolvedCollection.path,
656
- collection: resolvedCollection,
657
- context,
658
- sideEntityController
659
- });
660
- }}>
661
- {action.icon}
662
- </IconButton>
663
- ))}
664
- </div>}
665
-
666
- <Button
667
- variant="text"
668
- disabled={disabled}
669
- type="reset"
670
- >
671
- {status === "existing" ? "Discard" : "Clear"}
672
- </Button>
673
-
674
- <Button
675
- variant="text"
676
- color="primary"
677
- type="submit"
678
- disabled={disabled}
679
- onClick={() => {
680
- closeAfterSaveRef.current = false;
681
- }}
682
- >
683
- {status === "existing" && "Save"}
684
- {status === "copy" && "Create copy"}
685
- {status === "new" && "Create"}
686
- </Button>
687
-
688
- <Button
689
- variant="filled"
690
- color="primary"
691
- type="submit"
692
- disabled={disabled}
693
- onClick={() => {
694
- closeAfterSaveRef.current = true;
695
- }}
696
- >
697
- {status === "existing" && "Save and close"}
698
- {status === "copy" && "Create copy and close"}
699
- {status === "new" && "Create and close"}
700
- </Button>
701
-
702
- </DialogActions>}
703
- </form>
704
- );
705
- }
706
-
707
- export function yupToFormErrors(yupError: ValidationError): Record<string, any> {
708
- let errors: Record<string, any> = {};
709
- if (yupError.inner) {
710
- if (yupError.inner.length === 0) {
711
- return setIn(errors, yupError.path!, yupError.message);
712
- }
713
- for (const err of yupError.inner) {
714
- if (!getIn(errors, err.path!)) {
715
- errors = setIn(errors, err.path!, err.message);
716
- }
717
- }
718
- }
719
- return errors;
720
- }