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