@firecms/collection_editor 3.0.0-canary.8 → 3.0.0-canary.80
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 +1928 -1847
- 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 +4 -2
- package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +4 -3
- 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 +19 -35
- package/src/ConfigControllerProvider.tsx +65 -62
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +6 -4
- package/src/ui/CollectionViewHeaderAction.tsx +6 -3
- package/src/ui/EditorCollectionAction.tsx +9 -63
- package/src/ui/EditorCollectionActionStart.tsx +87 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +16 -11
- package/src/ui/NewCollectionButton.tsx +12 -10
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/PropertyAddColumnComponent.tsx +6 -3
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +34 -4
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +56 -31
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +4 -4
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +27 -24
- package/src/ui/collection_editor/GetCodeDialog.tsx +15 -3
- package/src/ui/collection_editor/PropertyEditView.tsx +5 -4
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +3 -6
- package/src/ui/collection_editor/PropertySelectItem.tsx +2 -2
- package/src/ui/collection_editor/PropertyTree.tsx +3 -3
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +25 -9
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +9 -7
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +14 -8
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +1 -1
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +5 -5
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
- package/src/useCollectionEditorPlugin.tsx +28 -27
- package/src/utils/collections.ts +30 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
CollectionActionsProps,
|
|
5
|
-
mergeDeep,
|
|
6
|
-
useAuthController,
|
|
7
|
-
useNavigationController,
|
|
8
|
-
useSnackbarController
|
|
9
|
-
} from "@firecms/core";
|
|
10
|
-
import { Button, IconButton, SaveIcon, SettingsIcon, Tooltip, UndoIcon, } from "@firecms/ui";
|
|
1
|
+
import { CollectionActionsProps, useAuthController, useNavigationController } from "@firecms/core";
|
|
2
|
+
import { IconButton, SettingsIcon, Tooltip, } from "@firecms/ui";
|
|
11
3
|
|
|
12
4
|
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
13
|
-
import { useCollectionsConfigController } from "../useCollectionsConfigController";
|
|
14
5
|
import { PersistedCollection } from "../types/persisted_collection";
|
|
15
6
|
|
|
16
7
|
export function EditorCollectionAction({
|
|
@@ -23,8 +14,6 @@ export function EditorCollectionAction({
|
|
|
23
14
|
const authController = useAuthController();
|
|
24
15
|
const navigationController = useNavigationController();
|
|
25
16
|
const collectionEditorController = useCollectionEditorController();
|
|
26
|
-
const configController = useCollectionsConfigController();
|
|
27
|
-
const snackbarController = useSnackbarController();
|
|
28
17
|
|
|
29
18
|
const parentCollection = navigationController.getCollectionFromIds(parentCollectionIds);
|
|
30
19
|
|
|
@@ -35,68 +24,25 @@ export function EditorCollectionAction({
|
|
|
35
24
|
}).editCollections
|
|
36
25
|
: true;
|
|
37
26
|
|
|
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
27
|
const editorButton = <Tooltip
|
|
87
28
|
title={canEditCollection ? "Edit collection" : "You don't have permissions to edit this collection"}>
|
|
88
29
|
<IconButton
|
|
89
30
|
color={"primary"}
|
|
90
31
|
disabled={!canEditCollection}
|
|
91
32
|
onClick={canEditCollection
|
|
92
|
-
? () => collectionEditorController?.editCollection({
|
|
33
|
+
? () => collectionEditorController?.editCollection({
|
|
34
|
+
id: collection.id,
|
|
35
|
+
fullPath,
|
|
36
|
+
parentCollectionIds,
|
|
37
|
+
parentCollection: parentCollection as PersistedCollection,
|
|
38
|
+
existingEntities: tableController?.data ?? []
|
|
39
|
+
})
|
|
93
40
|
: undefined}>
|
|
94
41
|
<SettingsIcon/>
|
|
95
42
|
</IconButton>
|
|
96
43
|
</Tooltip>;
|
|
97
44
|
|
|
98
45
|
return <>
|
|
99
|
-
{canEditCollection && saveDefaultFilterButton}
|
|
100
46
|
{editorButton}
|
|
101
47
|
</>
|
|
102
48
|
|
|
@@ -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
|
+
}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "@firecms/core";
|
|
7
7
|
import { DeleteIcon, IconButton, Menu, MenuItem, MoreVertIcon, SettingsIcon, } from "@firecms/ui";
|
|
8
8
|
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
9
|
-
import {
|
|
9
|
+
import { useState } from "react";
|
|
10
10
|
import { useCollectionsConfigController } from "../useCollectionsConfigController";
|
|
11
11
|
|
|
12
12
|
export function HomePageEditorCollectionAction({
|
|
@@ -24,13 +24,16 @@ export function HomePageEditorCollectionAction({
|
|
|
24
24
|
collection
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
const onEditCollectionClicked =
|
|
28
|
-
collectionEditorController?.editCollection({
|
|
29
|
-
|
|
27
|
+
const onEditCollectionClicked = () => {
|
|
28
|
+
collectionEditorController?.editCollection({
|
|
29
|
+
id: collection.id,
|
|
30
|
+
parentCollectionIds: []
|
|
31
|
+
});
|
|
32
|
+
};
|
|
30
33
|
|
|
31
34
|
const [deleteRequested, setDeleteRequested] = useState(false);
|
|
32
35
|
|
|
33
|
-
const deleteCollection =
|
|
36
|
+
const deleteCollection = () => {
|
|
34
37
|
configController?.deleteCollection({ id: collection.id }).then(() => {
|
|
35
38
|
setDeleteRequested(false);
|
|
36
39
|
snackbarController.open({
|
|
@@ -38,7 +41,7 @@ export function HomePageEditorCollectionAction({
|
|
|
38
41
|
type: "success"
|
|
39
42
|
});
|
|
40
43
|
});
|
|
41
|
-
}
|
|
44
|
+
};
|
|
42
45
|
|
|
43
46
|
return <>
|
|
44
47
|
|
|
@@ -49,11 +52,13 @@ export function HomePageEditorCollectionAction({
|
|
|
49
52
|
<MoreVertIcon size={"small"}/>
|
|
50
53
|
</IconButton>}
|
|
51
54
|
>
|
|
52
|
-
<MenuItem
|
|
53
|
-
|
|
54
|
-
event
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
<MenuItem
|
|
56
|
+
dense={true}
|
|
57
|
+
onClick={(event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
event.stopPropagation();
|
|
60
|
+
setDeleteRequested(true);
|
|
61
|
+
}}>
|
|
57
62
|
<DeleteIcon/>
|
|
58
63
|
Delete
|
|
59
64
|
</MenuItem>
|
|
@@ -3,14 +3,16 @@ import { useCollectionEditorController } from "../useCollectionEditorController"
|
|
|
3
3
|
|
|
4
4
|
export function NewCollectionButton() {
|
|
5
5
|
const collectionEditorController = useCollectionEditorController();
|
|
6
|
-
return <
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
return <div className={"bg-gray-50 dark:bg-gray-900 min-w-fit rounded"}>
|
|
7
|
+
<Button className={"min-w-fit"}
|
|
8
|
+
variant={"outlined"}
|
|
9
|
+
onClick={() => collectionEditorController.createCollection({
|
|
10
|
+
parentCollectionIds: [],
|
|
11
|
+
redirect: true,
|
|
12
|
+
sourceClick: "new_collection_button"
|
|
13
|
+
})}>
|
|
14
|
+
<AddIcon/>
|
|
15
|
+
New collection
|
|
16
|
+
</Button>
|
|
17
|
+
</div>
|
|
16
18
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PluginHomePageAdditionalCardsProps, useAuthController } from "@firecms/core";
|
|
2
|
-
import { AddIcon, Card,
|
|
2
|
+
import { AddIcon, Card, cls, Typography } from "@firecms/ui";
|
|
3
3
|
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
4
4
|
|
|
5
5
|
export function NewCollectionCard({
|
|
@@ -20,7 +20,7 @@ export function NewCollectionCard({
|
|
|
20
20
|
: true;
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
|
-
<Card className={
|
|
23
|
+
<Card className={cls("h-full p-4 min-h-[124px]")}
|
|
24
24
|
onClick={collectionEditorController && canCreateCollections
|
|
25
25
|
? () => collectionEditorController.createCollection({
|
|
26
26
|
initialValues: group ? { group } : undefined,
|
|
@@ -31,7 +31,7 @@ export function NewCollectionCard({
|
|
|
31
31
|
: undefined}>
|
|
32
32
|
|
|
33
33
|
<div
|
|
34
|
-
className="flex
|
|
34
|
+
className="flex items-center justify-center h-full w-full flex-grow flex-col">
|
|
35
35
|
<AddIcon color="primary" size={"large"}/>
|
|
36
36
|
<Typography color="primary"
|
|
37
37
|
variant={"caption"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDefaultPropertiesOrder, useAuthController } from "@firecms/core";
|
|
1
|
+
import { EntityTableController, getDefaultPropertiesOrder, useAuthController } from "@firecms/core";
|
|
2
2
|
import { AddIcon, Tooltip } from "@firecms/ui";
|
|
3
3
|
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
4
4
|
import { PersistedCollection } from "../types/persisted_collection";
|
|
@@ -6,11 +6,13 @@ import { PersistedCollection } from "../types/persisted_collection";
|
|
|
6
6
|
export function PropertyAddColumnComponent({
|
|
7
7
|
fullPath,
|
|
8
8
|
parentCollectionIds,
|
|
9
|
-
collection
|
|
9
|
+
collection,
|
|
10
|
+
tableController
|
|
10
11
|
}: {
|
|
11
12
|
fullPath: string,
|
|
12
13
|
parentCollectionIds: string[],
|
|
13
14
|
collection: PersistedCollection;
|
|
15
|
+
tableController: EntityTableController;
|
|
14
16
|
}) {
|
|
15
17
|
|
|
16
18
|
const authController = useAuthController();
|
|
@@ -32,7 +34,8 @@ export function PropertyAddColumnComponent({
|
|
|
32
34
|
editedCollectionId: collection.id,
|
|
33
35
|
parentCollectionIds,
|
|
34
36
|
currentPropertiesOrder: getDefaultPropertiesOrder(collection),
|
|
35
|
-
collection
|
|
37
|
+
collection,
|
|
38
|
+
existingEntities: tableController.data
|
|
36
39
|
});
|
|
37
40
|
}}>
|
|
38
41
|
<AddIcon color={"inherit"}/>
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
AutocompleteItem,
|
|
6
6
|
BooleanSwitchWithLabel,
|
|
7
7
|
Chip,
|
|
8
|
-
|
|
8
|
+
ClearIcon,
|
|
9
|
+
cls,
|
|
9
10
|
Container,
|
|
10
11
|
DebouncedTextField,
|
|
11
12
|
Dialog,
|
|
@@ -146,7 +147,7 @@ export function CollectionDetailsForm({
|
|
|
146
147
|
</FieldCaption>
|
|
147
148
|
</div>
|
|
148
149
|
|
|
149
|
-
<div className={
|
|
150
|
+
<div className={cls("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
|
|
150
151
|
<Field name={"path"}
|
|
151
152
|
as={DebouncedTextField}
|
|
152
153
|
label={"Path"}
|
|
@@ -216,7 +217,7 @@ export function CollectionDetailsForm({
|
|
|
216
217
|
label={"Collection id"}
|
|
217
218
|
error={showErrors && Boolean(errors.id)}/>
|
|
218
219
|
<FieldCaption error={touched.id && Boolean(errors.id)}>
|
|
219
|
-
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
|
|
220
|
+
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection. Typically the same as the path."}
|
|
220
221
|
</FieldCaption>
|
|
221
222
|
</div>
|
|
222
223
|
|
|
@@ -235,6 +236,35 @@ export function CollectionDetailsForm({
|
|
|
235
236
|
{showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
|
|
236
237
|
</FieldCaption>
|
|
237
238
|
</div>
|
|
239
|
+
<div className={"col-span-12"}>
|
|
240
|
+
<TextField
|
|
241
|
+
error={showErrors && Boolean(errors.sideDialogWidth)}
|
|
242
|
+
name={"sideDialogWidth"}
|
|
243
|
+
type={"number"}
|
|
244
|
+
aria-describedby={"sideDialogWidth-helper"}
|
|
245
|
+
onChange={(e) => {
|
|
246
|
+
setFieldTouched("sideDialogWidth", true);
|
|
247
|
+
const value = e.target.value;
|
|
248
|
+
if (!value) {
|
|
249
|
+
setFieldValue("sideDialogWidth", null);
|
|
250
|
+
} else if (!isNaN(Number(value))) {
|
|
251
|
+
setFieldValue("sideDialogWidth", Number(value));
|
|
252
|
+
}
|
|
253
|
+
}}
|
|
254
|
+
endAdornment={<IconButton
|
|
255
|
+
size={"small"}
|
|
256
|
+
onClick={() => {
|
|
257
|
+
setFieldValue("sideDialogWidth", null);
|
|
258
|
+
}}
|
|
259
|
+
disabled={!values.sideDialogWidth}>
|
|
260
|
+
<ClearIcon size={"small"}/>
|
|
261
|
+
</IconButton>}
|
|
262
|
+
value={values.sideDialogWidth ?? ""}
|
|
263
|
+
label={"Side dialog width"}/>
|
|
264
|
+
<FieldCaption error={showErrors && Boolean(errors.singularName)}>
|
|
265
|
+
{showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define the width (in pixels) of entities side dialog. Default is 768px"}
|
|
266
|
+
</FieldCaption>
|
|
267
|
+
</div>
|
|
238
268
|
<div className={"col-span-12"}>
|
|
239
269
|
<TextField
|
|
240
270
|
error={showErrors && Boolean(errors.description)}
|
|
@@ -272,7 +302,7 @@ export function CollectionDetailsForm({
|
|
|
272
302
|
<div className={"col-span-12"}>
|
|
273
303
|
<Select
|
|
274
304
|
name="customId"
|
|
275
|
-
label="
|
|
305
|
+
label="Document IDs generation"
|
|
276
306
|
position={"item-aligned"}
|
|
277
307
|
disabled={customIdValue === "code_defined"}
|
|
278
308
|
onValueChange={(v) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
CircularProgressCenter,
|
|
5
|
+
Entity,
|
|
5
6
|
EntityCollection,
|
|
6
7
|
ErrorView,
|
|
7
8
|
isPropertyBuilder,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
import {
|
|
26
27
|
ArrowBackIcon,
|
|
27
28
|
Button,
|
|
28
|
-
|
|
29
|
+
cls,
|
|
29
30
|
coolIconKeys,
|
|
30
31
|
defaultBorderMixin,
|
|
31
32
|
Dialog,
|
|
@@ -76,9 +77,10 @@ export interface CollectionEditorDialogProps {
|
|
|
76
77
|
icon: React.ReactNode
|
|
77
78
|
};
|
|
78
79
|
pathSuggestions?: (path?: string) => Promise<string[]>;
|
|
79
|
-
getUser
|
|
80
|
+
getUser?: (uid: string) => User | null;
|
|
80
81
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
81
82
|
parentCollection?: PersistedCollection;
|
|
83
|
+
existingEntities?: Entity<any>[];
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
@@ -88,13 +90,13 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
88
90
|
const [formDirty, setFormDirty] = React.useState<boolean>(false);
|
|
89
91
|
const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
|
|
90
92
|
|
|
91
|
-
const handleCancel =
|
|
93
|
+
const handleCancel = () => {
|
|
92
94
|
if (!formDirty) {
|
|
93
95
|
props.handleClose(undefined);
|
|
94
96
|
} else {
|
|
95
97
|
setUnsavedChangesDialogOpen(true);
|
|
96
98
|
}
|
|
97
|
-
}
|
|
99
|
+
};
|
|
98
100
|
|
|
99
101
|
useEffect(() => {
|
|
100
102
|
if (!open) {
|
|
@@ -136,7 +138,7 @@ type EditorView = "welcome"
|
|
|
136
138
|
| "extra_view"
|
|
137
139
|
| "subcollections";
|
|
138
140
|
|
|
139
|
-
export function CollectionEditor
|
|
141
|
+
export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
140
142
|
handleCancel: () => void,
|
|
141
143
|
setFormDirty: (dirty: boolean) => void
|
|
142
144
|
}) {
|
|
@@ -154,14 +156,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
154
156
|
const collectionsInThisLevel = (props.parentCollection ? props.parentCollection.subcollections : collections) ?? [];
|
|
155
157
|
const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
|
|
156
158
|
const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
|
|
157
|
-
const [collection, setCollection] = React.useState<PersistedCollection<
|
|
159
|
+
const [collection, setCollection] = React.useState<PersistedCollection<any> | undefined>();
|
|
158
160
|
const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
|
|
159
161
|
|
|
160
162
|
useEffect(() => {
|
|
161
163
|
try {
|
|
162
164
|
if (navigation.initialised) {
|
|
163
165
|
if (props.editedCollectionId) {
|
|
164
|
-
setCollection(navigation.getCollectionFromPaths
|
|
166
|
+
setCollection(navigation.getCollectionFromPaths([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
|
|
165
167
|
} else {
|
|
166
168
|
setCollection(undefined);
|
|
167
169
|
}
|
|
@@ -170,7 +172,8 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
170
172
|
} catch (e) {
|
|
171
173
|
console.error(e);
|
|
172
174
|
}
|
|
173
|
-
}, [
|
|
175
|
+
}, [props.editedCollectionId, props.parentCollectionIds, navigation.initialised, navigation.getCollectionFromPaths]);
|
|
176
|
+
|
|
174
177
|
if (!topLevelNavigation) {
|
|
175
178
|
throw Error("Internal: Navigation not ready in collection editor");
|
|
176
179
|
}
|
|
@@ -186,14 +189,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
186
189
|
}
|
|
187
190
|
: undefined;
|
|
188
191
|
|
|
189
|
-
const initialValues: PersistedCollection<
|
|
192
|
+
const initialValues: PersistedCollection<any> = initialCollection
|
|
190
193
|
? applyPropertyConfigs(initialCollection, propertyConfigs)
|
|
191
194
|
: {
|
|
192
195
|
id: initialValuesProp?.path ?? randomString(16),
|
|
193
196
|
path: initialValuesProp?.path ?? "",
|
|
194
197
|
name: initialValuesProp?.name ?? "",
|
|
195
198
|
group: initialValuesProp?.group ?? "",
|
|
196
|
-
properties: {} as PropertiesOrBuilders
|
|
199
|
+
properties: {} as PropertiesOrBuilders,
|
|
197
200
|
propertiesOrder: [],
|
|
198
201
|
icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
|
|
199
202
|
ownerId: authController.user?.uid ?? ""
|
|
@@ -243,7 +246,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
243
246
|
setCollection,
|
|
244
247
|
initialValues,
|
|
245
248
|
propertyConfigs,
|
|
246
|
-
groups
|
|
249
|
+
groups,
|
|
250
|
+
existingEntities
|
|
247
251
|
}: CollectionEditorDialogProps & {
|
|
248
252
|
handleCancel: () => void,
|
|
249
253
|
setFormDirty: (dirty: boolean) => void,
|
|
@@ -293,7 +297,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
293
297
|
});
|
|
294
298
|
};
|
|
295
299
|
|
|
296
|
-
const setNextMode =
|
|
300
|
+
const setNextMode = () => {
|
|
297
301
|
if (currentView === "details") {
|
|
298
302
|
if (importConfig.inUse) {
|
|
299
303
|
setCurrentView("import_data_saving");
|
|
@@ -314,14 +318,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
314
318
|
setCurrentView("details");
|
|
315
319
|
}
|
|
316
320
|
|
|
317
|
-
}
|
|
321
|
+
};
|
|
318
322
|
|
|
319
|
-
const doCollectionInference =
|
|
323
|
+
const doCollectionInference = (collection: PersistedCollection<any>) => {
|
|
320
324
|
if (!collectionInference) return undefined;
|
|
321
325
|
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentCollectionIds ?? []);
|
|
322
|
-
}
|
|
326
|
+
};
|
|
323
327
|
|
|
324
|
-
const inferCollectionFromData =
|
|
328
|
+
const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
|
|
325
329
|
|
|
326
330
|
try {
|
|
327
331
|
if (!doCollectionInference) {
|
|
@@ -365,10 +369,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
365
369
|
});
|
|
366
370
|
return newCollection;
|
|
367
371
|
}
|
|
368
|
-
}
|
|
372
|
+
};
|
|
369
373
|
|
|
370
374
|
const onSubmit = (newCollectionState: PersistedCollection<M>, formexController: FormexController<PersistedCollection<M>>) => {
|
|
371
|
-
console.
|
|
375
|
+
console.debug("Submitting collection", newCollectionState);
|
|
372
376
|
try {
|
|
373
377
|
|
|
374
378
|
if (!isNewCollection) {
|
|
@@ -480,25 +484,36 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
480
484
|
|
|
481
485
|
const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
|
|
482
486
|
const resolvedPath = !pathError ? navigation.resolveAliasesFrom(updatedFullPath) : undefined;
|
|
483
|
-
const getDataWithPath = resolvedPath && getData ? () =>
|
|
487
|
+
const getDataWithPath = resolvedPath && getData ? async () => {
|
|
488
|
+
const data = await getData(resolvedPath, parentPaths ?? []);
|
|
489
|
+
if (existingEntities) {
|
|
490
|
+
const existingData = existingEntities.map(e => e.values);
|
|
491
|
+
data.push(...existingData);
|
|
492
|
+
}
|
|
493
|
+
return data;
|
|
494
|
+
} : undefined;
|
|
484
495
|
|
|
485
496
|
useEffect(() => {
|
|
486
497
|
setFormDirty(dirty);
|
|
487
498
|
}, [dirty]);
|
|
488
499
|
|
|
489
|
-
function onImportDataSet(data: object[]) {
|
|
500
|
+
function onImportDataSet(data: object[], propertiesOrder?: string[]) {
|
|
490
501
|
importConfig.setInUse(true);
|
|
491
502
|
buildEntityPropertiesFromData(data, getInferenceType)
|
|
492
503
|
.then((properties) => {
|
|
493
504
|
const res = cleanPropertiesFromImport(properties);
|
|
494
505
|
|
|
495
|
-
setFieldValue("properties", res.properties);
|
|
496
|
-
setFieldValue("propertiesOrder", Object.keys(res.properties));
|
|
497
|
-
|
|
498
506
|
importConfig.setIdColumn(res.idColumn);
|
|
499
507
|
importConfig.setImportData(data);
|
|
500
508
|
importConfig.setHeadersMapping(res.headersMapping);
|
|
509
|
+
const filteredHeadingsOrder = ((propertiesOrder ?? [])
|
|
510
|
+
.filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
|
|
511
|
+
importConfig.setHeadingsOrder(filteredHeadingsOrder);
|
|
501
512
|
importConfig.setOriginProperties(res.properties);
|
|
513
|
+
|
|
514
|
+
const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
|
|
515
|
+
setFieldValue("properties", res.properties);
|
|
516
|
+
setFieldValue("propertiesOrder", mappedHeadings);
|
|
502
517
|
});
|
|
503
518
|
}
|
|
504
519
|
|
|
@@ -521,7 +536,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
521
536
|
|
|
522
537
|
<>
|
|
523
538
|
{!isNewCollection && <Tabs value={currentView}
|
|
524
|
-
className={
|
|
539
|
+
className={cls(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
|
|
525
540
|
onValueChange={(v) => setCurrentView(v as EditorView)}>
|
|
526
541
|
<Tab value={"details"}>
|
|
527
542
|
Details
|
|
@@ -536,7 +551,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
536
551
|
|
|
537
552
|
<form noValidate
|
|
538
553
|
onSubmit={formController.handleSubmit}
|
|
539
|
-
className={
|
|
554
|
+
className={cls(
|
|
540
555
|
isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
|
|
541
556
|
"flex-grow flex flex-col relative")}>
|
|
542
557
|
|
|
@@ -551,9 +566,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
551
566
|
{currentView === "welcome" &&
|
|
552
567
|
<CollectionEditorWelcomeView
|
|
553
568
|
path={path}
|
|
554
|
-
onContinue={(importData) => {
|
|
569
|
+
onContinue={(importData, propertiesOrder) => {
|
|
570
|
+
// console.log("Import data", importData, propertiesOrder)
|
|
555
571
|
if (importData) {
|
|
556
|
-
onImportDataSet(importData);
|
|
572
|
+
onImportDataSet(importData, propertiesOrder);
|
|
557
573
|
setCurrentView("import_data_mapping");
|
|
558
574
|
} else {
|
|
559
575
|
setCurrentView("details");
|
|
@@ -733,7 +749,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
733
749
|
}
|
|
734
750
|
|
|
735
751
|
function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
|
|
736
|
-
const {
|
|
752
|
+
const {
|
|
753
|
+
properties,
|
|
754
|
+
...rest
|
|
755
|
+
} = collection;
|
|
737
756
|
const propertiesResult: PropertiesOrBuilders<any> = {};
|
|
738
757
|
if (properties) {
|
|
739
758
|
Object.keys(properties).forEach((key) => {
|
|
@@ -741,7 +760,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
|
|
|
741
760
|
});
|
|
742
761
|
}
|
|
743
762
|
|
|
744
|
-
return {
|
|
763
|
+
return {
|
|
764
|
+
...rest,
|
|
765
|
+
properties: propertiesResult
|
|
766
|
+
};
|
|
745
767
|
}
|
|
746
768
|
|
|
747
769
|
function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
|
|
@@ -761,7 +783,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
761
783
|
Object.keys(internalProperty.properties).forEach((key) => {
|
|
762
784
|
properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
|
|
763
785
|
});
|
|
764
|
-
internalProperty = {
|
|
786
|
+
internalProperty = {
|
|
787
|
+
...internalProperty,
|
|
788
|
+
properties
|
|
789
|
+
};
|
|
765
790
|
}
|
|
766
791
|
|
|
767
792
|
}
|