@firecms/core 3.0.0-canary.5 → 3.0.0-canary.51
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 +2 -2
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
- package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
- package/dist/components/EntityPreview.d.ts +25 -7
- package/dist/components/EntityView.d.ts +11 -0
- package/dist/components/FieldCaption.d.ts +5 -0
- package/dist/components/FireCMSAppBar.d.ts +3 -2
- package/dist/components/HomePage/NavigationCard.d.ts +8 -0
- package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
- package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
- package/dist/components/HomePage/index.d.ts +3 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/common/types.d.ts +4 -6
- package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
- package/dist/components/index.d.ts +4 -2
- package/dist/contexts/AuthControllerContext.d.ts +1 -1
- package/dist/core/Drawer.d.ts +5 -12
- package/dist/core/DrawerNavigationItem.d.ts +9 -0
- package/dist/core/{EntityView.d.ts → EntityEditView.d.ts} +2 -2
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/core/Scaffold.d.ts +7 -10
- package/dist/core/index.d.ts +3 -4
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/form/components/StorageItemPreview.d.ts +3 -2
- package/dist/form/components/StorageUploadProgress.d.ts +1 -1
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
- package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
- package/dist/form/validation.d.ts +1 -1
- 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 +2 -2
- package/dist/hooks/data/useEntityFetch.d.ts +3 -3
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useBuildNavigationController.d.ts +6 -4
- package/dist/hooks/useProjectLog.d.ts +6 -2
- package/dist/hooks/useStorageSource.d.ts +2 -2
- package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
- package/dist/index.es.js +9644 -9122
- 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 -12
- package/dist/preview/PropertyPreview.d.ts +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +1 -4
- package/dist/preview/components/BooleanPreview.d.ts +5 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -7
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +37 -1
- package/dist/types/collections.d.ts +29 -5
- package/dist/types/datasource.d.ts +3 -6
- 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/entity_overrides.d.ts +6 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/navigation.d.ts +14 -13
- package/dist/types/permissions.d.ts +5 -1
- package/dist/types/plugins.d.ts +20 -20
- package/dist/types/properties.d.ts +4 -4
- package/dist/types/property_config.d.ts +2 -2
- 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 +9 -1
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icon_synonyms.d.ts +2 -4
- package/dist/util/icons.d.ts +8 -2
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +4 -2
- package/dist/util/resolutions.d.ts +9 -13
- package/dist/util/useTraceUpdate.d.ts +1 -0
- package/package.json +139 -119
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/DeleteEntityDialog.tsx +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +275 -278
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +44 -44
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +11 -6
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +560 -554
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
- package/src/components/EntityPreview.tsx +207 -70
- package/src/components/EntityView.tsx +84 -0
- package/src/components/FieldCaption.tsx +14 -0
- package/src/components/FireCMSAppBar.tsx +33 -11
- package/src/components/HomePage/DefaultHomePage.tsx +15 -11
- package/src/components/HomePage/NavigationCard.tsx +69 -0
- package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
- package/src/components/HomePage/index.tsx +3 -1
- package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
- package/src/components/ReferenceWidget.tsx +5 -5
- package/src/components/SearchIconsView.tsx +4 -4
- package/src/components/SelectableTable/SelectableTable.tsx +1 -1
- 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 +35 -24
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +28 -20
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
- package/src/components/common/types.tsx +4 -6
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
- package/src/components/index.tsx +4 -2
- package/src/contexts/AuthControllerContext.tsx +1 -1
- package/src/core/Drawer.tsx +78 -103
- package/src/core/DrawerNavigationItem.tsx +62 -0
- package/src/core/{EntityView.tsx → EntityEditView.tsx} +21 -40
- package/src/core/EntitySidePanel.tsx +2 -2
- package/src/core/FireCMS.tsx +22 -7
- package/src/core/NavigationRoutes.tsx +11 -4
- package/src/core/Scaffold.tsx +76 -61
- package/src/core/field_configs.tsx +1 -2
- package/src/core/index.tsx +3 -4
- package/src/form/EntityForm.tsx +42 -22
- package/src/form/PropertyFieldBinding.tsx +0 -2
- package/src/form/components/StorageItemPreview.tsx +5 -3
- package/src/form/components/StorageUploadProgress.tsx +7 -6
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +8 -12
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +15 -15
- package/src/form/field_bindings/MapFieldBinding.tsx +15 -15
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -0
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +14 -5
- package/src/form/field_bindings/TextFieldBinding.tsx +7 -5
- package/src/form/validation.ts +3 -4
- package/src/hooks/data/delete.ts +3 -3
- package/src/hooks/data/save.ts +2 -2
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useDataSource.tsx +8 -3
- package/src/hooks/data/useEntityFetch.tsx +4 -4
- package/src/hooks/index.tsx +3 -0
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +9 -10
- package/src/hooks/useBuildModeController.tsx +11 -5
- package/src/hooks/useBuildNavigationController.tsx +199 -81
- package/src/hooks/useProjectLog.tsx +17 -7
- package/src/hooks/useReferenceDialog.tsx +2 -2
- package/src/hooks/useStorageSource.tsx +7 -2
- package/src/hooks/useValidateAuthenticator.tsx +115 -0
- package/src/internal/useBuildDataSource.ts +42 -44
- package/src/internal/useBuildSideEntityController.tsx +86 -20
- package/src/preview/PropertyPreview.tsx +3 -14
- package/src/preview/PropertyPreviewProps.tsx +1 -11
- package/src/preview/components/BooleanPreview.tsx +19 -4
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +55 -147
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +0 -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 +0 -1
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +0 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
- package/src/types/analytics.ts +1 -0
- package/src/types/auth.tsx +50 -1
- package/src/types/collections.ts +33 -5
- package/src/types/datasource.ts +8 -5
- package/src/types/entities.ts +9 -1
- package/src/types/entity_actions.tsx +17 -0
- package/src/types/entity_callbacks.ts +2 -2
- package/src/types/entity_overrides.tsx +7 -0
- package/src/types/firecms.tsx +0 -1
- package/src/types/index.ts +2 -1
- package/src/types/navigation.ts +17 -16
- package/src/types/permissions.ts +6 -1
- package/src/types/plugins.tsx +26 -28
- package/src/types/properties.ts +8 -6
- package/src/types/property_config.tsx +2 -2
- package/src/types/roles.ts +41 -0
- package/src/types/side_entity_controller.tsx +1 -0
- package/src/types/storage.ts +12 -3
- package/src/types/user.ts +7 -0
- package/src/util/collections.ts +22 -0
- package/src/util/entities.ts +1 -1
- package/src/util/icon_list.ts +2 -2
- package/src/util/icon_synonyms.ts +4 -6
- package/src/util/icons.tsx +11 -3
- package/src/util/navigation_utils.ts +6 -6
- package/src/util/objects.ts +0 -14
- package/src/util/permissions.ts +11 -8
- package/src/util/references.ts +36 -5
- package/src/util/resolutions.ts +6 -24
- package/src/util/strings.ts +2 -2
- package/src/util/useTraceUpdate.tsx +2 -1
- package/dist/core/SideEntityView.d.ts +0 -7
- 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/components/HomePage/NavigationCollectionCard.tsx +0 -146
- package/src/core/SideEntityView.tsx +0 -38
- package/src/internal/useBuildCustomizationController.tsx +0 -5
- package/src/internal/useLocaleConfig.tsx +0 -18
- package/src/types/appcheck.ts +0 -29
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import { AuthController } from "../types";
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { AuthController, FireCMSPlugin } from "../types";
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_SERVER_DEV = "https://api-kdoe6pj3qq-ey.a.run.app";
|
|
5
5
|
export const DEFAULT_SERVER = "https://api-drplyi3b6q-ey.a.run.app";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
export type AccessResponse = {
|
|
8
|
+
blocked?: boolean;
|
|
9
|
+
message?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function makeRequest(authController: AuthController, pluginKeys: string[] | undefined) {
|
|
8
13
|
const firebaseToken = await authController.getAuthToken();
|
|
9
14
|
return fetch(DEFAULT_SERVER + "/access_log",
|
|
10
15
|
{
|
|
@@ -14,18 +19,23 @@ async function makeRequest(authController: AuthController) {
|
|
|
14
19
|
"Content-Type": "application/json",
|
|
15
20
|
Authorization: `Basic ${firebaseToken}`,
|
|
16
21
|
},
|
|
17
|
-
body: JSON.stringify({})
|
|
22
|
+
body: JSON.stringify({ plugins: pluginKeys })
|
|
18
23
|
})
|
|
19
24
|
.then(async (res) => {
|
|
25
|
+
return res.json();
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
export function useProjectLog(authController: AuthController
|
|
29
|
+
export function useProjectLog(authController: AuthController,
|
|
30
|
+
plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
|
|
31
|
+
const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
|
|
24
32
|
const accessedUserRef = useRef<string | null>(null);
|
|
33
|
+
const pluginKeys = plugins?.map(plugin => plugin.key);
|
|
25
34
|
useEffect(() => {
|
|
26
35
|
if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
|
|
27
|
-
makeRequest(authController);
|
|
36
|
+
makeRequest(authController, pluginKeys).then(setAccessResponse);
|
|
28
37
|
accessedUserRef.current = authController.user.uid;
|
|
29
38
|
}
|
|
30
|
-
}, [authController]);
|
|
39
|
+
}, [authController, pluginKeys]);
|
|
40
|
+
return accessResponse;
|
|
31
41
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useSideDialogsController } from "./useSideDialogsController";
|
|
2
|
-
import {
|
|
2
|
+
import { ReferenceSelectionInnerProps, ReferenceSelectionTable } from "../components";
|
|
3
3
|
import { useCallback } from "react";
|
|
4
4
|
import { useNavigationController } from "./useNavigationController";
|
|
5
5
|
|
|
@@ -27,7 +27,7 @@ export function useReferenceDialog<M extends Record<string, any>>(referenceDialo
|
|
|
27
27
|
if (!usedCollection)
|
|
28
28
|
usedCollection = navigation.getCollection(referenceDialogProps.path);
|
|
29
29
|
if (!usedCollection)
|
|
30
|
-
throw Error("Not able to resolve the collection in useReferenceDialog");
|
|
30
|
+
throw Error("Not able to resolve the collection in useReferenceDialog. Make sure a collection is registered in path " + referenceDialogProps.path);
|
|
31
31
|
sideDialogsController.open({
|
|
32
32
|
key: `reference_${referenceDialogProps.path}`,
|
|
33
33
|
component:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageSource } from "../types";
|
|
1
|
+
import { EntityCollection, StorageSource } from "../types";
|
|
2
2
|
import { StorageSourceContext } from "../contexts/StorageSourceContext";
|
|
3
3
|
import { useContext } from "react";
|
|
4
4
|
|
|
@@ -6,4 +6,9 @@ import { useContext } from "react";
|
|
|
6
6
|
* Use this hook to get the storage source being used
|
|
7
7
|
* @group Hooks and utilities
|
|
8
8
|
*/
|
|
9
|
-
export const useStorageSource = (): StorageSource =>
|
|
9
|
+
export const useStorageSource = (collection?: EntityCollection): StorageSource => {
|
|
10
|
+
const defaultStorageSource = useContext(StorageSourceContext);
|
|
11
|
+
if (collection?.overrides?.storageSource)
|
|
12
|
+
return collection.overrides.storageSource;
|
|
13
|
+
return defaultStorageSource;
|
|
14
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import equal from "react-fast-compare";
|
|
3
|
+
|
|
4
|
+
import { AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This hook is used internally for validating an authenticator.
|
|
8
|
+
*
|
|
9
|
+
* @param authController
|
|
10
|
+
* @param authentication
|
|
11
|
+
* @param storageSource
|
|
12
|
+
* @param dataSourceDelegate
|
|
13
|
+
*/
|
|
14
|
+
export function useValidateAuthenticator<UserType extends User = User, Controller extends AuthController<UserType> = AuthController<UserType>>({
|
|
15
|
+
disabled,
|
|
16
|
+
authController,
|
|
17
|
+
authenticator,
|
|
18
|
+
storageSource,
|
|
19
|
+
dataSourceDelegate
|
|
20
|
+
}:
|
|
21
|
+
{
|
|
22
|
+
disabled?: boolean,
|
|
23
|
+
authController: Controller,
|
|
24
|
+
authenticator?: boolean | Authenticator<UserType, Controller>,
|
|
25
|
+
dataSourceDelegate: DataSourceDelegate;
|
|
26
|
+
storageSource: StorageSource;
|
|
27
|
+
}): {
|
|
28
|
+
canAccessMainView: boolean,
|
|
29
|
+
authLoading: boolean,
|
|
30
|
+
notAllowedError: any,
|
|
31
|
+
authVerified: boolean,
|
|
32
|
+
} {
|
|
33
|
+
|
|
34
|
+
const authenticationEnabled = Boolean(authenticator);
|
|
35
|
+
|
|
36
|
+
const [authLoading, setAuthLoading] = useState<boolean>(authenticationEnabled);
|
|
37
|
+
const [notAllowedError, setNotAllowedError] = useState<any>(false);
|
|
38
|
+
const [authVerified, setAuthVerified] = useState<boolean>(!authenticationEnabled || Boolean(authController.loginSkipped));
|
|
39
|
+
|
|
40
|
+
const canAccessMainView = (authVerified) &&
|
|
41
|
+
(!authenticationEnabled || Boolean(authController.user) || Boolean(authController.loginSkipped)) &&
|
|
42
|
+
!notAllowedError;
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (authController.loginSkipped)
|
|
46
|
+
setAuthVerified(true);
|
|
47
|
+
}, [authController.loginSkipped]);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* We use this ref to check the authentication only if the user has
|
|
51
|
+
* changed.
|
|
52
|
+
*/
|
|
53
|
+
const checkedUserRef = useRef<User | undefined>();
|
|
54
|
+
|
|
55
|
+
const checkAuthentication = useCallback(async () => {
|
|
56
|
+
|
|
57
|
+
if (disabled) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (authController.initialLoading) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!authController.user && !authController.loginSkipped) {
|
|
66
|
+
checkedUserRef.current = undefined;
|
|
67
|
+
setAuthLoading(false);
|
|
68
|
+
setAuthVerified(false);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const delegateUser = authController.user;
|
|
73
|
+
console.debug("Checking authentication for user", delegateUser);
|
|
74
|
+
|
|
75
|
+
if (authenticator instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
|
|
76
|
+
setAuthLoading(true);
|
|
77
|
+
try {
|
|
78
|
+
const allowed = await authenticator({
|
|
79
|
+
user: delegateUser,
|
|
80
|
+
authController,
|
|
81
|
+
dataSourceDelegate,
|
|
82
|
+
storageSource
|
|
83
|
+
});
|
|
84
|
+
if (!allowed) {
|
|
85
|
+
authController.signOut();
|
|
86
|
+
setNotAllowedError(true);
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
setNotAllowedError(e);
|
|
90
|
+
authController.signOut();
|
|
91
|
+
}
|
|
92
|
+
setAuthLoading(false);
|
|
93
|
+
setAuthVerified(true);
|
|
94
|
+
checkedUserRef.current = delegateUser;
|
|
95
|
+
} else {
|
|
96
|
+
setAuthLoading(false);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!authController.initialLoading && !delegateUser) {
|
|
100
|
+
setAuthVerified(true);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}, [disabled, authController, authenticator, dataSourceDelegate, storageSource]);
|
|
104
|
+
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
checkAuthentication();
|
|
107
|
+
}, [checkAuthentication]);
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
canAccessMainView,
|
|
111
|
+
authLoading: authenticationEnabled && authLoading,
|
|
112
|
+
notAllowedError,
|
|
113
|
+
authVerified
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -5,12 +5,10 @@ import {
|
|
|
5
5
|
DeleteEntityProps,
|
|
6
6
|
Entity,
|
|
7
7
|
EntityCollection,
|
|
8
|
-
EntityReference,
|
|
9
8
|
EntityValues,
|
|
10
9
|
FetchCollectionProps,
|
|
11
10
|
FetchEntityProps,
|
|
12
11
|
FilterValues,
|
|
13
|
-
GeoPoint,
|
|
14
12
|
ListenCollectionProps,
|
|
15
13
|
ListenEntityProps,
|
|
16
14
|
NavigationController,
|
|
@@ -207,13 +205,10 @@ export function useBuildDataSource({
|
|
|
207
205
|
|
|
208
206
|
const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties;
|
|
209
207
|
|
|
210
|
-
const firestoreValues = cmsToDelegateModel(
|
|
208
|
+
const firestoreValues = delegate.cmsToDelegateModel(
|
|
211
209
|
values,
|
|
212
|
-
delegate.buildReference,
|
|
213
|
-
delegate.buildGeoPoint,
|
|
214
|
-
delegate.buildDate,
|
|
215
|
-
delegate.buildDeleteFieldValue
|
|
216
210
|
);
|
|
211
|
+
|
|
217
212
|
const updatedFirestoreValues: EntityValues<M> = properties
|
|
218
213
|
? updateDateAutoValues(
|
|
219
214
|
{
|
|
@@ -316,45 +311,48 @@ export function useBuildDataSource({
|
|
|
316
311
|
sortBy
|
|
317
312
|
}
|
|
318
313
|
)
|
|
319
|
-
}, [delegate.isFilterCombinationValid])
|
|
314
|
+
}, [delegate.isFilterCombinationValid]),
|
|
320
315
|
|
|
321
316
|
};
|
|
322
317
|
|
|
323
318
|
}
|
|
324
319
|
|
|
325
|
-
/**
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
|
|
320
|
+
// /**
|
|
321
|
+
// * Recursive function that converts Firestore data types into CMS or plain
|
|
322
|
+
// * JS types.
|
|
323
|
+
// * FireCMS uses Javascript dates internally instead of Firestore timestamps.
|
|
324
|
+
// * This makes it easier to interact with the rest of the libraries and
|
|
325
|
+
// * bindings.
|
|
326
|
+
// * Also, Firestore references are replaced with {@link EntityReference}
|
|
327
|
+
// * @param data
|
|
328
|
+
// * @param buildReference
|
|
329
|
+
// * @param buildGeoPoint
|
|
330
|
+
// * @param buildDate
|
|
331
|
+
// * @param buildDelete
|
|
332
|
+
// * @group Firestore
|
|
333
|
+
// */
|
|
334
|
+
// export function cmsToDelegateModel(data: any,
|
|
335
|
+
// buildReference: (reference: EntityReference) => any,
|
|
336
|
+
// buildGeoPoint: (geoPoint: GeoPoint) => any,
|
|
337
|
+
// buildDate: (date: Date) => any,
|
|
338
|
+
// buildDelete: () => any
|
|
339
|
+
// ): any {
|
|
340
|
+
// if (data === undefined) {
|
|
341
|
+
// return buildDelete();
|
|
342
|
+
// } else if (data === null) {
|
|
343
|
+
// return null;
|
|
344
|
+
// } else if (Array.isArray(data)) {
|
|
345
|
+
// return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
|
|
346
|
+
// } else if (data.isEntityReference && data.isEntityReference()) {
|
|
347
|
+
// return buildReference(data);
|
|
348
|
+
// } else if (data instanceof GeoPoint) {
|
|
349
|
+
// return buildGeoPoint(data);
|
|
350
|
+
// } else if (data instanceof Date) {
|
|
351
|
+
// return buildDate(data);
|
|
352
|
+
// } else if (data && typeof data === "object") {
|
|
353
|
+
// return Object.entries(data)
|
|
354
|
+
// .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
|
|
355
|
+
// .reduce((a, b) => ({ ...a, ...b }), {});
|
|
356
|
+
// }
|
|
357
|
+
// return data;
|
|
358
|
+
// }
|
|
@@ -3,13 +3,14 @@ import {
|
|
|
3
3
|
EntityCollection,
|
|
4
4
|
EntitySidePanelProps,
|
|
5
5
|
NavigationController,
|
|
6
|
+
ResolvedProperty,
|
|
6
7
|
SideDialogPanelProps,
|
|
7
8
|
SideDialogsController,
|
|
8
9
|
SideEntityController
|
|
9
10
|
} from "../types";
|
|
10
11
|
import { getNavigationEntriesFromPathInternal, NavigationViewInternal } from "../util/navigation_from_path";
|
|
11
12
|
import { useLocation } from "react-router-dom";
|
|
12
|
-
import { removeInitialAndTrailingSlashes, resolveDefaultSelectedView } from "../util";
|
|
13
|
+
import { removeInitialAndTrailingSlashes, resolveCollection, resolveDefaultSelectedView } from "../util";
|
|
13
14
|
import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, FORM_CONTAINER_WIDTH } from "./common";
|
|
14
15
|
import { useLargeLayout } from "../hooks";
|
|
15
16
|
import { EntitySidePanel } from "../core/EntitySidePanel";
|
|
@@ -19,8 +20,66 @@ const NEW_URL_HASH = "new";
|
|
|
19
20
|
export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: boolean): string {
|
|
20
21
|
if (small) return CONTAINER_FULL_WIDTH;
|
|
21
22
|
const mainViewSelected = !props.selectedSubPath;
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
let resolvedWidth: string | undefined;
|
|
24
|
+
if (props.width) {
|
|
25
|
+
resolvedWidth = typeof props.width === "number" ? `${props.width}px` : props.width;
|
|
26
|
+
} else if (props.collection?.sideDialogWidth) {
|
|
27
|
+
resolvedWidth = typeof props.collection.sideDialogWidth === "number" ? `${props.collection.sideDialogWidth}px` : props.collection.sideDialogWidth;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!mainViewSelected) {
|
|
31
|
+
return `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedWidth ?? FORM_CONTAINER_WIDTH})`
|
|
32
|
+
} else {
|
|
33
|
+
if (resolvedWidth) {
|
|
34
|
+
return resolvedWidth
|
|
35
|
+
} else if (!props.collection) {
|
|
36
|
+
return FORM_CONTAINER_WIDTH;
|
|
37
|
+
} else {
|
|
38
|
+
return calculateCollectionDesiredWidth(props.collection);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const collectionViewWidthCache: { [key: string]: string } = {};
|
|
44
|
+
|
|
45
|
+
function calculateCollectionDesiredWidth(collection: EntityCollection<any>): string {
|
|
46
|
+
if (collectionViewWidthCache[collection.id]) {
|
|
47
|
+
return collectionViewWidthCache[collection.id];
|
|
48
|
+
}
|
|
49
|
+
const resolvedCollection = resolveCollection({
|
|
50
|
+
collection,
|
|
51
|
+
path: "__ignored"
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
let result = FORM_CONTAINER_WIDTH
|
|
55
|
+
if (resolvedCollection?.properties) {
|
|
56
|
+
const values = Object.values(resolvedCollection.properties).map((p: ResolvedProperty) => getNestedPropertiesDepth(p));
|
|
57
|
+
const maxDepth = Math.max(...values);
|
|
58
|
+
if (maxDepth < 3) {
|
|
59
|
+
result = FORM_CONTAINER_WIDTH;
|
|
60
|
+
} else {
|
|
61
|
+
result = 768 + 32 * (maxDepth - 2) + "px";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
collectionViewWidthCache[collection.id] = result;
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getNestedPropertiesDepth(property: ResolvedProperty, accumulator: number = 0): number {
|
|
69
|
+
if (property.dataType === "map" && property.properties) {
|
|
70
|
+
const values = Object.values(property.properties).flatMap((property) => getNestedPropertiesDepth(property, accumulator + 1));
|
|
71
|
+
return Math.max(...values);
|
|
72
|
+
} else if (property.dataType === "array" && property.oneOf) {
|
|
73
|
+
return accumulator + 3;
|
|
74
|
+
} else if (property.dataType === "array" && property.of) {
|
|
75
|
+
if (Array.isArray(property.of)) {
|
|
76
|
+
return Math.max(...property.of.map((p) => getNestedPropertiesDepth(p, accumulator + 1)));
|
|
77
|
+
} else {
|
|
78
|
+
return getNestedPropertiesDepth(property.of, accumulator + 1);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
return accumulator + 1;
|
|
82
|
+
}
|
|
24
83
|
}
|
|
25
84
|
|
|
26
85
|
export const useBuildSideEntityController = (navigation: NavigationController,
|
|
@@ -34,23 +93,26 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
34
93
|
// only on initialisation, create panels from URL
|
|
35
94
|
useEffect(() => {
|
|
36
95
|
if (!navigation.loading && !initialised.current) {
|
|
96
|
+
console.debug("Initialising side entity controller");
|
|
37
97
|
if (navigation.isUrlCollectionPath(location.pathname)) {
|
|
38
98
|
const newFlag = location.hash === `#${NEW_URL_HASH}`;
|
|
39
99
|
const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
|
|
40
100
|
const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections ?? [], newFlag);
|
|
41
101
|
for (let i = 0; i < panelsFromUrl.length; i++) {
|
|
42
|
-
const
|
|
102
|
+
const props = panelsFromUrl[i];
|
|
43
103
|
setTimeout(() => {
|
|
44
104
|
if (i === 0)
|
|
45
|
-
sideDialogsController.replace(propsToSidePanel(
|
|
105
|
+
sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
|
|
46
106
|
else
|
|
47
|
-
sideDialogsController.open(propsToSidePanel(
|
|
107
|
+
sideDialogsController.open(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout))
|
|
48
108
|
}, 1);
|
|
49
109
|
}
|
|
110
|
+
} else {
|
|
111
|
+
// console.warn("Location path is not a collection path");
|
|
50
112
|
}
|
|
51
113
|
initialised.current = true;
|
|
52
114
|
}
|
|
53
|
-
}, [location, navigation, sideDialogsController, smallLayout]);
|
|
115
|
+
}, [location, navigation.loading, navigation.isUrlCollectionPath, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout, navigation]);
|
|
54
116
|
|
|
55
117
|
const close = useCallback(() => {
|
|
56
118
|
sideDialogsController.close();
|
|
@@ -73,9 +135,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
73
135
|
sideDialogsController.open(propsToSidePanel({
|
|
74
136
|
selectedSubPath: defaultSelectedView,
|
|
75
137
|
...props,
|
|
76
|
-
}, navigation, smallLayout));
|
|
138
|
+
}, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
|
|
77
139
|
|
|
78
|
-
}, [sideDialogsController, navigation, smallLayout]);
|
|
140
|
+
}, [sideDialogsController, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout]);
|
|
79
141
|
|
|
80
142
|
const replace = useCallback((props: EntitySidePanelProps<any>) => {
|
|
81
143
|
|
|
@@ -83,9 +145,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
83
145
|
throw Error("If you want to copy an entity you need to provide an entityId");
|
|
84
146
|
}
|
|
85
147
|
|
|
86
|
-
sideDialogsController.replace(propsToSidePanel(props, navigation, smallLayout));
|
|
148
|
+
sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
|
|
87
149
|
|
|
88
|
-
}, [navigation, sideDialogsController, smallLayout]);
|
|
150
|
+
}, [navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout]);
|
|
89
151
|
|
|
90
152
|
return {
|
|
91
153
|
close,
|
|
@@ -116,7 +178,8 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
|
|
|
116
178
|
sidePanels.push({
|
|
117
179
|
path: navigationEntry.path,
|
|
118
180
|
entityId: navigationEntry.entityId,
|
|
119
|
-
copy: false
|
|
181
|
+
copy: false,
|
|
182
|
+
width: navigationEntry.parentCollection?.sideDialogWidth
|
|
120
183
|
}
|
|
121
184
|
);
|
|
122
185
|
} else if (navigationEntry.type === "custom_view") {
|
|
@@ -146,27 +209,30 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
|
|
|
146
209
|
return sidePanels;
|
|
147
210
|
}
|
|
148
211
|
|
|
149
|
-
const propsToSidePanel = (props: EntitySidePanelProps<any>,
|
|
212
|
+
const propsToSidePanel = (props: EntitySidePanelProps<any>,
|
|
213
|
+
buildUrlCollectionPath: (path: string) => string,
|
|
214
|
+
resolveAliasesFrom: (pathWithAliases: string) => string,
|
|
215
|
+
smallLayout: boolean): SideDialogPanelProps => {
|
|
150
216
|
|
|
151
217
|
const collectionPath = removeInitialAndTrailingSlashes(props.path);
|
|
152
218
|
|
|
153
219
|
const newPath = props.entityId
|
|
154
|
-
?
|
|
155
|
-
:
|
|
156
|
-
const resolvedPath =
|
|
220
|
+
? buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
|
|
221
|
+
: buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
|
|
222
|
+
const resolvedPath = resolveAliasesFrom(props.path);
|
|
157
223
|
|
|
158
224
|
const resolvedPanelProps: EntitySidePanelProps<any> = {
|
|
159
225
|
...props,
|
|
160
|
-
path: resolvedPath
|
|
226
|
+
path: resolvedPath,
|
|
161
227
|
};
|
|
162
228
|
|
|
163
|
-
return
|
|
229
|
+
return {
|
|
164
230
|
key: `${props.path}/${props.entityId}`,
|
|
165
231
|
component: <EntitySidePanel {...resolvedPanelProps}/>,
|
|
166
232
|
urlPath: newPath,
|
|
167
|
-
parentUrlPath:
|
|
233
|
+
parentUrlPath: buildUrlCollectionPath(collectionPath),
|
|
168
234
|
width: getEntityViewWidth(props, smallLayout),
|
|
169
235
|
onClose: props.onClose
|
|
170
|
-
}
|
|
236
|
+
};
|
|
171
237
|
}
|
|
172
238
|
;
|
|
@@ -53,7 +53,6 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
53
53
|
const property = resolveProperty({
|
|
54
54
|
propertyKey,
|
|
55
55
|
propertyOrBuilder: inputProperty,
|
|
56
|
-
propertyValue: value,
|
|
57
56
|
fields: customizationController.propertyConfigs
|
|
58
57
|
});
|
|
59
58
|
|
|
@@ -92,7 +91,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
92
91
|
size={props.size}
|
|
93
92
|
storagePathOrDownloadUrl={value}/>;
|
|
94
93
|
} else if (stringProperty.markdown) {
|
|
95
|
-
content = <Markdown source={value}/>;
|
|
94
|
+
content = <Markdown source={value} size={"small"}/>;
|
|
96
95
|
} else {
|
|
97
96
|
content = <StringPropertyPreview {...props}
|
|
98
97
|
property={stringProperty}
|
|
@@ -113,17 +112,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
113
112
|
content = <ArrayPropertyPreview {...props}
|
|
114
113
|
value={value}
|
|
115
114
|
property={property as ResolvedArrayProperty}/>;
|
|
116
|
-
}
|
|
117
|
-
// else if (arrayProperty.of.dataType === "map") {
|
|
118
|
-
// content =
|
|
119
|
-
// <ArrayOfMapsPreview propertyKey={propertyKey}
|
|
120
|
-
// property={property as ResolvedArrayProperty}
|
|
121
|
-
// value={value as Record<string, any>[]} // This might be wrong
|
|
122
|
-
// entity={entity}
|
|
123
|
-
// size={size}
|
|
124
|
-
// />;
|
|
125
|
-
// }
|
|
126
|
-
else if (arrayProperty.of.dataType === "reference") {
|
|
115
|
+
} else if (arrayProperty.of.dataType === "reference") {
|
|
127
116
|
content = <ArrayOfReferencesPreview {...props}
|
|
128
117
|
value={value}
|
|
129
118
|
property={property as ResolvedArrayProperty}/>;
|
|
@@ -195,7 +184,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
195
184
|
|
|
196
185
|
} else if (property.dataType === "boolean") {
|
|
197
186
|
if (typeof value === "boolean") {
|
|
198
|
-
content = <BooleanPreview value={value}/>;
|
|
187
|
+
content = <BooleanPreview value={value} size={size} property={property}/>;
|
|
199
188
|
} else {
|
|
200
189
|
content = buildWrongValueType(propertyKey, property.dataType, value);
|
|
201
190
|
}
|
|
@@ -8,7 +8,7 @@ export type PreviewSize = "medium" | "small" | "tiny";
|
|
|
8
8
|
/**
|
|
9
9
|
* @group Preview components
|
|
10
10
|
*/
|
|
11
|
-
export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any
|
|
11
|
+
export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any> {
|
|
12
12
|
/**
|
|
13
13
|
* Name of the property
|
|
14
14
|
*/
|
|
@@ -24,11 +24,6 @@ export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any
|
|
|
24
24
|
*/
|
|
25
25
|
property: Property<T> | ResolvedProperty<T>;
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
* Click handler
|
|
29
|
-
*/
|
|
30
|
-
// onClick?: () => void;
|
|
31
|
-
|
|
32
27
|
/**
|
|
33
28
|
* Desired size of the preview, depending on the context.
|
|
34
29
|
*/
|
|
@@ -51,9 +46,4 @@ export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any
|
|
|
51
46
|
*/
|
|
52
47
|
customProps?: CustomProps;
|
|
53
48
|
|
|
54
|
-
/**
|
|
55
|
-
* Entity this property refers to
|
|
56
|
-
*/
|
|
57
|
-
// entity?: Entity<M>;
|
|
58
|
-
|
|
59
49
|
}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Checkbox } from "@firecms/ui";
|
|
2
|
+
import { Checkbox, cn } from "@firecms/ui";
|
|
3
|
+
import { PreviewSize } from "../PropertyPreviewProps";
|
|
4
|
+
import { Property } from "../../types";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* @group Preview components
|
|
6
8
|
*/
|
|
7
|
-
export function BooleanPreview({
|
|
8
|
-
|
|
9
|
+
export function BooleanPreview({
|
|
10
|
+
value,
|
|
11
|
+
size,
|
|
12
|
+
property
|
|
13
|
+
}: {
|
|
14
|
+
value: boolean,
|
|
15
|
+
size: PreviewSize,
|
|
16
|
+
property: Property,
|
|
9
17
|
}): React.ReactElement {
|
|
10
|
-
return <
|
|
18
|
+
return <div className={"flex flex-row gap-2 items-center"}>
|
|
19
|
+
<Checkbox checked={value}
|
|
20
|
+
padding={false}
|
|
21
|
+
size={size}
|
|
22
|
+
color={"secondary"}/>
|
|
23
|
+
{property.name && <span
|
|
24
|
+
className={cn("text-text-secondary dark:text-text-secondary-dark", size === "tiny" ? "text-sm" : "")}>{property.name}</span>}
|
|
25
|
+
</div>;
|
|
11
26
|
}
|