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