@firecms/collection_editor 3.0.0-beta.5 → 3.0.0-beta.6
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/LICENSE +113 -21
- package/dist/ConfigControllerProvider.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +1721 -1712
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +1 -1
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +1 -1
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
- package/dist/useCollectionEditorPlugin.d.ts +6 -9
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +14 -14
- package/src/ConfigControllerProvider.tsx +28 -34
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +1 -1
- package/src/ui/EditorCollectionAction.tsx +0 -51
- package/src/ui/EditorCollectionActionStart.tsx +87 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +1 -1
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +26 -11
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +2 -2
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +2 -2
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +1 -1
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +23 -8
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +9 -7
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +9 -3
- package/src/useCollectionEditorPlugin.tsx +27 -26
- package/src/utils/collections.ts +30 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
|
@@ -25,7 +25,7 @@ export interface CollectionEditorDialogProps {
|
|
|
25
25
|
icon: React.ReactNode;
|
|
26
26
|
};
|
|
27
27
|
pathSuggestions?: (path?: string) => Promise<string[]>;
|
|
28
|
-
getUser
|
|
28
|
+
getUser?: (uid: string) => User | null;
|
|
29
29
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
30
30
|
parentCollection?: PersistedCollection;
|
|
31
31
|
}
|
|
@@ -4,7 +4,7 @@ export declare function CollectionEditorWelcomeView({ path, pathSuggestions, par
|
|
|
4
4
|
path: string;
|
|
5
5
|
pathSuggestions?: (path: string) => Promise<string[]>;
|
|
6
6
|
parentCollection?: EntityCollection;
|
|
7
|
-
onContinue: (importData?: object[]) => void;
|
|
7
|
+
onContinue: (importData?: object[], propertiesOrder?: string[]) => void;
|
|
8
8
|
existingCollectionPaths?: string[];
|
|
9
9
|
}): import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
export declare function TemplateButton({ title, subtitle, icon, onClick }: {
|
|
@@ -9,7 +9,7 @@ type CollectionEditorFormProps = {
|
|
|
9
9
|
setDirty?: (dirty: boolean) => void;
|
|
10
10
|
reservedGroups?: string[];
|
|
11
11
|
extraIcon: React.ReactNode;
|
|
12
|
-
getUser
|
|
12
|
+
getUser?: (uid: string) => User | null;
|
|
13
13
|
getData?: () => Promise<object[]>;
|
|
14
14
|
doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
|
|
15
15
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
@@ -4,17 +4,17 @@ import { DraggableProvided } from "@hello-pangea/dnd";
|
|
|
4
4
|
export declare const PropertyTree: React.MemoExoticComponent<(<M extends {
|
|
5
5
|
[Key: string]: CMSType;
|
|
6
6
|
}>({ namespace, selectedPropertyKey, onPropertyClick, properties, propertiesOrder: propertiesOrderProp, additionalFields, errors, onPropertyMove, onPropertyRemove, className, inferredPropertyKeys, collectionEditable }: {
|
|
7
|
-
namespace?: string
|
|
8
|
-
selectedPropertyKey?: string
|
|
9
|
-
onPropertyClick?: (
|
|
7
|
+
namespace?: string;
|
|
8
|
+
selectedPropertyKey?: string;
|
|
9
|
+
onPropertyClick?: (propertyKey: string, namespace?: string) => void;
|
|
10
10
|
properties: PropertiesOrBuilders<M>;
|
|
11
|
-
propertiesOrder?: string[]
|
|
12
|
-
additionalFields?: AdditionalFieldDelegate<M
|
|
11
|
+
propertiesOrder?: string[];
|
|
12
|
+
additionalFields?: AdditionalFieldDelegate<M>[];
|
|
13
13
|
errors: Record<string, any>;
|
|
14
|
-
onPropertyMove?: (
|
|
15
|
-
onPropertyRemove?: (
|
|
16
|
-
className?: string
|
|
17
|
-
inferredPropertyKeys?: string[]
|
|
14
|
+
onPropertyMove?: (propertiesOrder: string[], namespace?: string) => void;
|
|
15
|
+
onPropertyRemove?: (propertyKey: string, namespace?: string) => void;
|
|
16
|
+
className?: string;
|
|
17
|
+
inferredPropertyKeys?: string[];
|
|
18
18
|
collectionEditable: boolean;
|
|
19
19
|
}) => import("react/jsx-runtime").JSX.Element)>;
|
|
20
20
|
export declare function PropertyTreeEntry({ propertyKey, namespace, propertyOrBuilder, additionalField, provided, selectedPropertyKey, errors, onPropertyClick, onPropertyMove, onPropertyRemove, inferredPropertyKeys, collectionEditable }: {
|
|
@@ -7,6 +7,6 @@ export declare function SubcollectionsEditTab({ collection, parentCollection, co
|
|
|
7
7
|
parentCollection?: EntityCollection;
|
|
8
8
|
configController: CollectionsConfigController;
|
|
9
9
|
collectionInference?: CollectionInference;
|
|
10
|
-
getUser
|
|
10
|
+
getUser?: (uid: string) => User | null;
|
|
11
11
|
parentCollectionIds?: string[];
|
|
12
12
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -18,19 +18,18 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
18
18
|
* names when creating collections.
|
|
19
19
|
* e.g. ["admin"]
|
|
20
20
|
*/
|
|
21
|
-
reservedGroups
|
|
21
|
+
reservedGroups?: string[];
|
|
22
22
|
extraView?: {
|
|
23
23
|
View: React.ComponentType<{
|
|
24
24
|
path: string;
|
|
25
25
|
}>;
|
|
26
26
|
icon: React.ReactNode;
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
getPathSuggestions?: (path?: string) => Promise<string[]>;
|
|
29
29
|
collectionInference?: CollectionInference;
|
|
30
30
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
31
|
-
getUser
|
|
31
|
+
getUser?: (uid: string) => UserType | null;
|
|
32
32
|
onAnalyticsEvent?: (event: string, params?: object) => void;
|
|
33
|
-
introMode?: "new_project" | "existing_project";
|
|
34
33
|
}
|
|
35
34
|
/**
|
|
36
35
|
* Use this hook to initialise the Collection Editor plugin.
|
|
@@ -39,11 +38,9 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
39
38
|
* @param configPermissions
|
|
40
39
|
* @param reservedGroups
|
|
41
40
|
* @param extraView
|
|
42
|
-
* @param
|
|
41
|
+
* @param getPathsSuggestions
|
|
43
42
|
* @param getUser
|
|
44
43
|
* @param collectionInference
|
|
45
44
|
*/
|
|
46
|
-
export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController,
|
|
47
|
-
export declare function IntroWidget({
|
|
48
|
-
introMode?: "new_project" | "existing_project";
|
|
49
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController, configPermissions, reservedGroups, extraView, getPathSuggestions, getUser, collectionInference, getData, onAnalyticsEvent }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection>;
|
|
46
|
+
export declare function IntroWidget({}: {}): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { EntityCollection, ModifyCollectionProps } from "@firecms/core";
|
|
2
|
+
import { PersistedCollection } from "../types/persisted_collection";
|
|
3
|
+
/**
|
|
4
|
+
* Function in charge of merging collections defined in code with those stored in the backend.
|
|
5
|
+
*/
|
|
6
|
+
export declare const mergeCollections: (baseCollections: EntityCollection[], backendCollections: PersistedCollection[], modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void) => EntityCollection<any, any>[];
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/collection_editor",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-beta.
|
|
4
|
+
"version": "3.0.0-beta.6",
|
|
5
5
|
"main": "./dist/index.umd.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"source": "src/index.ts",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@firecms/data_import_export": "^3.0.0-beta.
|
|
11
|
-
"@firecms/formex": "^3.0.0-beta.
|
|
12
|
-
"@firecms/schema_inference": "^3.0.0-beta.
|
|
13
|
-
"@firecms/ui": "^3.0.0-beta.
|
|
10
|
+
"@firecms/data_import_export": "^3.0.0-beta.6",
|
|
11
|
+
"@firecms/formex": "^3.0.0-beta.6",
|
|
12
|
+
"@firecms/schema_inference": "^3.0.0-beta.6",
|
|
13
|
+
"@firecms/ui": "^3.0.0-beta.6",
|
|
14
14
|
"json5": "^2.2.3",
|
|
15
15
|
"prism-react-renderer": "^2.3.1"
|
|
16
16
|
},
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@jest/globals": "^29.7.0",
|
|
57
|
-
"@types/react": "^18.2.
|
|
58
|
-
"@types/react-dom": "^18.2.
|
|
59
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
60
|
-
"@typescript-eslint/parser": "^7.
|
|
57
|
+
"@types/react": "^18.2.79",
|
|
58
|
+
"@types/react-dom": "^18.2.25",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
60
|
+
"@typescript-eslint/parser": "^7.7.0",
|
|
61
61
|
"@vitejs/plugin-react": "^4.2.1",
|
|
62
62
|
"eslint": "^8.57.0",
|
|
63
63
|
"eslint-config-standard": "^17.1.0",
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
"eslint-plugin-react": "^7.34.1",
|
|
68
68
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
69
69
|
"jest": "^29.7.0",
|
|
70
|
-
"react-router": "^6.22.
|
|
71
|
-
"react-router-dom": "^6.22.
|
|
70
|
+
"react-router": "^6.22.3",
|
|
71
|
+
"react-router-dom": "^6.22.3",
|
|
72
72
|
"ts-jest": "^29.1.2",
|
|
73
|
-
"typescript": "^5.4.
|
|
74
|
-
"vite": "^5.2.
|
|
73
|
+
"typescript": "^5.4.5",
|
|
74
|
+
"vite": "^5.2.9",
|
|
75
75
|
"vite-plugin-fonts": "^0.7.0"
|
|
76
76
|
},
|
|
77
77
|
"files": [
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"publishConfig": {
|
|
82
82
|
"access": "public"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "ffcbddbd73211bb644ff7503c2c62790bff6202a"
|
|
85
85
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { PropsWithChildren, useCallback
|
|
1
|
+
import React, { PropsWithChildren, useCallback } from "react";
|
|
2
2
|
import equal from "react-fast-compare"
|
|
3
3
|
|
|
4
4
|
import { CollectionsConfigController } from "./types/config_controller";
|
|
@@ -48,9 +48,9 @@ export interface ConfigControllerProviderProps {
|
|
|
48
48
|
icon: React.ReactNode
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
getPathSuggestions?: (path?: string) => Promise<string[]>;
|
|
52
52
|
|
|
53
|
-
getUser
|
|
53
|
+
getUser?: (uid: string) => User | null
|
|
54
54
|
|
|
55
55
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
56
56
|
|
|
@@ -66,7 +66,7 @@ export const ConfigControllerProvider = React.memo(
|
|
|
66
66
|
reservedGroups,
|
|
67
67
|
collectionInference,
|
|
68
68
|
extraView,
|
|
69
|
-
|
|
69
|
+
getPathSuggestions,
|
|
70
70
|
getUser,
|
|
71
71
|
getData,
|
|
72
72
|
onAnalyticsEvent
|
|
@@ -77,20 +77,6 @@ export const ConfigControllerProvider = React.memo(
|
|
|
77
77
|
const snackbarController = useSnackbarController();
|
|
78
78
|
const { propertyConfigs } = useCustomizationController();
|
|
79
79
|
|
|
80
|
-
const {
|
|
81
|
-
collections
|
|
82
|
-
} = navigation;
|
|
83
|
-
const existingPaths = (collections ?? []).map(col => col.path.trim().toLowerCase());
|
|
84
|
-
|
|
85
|
-
const [rootPathSuggestions, setRootPathSuggestions] = React.useState<string[] | undefined>();
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
if (pathSuggestions) {
|
|
88
|
-
pathSuggestions().then((paths) => {
|
|
89
|
-
setRootPathSuggestions(paths.filter(p => !existingPaths.includes(p.trim().toLowerCase())));
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}, [pathSuggestions]);
|
|
93
|
-
|
|
94
80
|
const [currentDialog, setCurrentDialog] = React.useState<{
|
|
95
81
|
isNewCollection: boolean,
|
|
96
82
|
parentCollection?: PersistedCollection,
|
|
@@ -135,7 +121,10 @@ export const ConfigControllerProvider = React.memo(
|
|
|
135
121
|
parentCollection?: PersistedCollection
|
|
136
122
|
}) => {
|
|
137
123
|
console.debug("Edit collection", id, fullPath, parentCollectionIds, parentCollection);
|
|
138
|
-
onAnalyticsEvent?.("edit_collection", {
|
|
124
|
+
onAnalyticsEvent?.("edit_collection", {
|
|
125
|
+
id,
|
|
126
|
+
fullPath
|
|
127
|
+
});
|
|
139
128
|
setCurrentDialog({
|
|
140
129
|
editedCollectionId: id,
|
|
141
130
|
fullPath,
|
|
@@ -162,7 +151,10 @@ export const ConfigControllerProvider = React.memo(
|
|
|
162
151
|
collection: PersistedCollection,
|
|
163
152
|
}) => {
|
|
164
153
|
console.debug("Edit property", propertyKey, property, editedCollectionId, currentPropertiesOrder, parentCollectionIds, collection);
|
|
165
|
-
onAnalyticsEvent?.("edit_property", {
|
|
154
|
+
onAnalyticsEvent?.("edit_property", {
|
|
155
|
+
propertyKey,
|
|
156
|
+
editedCollectionId
|
|
157
|
+
});
|
|
166
158
|
// namespace is all the path until the last dot
|
|
167
159
|
const namespace = propertyKey && propertyKey.includes(".")
|
|
168
160
|
? propertyKey.substring(0, propertyKey.lastIndexOf("."))
|
|
@@ -175,7 +167,7 @@ export const ConfigControllerProvider = React.memo(
|
|
|
175
167
|
property,
|
|
176
168
|
namespace,
|
|
177
169
|
currentPropertiesOrder,
|
|
178
|
-
editedCollectionId
|
|
170
|
+
editedCollectionId,
|
|
179
171
|
parentCollectionIds,
|
|
180
172
|
collectionEditable: collection?.editable ?? false
|
|
181
173
|
});
|
|
@@ -198,8 +190,20 @@ export const ConfigControllerProvider = React.memo(
|
|
|
198
190
|
redirect: boolean,
|
|
199
191
|
sourceClick?: string
|
|
200
192
|
}) => {
|
|
201
|
-
console.debug("Create collection", {
|
|
202
|
-
|
|
193
|
+
console.debug("Create collection", {
|
|
194
|
+
parentCollectionIds,
|
|
195
|
+
parentCollection,
|
|
196
|
+
initialValues,
|
|
197
|
+
redirect,
|
|
198
|
+
sourceClick
|
|
199
|
+
});
|
|
200
|
+
onAnalyticsEvent?.("create_collection", {
|
|
201
|
+
parentCollectionIds,
|
|
202
|
+
parentCollection,
|
|
203
|
+
initialValues,
|
|
204
|
+
redirect,
|
|
205
|
+
sourceClick
|
|
206
|
+
});
|
|
203
207
|
setCurrentDialog({
|
|
204
208
|
isNewCollection: true,
|
|
205
209
|
parentCollectionIds,
|
|
@@ -209,16 +213,6 @@ export const ConfigControllerProvider = React.memo(
|
|
|
209
213
|
});
|
|
210
214
|
}, []);
|
|
211
215
|
|
|
212
|
-
const getPathSuggestions = !pathSuggestions
|
|
213
|
-
? undefined
|
|
214
|
-
: (path?: string) => {
|
|
215
|
-
if (!path && rootPathSuggestions)
|
|
216
|
-
return Promise.resolve(rootPathSuggestions);
|
|
217
|
-
else {
|
|
218
|
-
return pathSuggestions?.(path);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
216
|
return (
|
|
223
217
|
<ConfigControllerContext.Provider value={collectionConfigController}>
|
|
224
218
|
<CollectionEditorContext.Provider
|
|
@@ -227,7 +221,7 @@ export const ConfigControllerProvider = React.memo(
|
|
|
227
221
|
createCollection,
|
|
228
222
|
editProperty,
|
|
229
223
|
configPermissions: configPermissions ?? defaultConfigPermissions,
|
|
230
|
-
|
|
224
|
+
getPathSuggestions
|
|
231
225
|
}}>
|
|
232
226
|
|
|
233
227
|
{children}
|
package/src/index.ts
CHANGED
|
@@ -23,8 +23,6 @@ export function EditorCollectionAction({
|
|
|
23
23
|
const authController = useAuthController();
|
|
24
24
|
const navigationController = useNavigationController();
|
|
25
25
|
const collectionEditorController = useCollectionEditorController();
|
|
26
|
-
const configController = useCollectionsConfigController();
|
|
27
|
-
const snackbarController = useSnackbarController();
|
|
28
26
|
|
|
29
27
|
const parentCollection = navigationController.getCollectionFromIds(parentCollectionIds);
|
|
30
28
|
|
|
@@ -35,54 +33,6 @@ export function EditorCollectionAction({
|
|
|
35
33
|
}).editCollections
|
|
36
34
|
: true;
|
|
37
35
|
|
|
38
|
-
let saveDefaultFilterButton = null;
|
|
39
|
-
if (!equal(getObjectOrNull(tableController.filterValues), getObjectOrNull(collection.initialFilter)) ||
|
|
40
|
-
!equal(getObjectOrNull(tableController.sortBy), getObjectOrNull(collection.initialSort))) {
|
|
41
|
-
saveDefaultFilterButton = <>
|
|
42
|
-
{(collection.initialFilter || collection.initialSort) && <Tooltip
|
|
43
|
-
title={"Reset to default filter and sort"}>
|
|
44
|
-
<Button
|
|
45
|
-
color={"primary"}
|
|
46
|
-
size={"small"}
|
|
47
|
-
variant={"text"}
|
|
48
|
-
onClick={() => {
|
|
49
|
-
tableController.clearFilter?.();
|
|
50
|
-
if (collection?.initialFilter)
|
|
51
|
-
tableController.setFilterValues?.(collection?.initialFilter);
|
|
52
|
-
if (collection?.initialSort)
|
|
53
|
-
tableController.setSortBy?.(collection?.initialSort);
|
|
54
|
-
}}>
|
|
55
|
-
<UndoIcon/>
|
|
56
|
-
</Button>
|
|
57
|
-
</Tooltip>}
|
|
58
|
-
|
|
59
|
-
<Tooltip
|
|
60
|
-
title={tableController.sortBy || tableController.filterValues ? "Save default filter and sort" : "Clear default filter and sort"}>
|
|
61
|
-
<Button
|
|
62
|
-
color={"primary"}
|
|
63
|
-
size={"small"}
|
|
64
|
-
variant={"outlined"}
|
|
65
|
-
onClick={() => configController
|
|
66
|
-
?.saveCollection({
|
|
67
|
-
id: collection.id,
|
|
68
|
-
parentCollectionIds,
|
|
69
|
-
collectionData: mergeDeep(collection as PersistedCollection,
|
|
70
|
-
{
|
|
71
|
-
initialFilter: tableController.filterValues ?? null,
|
|
72
|
-
initialSort: tableController.sortBy ?? null
|
|
73
|
-
})
|
|
74
|
-
}).then(() => {
|
|
75
|
-
snackbarController.open({
|
|
76
|
-
type: "success",
|
|
77
|
-
message: "Default config saved"
|
|
78
|
-
});
|
|
79
|
-
})}>
|
|
80
|
-
<SaveIcon/>
|
|
81
|
-
</Button>
|
|
82
|
-
</Tooltip>
|
|
83
|
-
</>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
36
|
const editorButton = <Tooltip
|
|
87
37
|
title={canEditCollection ? "Edit collection" : "You don't have permissions to edit this collection"}>
|
|
88
38
|
<IconButton
|
|
@@ -96,7 +46,6 @@ export function EditorCollectionAction({
|
|
|
96
46
|
</Tooltip>;
|
|
97
47
|
|
|
98
48
|
return <>
|
|
99
|
-
{canEditCollection && saveDefaultFilterButton}
|
|
100
49
|
{editorButton}
|
|
101
50
|
</>
|
|
102
51
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import equal from "react-fast-compare"
|
|
2
|
+
|
|
3
|
+
import { CollectionActionsProps, mergeDeep, useAuthController, useSnackbarController } from "@firecms/core";
|
|
4
|
+
import { Button, SaveIcon, Tooltip, UndoIcon, } from "@firecms/ui";
|
|
5
|
+
|
|
6
|
+
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
7
|
+
import { useCollectionsConfigController } from "../useCollectionsConfigController";
|
|
8
|
+
import { PersistedCollection } from "../types/persisted_collection";
|
|
9
|
+
|
|
10
|
+
export function EditorCollectionActionStart({
|
|
11
|
+
path: fullPath,
|
|
12
|
+
parentCollectionIds,
|
|
13
|
+
collection,
|
|
14
|
+
tableController
|
|
15
|
+
}: CollectionActionsProps) {
|
|
16
|
+
|
|
17
|
+
const authController = useAuthController();
|
|
18
|
+
const collectionEditorController = useCollectionEditorController();
|
|
19
|
+
const configController = useCollectionsConfigController();
|
|
20
|
+
const snackbarController = useSnackbarController();
|
|
21
|
+
|
|
22
|
+
const canEditCollection = collectionEditorController.configPermissions
|
|
23
|
+
? collectionEditorController.configPermissions({
|
|
24
|
+
user: authController.user,
|
|
25
|
+
collection
|
|
26
|
+
}).editCollections
|
|
27
|
+
: true;
|
|
28
|
+
|
|
29
|
+
let saveDefaultFilterButton = null;
|
|
30
|
+
if (!equal(getObjectOrNull(tableController.filterValues), getObjectOrNull(collection.initialFilter)) ||
|
|
31
|
+
!equal(getObjectOrNull(tableController.sortBy), getObjectOrNull(collection.initialSort))) {
|
|
32
|
+
saveDefaultFilterButton = <>
|
|
33
|
+
<Tooltip
|
|
34
|
+
title={tableController.sortBy || tableController.filterValues ? "Save default filter and sort" : "Clear default filter and sort"}>
|
|
35
|
+
<Button
|
|
36
|
+
color={"primary"}
|
|
37
|
+
size={"small"}
|
|
38
|
+
variant={"outlined"}
|
|
39
|
+
onClick={() => configController
|
|
40
|
+
?.saveCollection({
|
|
41
|
+
id: collection.id,
|
|
42
|
+
parentCollectionIds,
|
|
43
|
+
collectionData: mergeDeep(collection as PersistedCollection,
|
|
44
|
+
{
|
|
45
|
+
initialFilter: tableController.filterValues ?? null,
|
|
46
|
+
initialSort: tableController.sortBy ?? null
|
|
47
|
+
})
|
|
48
|
+
}).then(() => {
|
|
49
|
+
snackbarController.open({
|
|
50
|
+
type: "success",
|
|
51
|
+
message: "Default config saved"
|
|
52
|
+
});
|
|
53
|
+
})}>
|
|
54
|
+
<SaveIcon/>
|
|
55
|
+
</Button>
|
|
56
|
+
</Tooltip>
|
|
57
|
+
|
|
58
|
+
{(collection.initialFilter || collection.initialSort) && <Tooltip
|
|
59
|
+
title={"Reset to default filter and sort"}>
|
|
60
|
+
<Button
|
|
61
|
+
color={"primary"}
|
|
62
|
+
size={"small"}
|
|
63
|
+
variant={"text"}
|
|
64
|
+
onClick={() => {
|
|
65
|
+
tableController.clearFilter?.();
|
|
66
|
+
if (collection?.initialFilter)
|
|
67
|
+
tableController.setFilterValues?.(collection?.initialFilter);
|
|
68
|
+
if (collection?.initialSort)
|
|
69
|
+
tableController.setSortBy?.(collection?.initialSort);
|
|
70
|
+
}}>
|
|
71
|
+
<UndoIcon/>
|
|
72
|
+
</Button>
|
|
73
|
+
</Tooltip>}
|
|
74
|
+
</>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return <>
|
|
78
|
+
{canEditCollection && saveDefaultFilterButton}
|
|
79
|
+
</>
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getObjectOrNull(o?: object): object | null {
|
|
84
|
+
if (o && Object.keys(o).length === 0)
|
|
85
|
+
return o
|
|
86
|
+
return o ?? null;
|
|
87
|
+
}
|
|
@@ -216,7 +216,7 @@ export function CollectionDetailsForm({
|
|
|
216
216
|
label={"Collection id"}
|
|
217
217
|
error={showErrors && Boolean(errors.id)}/>
|
|
218
218
|
<FieldCaption error={touched.id && Boolean(errors.id)}>
|
|
219
|
-
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
|
|
219
|
+
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection. Typically the same as the path."}
|
|
220
220
|
</FieldCaption>
|
|
221
221
|
</div>
|
|
222
222
|
|
|
@@ -76,7 +76,7 @@ export interface CollectionEditorDialogProps {
|
|
|
76
76
|
icon: React.ReactNode
|
|
77
77
|
};
|
|
78
78
|
pathSuggestions?: (path?: string) => Promise<string[]>;
|
|
79
|
-
getUser
|
|
79
|
+
getUser?: (uid: string) => User | null;
|
|
80
80
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
81
81
|
parentCollection?: PersistedCollection;
|
|
82
82
|
}
|
|
@@ -170,7 +170,8 @@ export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
|
170
170
|
} catch (e) {
|
|
171
171
|
console.error(e);
|
|
172
172
|
}
|
|
173
|
-
}, [
|
|
173
|
+
}, [props.editedCollectionId, props.parentCollectionIds, navigation]);
|
|
174
|
+
|
|
174
175
|
if (!topLevelNavigation) {
|
|
175
176
|
throw Error("Internal: Navigation not ready in collection editor");
|
|
176
177
|
}
|
|
@@ -486,19 +487,23 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
486
487
|
setFormDirty(dirty);
|
|
487
488
|
}, [dirty]);
|
|
488
489
|
|
|
489
|
-
function onImportDataSet(data: object[]) {
|
|
490
|
+
function onImportDataSet(data: object[], propertiesOrder?: string[]) {
|
|
490
491
|
importConfig.setInUse(true);
|
|
491
492
|
buildEntityPropertiesFromData(data, getInferenceType)
|
|
492
493
|
.then((properties) => {
|
|
493
494
|
const res = cleanPropertiesFromImport(properties);
|
|
494
495
|
|
|
495
|
-
setFieldValue("properties", res.properties);
|
|
496
|
-
setFieldValue("propertiesOrder", Object.keys(res.properties));
|
|
497
|
-
|
|
498
496
|
importConfig.setIdColumn(res.idColumn);
|
|
499
497
|
importConfig.setImportData(data);
|
|
500
498
|
importConfig.setHeadersMapping(res.headersMapping);
|
|
499
|
+
const filteredHeadingsOrder = ((propertiesOrder ?? [])
|
|
500
|
+
.filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
|
|
501
|
+
importConfig.setHeadingsOrder(filteredHeadingsOrder);
|
|
501
502
|
importConfig.setOriginProperties(res.properties);
|
|
503
|
+
|
|
504
|
+
const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
|
|
505
|
+
setFieldValue("properties", res.properties);
|
|
506
|
+
setFieldValue("propertiesOrder", mappedHeadings);
|
|
502
507
|
});
|
|
503
508
|
}
|
|
504
509
|
|
|
@@ -551,9 +556,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
551
556
|
{currentView === "welcome" &&
|
|
552
557
|
<CollectionEditorWelcomeView
|
|
553
558
|
path={path}
|
|
554
|
-
onContinue={(importData) => {
|
|
559
|
+
onContinue={(importData, propertiesOrder) => {
|
|
560
|
+
// console.log("Import data", importData, propertiesOrder)
|
|
555
561
|
if (importData) {
|
|
556
|
-
onImportDataSet(importData);
|
|
562
|
+
onImportDataSet(importData, propertiesOrder);
|
|
557
563
|
setCurrentView("import_data_mapping");
|
|
558
564
|
} else {
|
|
559
565
|
setCurrentView("details");
|
|
@@ -733,7 +739,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
733
739
|
}
|
|
734
740
|
|
|
735
741
|
function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
|
|
736
|
-
const {
|
|
742
|
+
const {
|
|
743
|
+
properties,
|
|
744
|
+
...rest
|
|
745
|
+
} = collection;
|
|
737
746
|
const propertiesResult: PropertiesOrBuilders<any> = {};
|
|
738
747
|
if (properties) {
|
|
739
748
|
Object.keys(properties).forEach((key) => {
|
|
@@ -741,7 +750,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
|
|
|
741
750
|
});
|
|
742
751
|
}
|
|
743
752
|
|
|
744
|
-
return {
|
|
753
|
+
return {
|
|
754
|
+
...rest,
|
|
755
|
+
properties: propertiesResult
|
|
756
|
+
};
|
|
745
757
|
}
|
|
746
758
|
|
|
747
759
|
function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
|
|
@@ -761,7 +773,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
761
773
|
Object.keys(internalProperty.properties).forEach((key) => {
|
|
762
774
|
properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
|
|
763
775
|
});
|
|
764
|
-
internalProperty = {
|
|
776
|
+
internalProperty = {
|
|
777
|
+
...internalProperty,
|
|
778
|
+
properties
|
|
779
|
+
};
|
|
765
780
|
}
|
|
766
781
|
|
|
767
782
|
}
|
|
@@ -19,7 +19,7 @@ export function CollectionEditorWelcomeView({
|
|
|
19
19
|
path: string;
|
|
20
20
|
pathSuggestions?: (path: string) => Promise<string[]>;
|
|
21
21
|
parentCollection?: EntityCollection;
|
|
22
|
-
onContinue: (importData?: object[]) => void;
|
|
22
|
+
onContinue: (importData?: object[], propertiesOrder?: string[]) => void;
|
|
23
23
|
existingCollectionPaths?: string[];
|
|
24
24
|
}) {
|
|
25
25
|
|
|
@@ -154,7 +154,7 @@ export function CollectionEditorWelcomeView({
|
|
|
154
154
|
● Create a collection from a file (csv, json, xls, xslx...)
|
|
155
155
|
</Typography>
|
|
156
156
|
|
|
157
|
-
<ImportFileUpload onDataAdded={(data) => onContinue(data)}/>
|
|
157
|
+
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
|
|
158
158
|
|
|
159
159
|
</div>}
|
|
160
160
|
|
|
@@ -43,7 +43,7 @@ type CollectionEditorFormProps = {
|
|
|
43
43
|
setDirty?: (dirty: boolean) => void;
|
|
44
44
|
reservedGroups?: string[];
|
|
45
45
|
extraIcon: React.ReactNode;
|
|
46
|
-
getUser
|
|
46
|
+
getUser?: (uid: string) => User | null;
|
|
47
47
|
getData?: () => Promise<object[]>;
|
|
48
48
|
doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
|
|
49
49
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
@@ -301,7 +301,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
301
301
|
? values.propertiesOrder
|
|
302
302
|
: Object.keys(values.properties)) as string[];
|
|
303
303
|
|
|
304
|
-
const owner = useMemo(() => values.ownerId ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
|
|
304
|
+
const owner = useMemo(() => values.ownerId && getUser ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
|
|
305
305
|
|
|
306
306
|
const onPropertyClick = useCallback((propertyKey: string, namespace?: string) => {
|
|
307
307
|
console.debug("CollectionEditor: onPropertyClick", {
|
|
@@ -41,7 +41,7 @@ export function SubcollectionsEditTab({
|
|
|
41
41
|
parentCollection?: EntityCollection,
|
|
42
42
|
configController: CollectionsConfigController;
|
|
43
43
|
collectionInference?: CollectionInference;
|
|
44
|
-
getUser
|
|
44
|
+
getUser?: (uid: string) => User | null;
|
|
45
45
|
parentCollectionIds?: string[];
|
|
46
46
|
}) {
|
|
47
47
|
|