@firecms/collection_editor 3.0.0-canary.7 → 3.0.0-canary.70
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 +1944 -1890
- 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/types/persisted_collection.d.ts +1 -1
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +2 -2
- 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/ui/collection_editor/utils/supported_fields.d.ts +2 -2
- package/dist/useCollectionEditorPlugin.d.ts +6 -9
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +26 -26
- package/src/ConfigControllerProvider.tsx +53 -59
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +1 -1
- package/src/types/persisted_collection.ts +1 -1
- package/src/ui/EditorCollectionAction.tsx +0 -51
- package/src/ui/EditorCollectionActionStart.tsx +87 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +16 -11
- package/src/ui/MissingReferenceWidget.tsx +2 -1
- package/src/ui/NewCollectionButton.tsx +12 -10
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +51 -22
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +49 -32
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +5 -5
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +27 -24
- package/src/ui/collection_editor/EnumForm.tsx +3 -4
- package/src/ui/collection_editor/GetCodeDialog.tsx +15 -3
- package/src/ui/collection_editor/PropertyEditView.tsx +9 -8
- 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 +1 -1
- 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/CommonPropertyFields.tsx +7 -8
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +7 -8
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +3 -4
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +3 -4
- package/src/ui/collection_editor/utils/supported_fields.tsx +3 -3
- package/src/useCollectionEditorPlugin.tsx +29 -30
- package/src/utils/collections.ts +30 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/dist/ui/collection_editor/properties/FieldHelperView.d.ts +0 -4
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
- package/src/ui/collection_editor/properties/FieldHelperView.tsx +0 -13
|
@@ -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>
|
|
@@ -11,7 +11,8 @@ export function MissingReferenceWidget({ path: pathProp }: {
|
|
|
11
11
|
const collectionEditor = useCollectionEditorController();
|
|
12
12
|
return <div className={"p-1 flex flex-col items-center"}>
|
|
13
13
|
<ErrorView error={"No collection for path: " + path}/>
|
|
14
|
-
<Button className={"mx-2"} variant={"outlined"}
|
|
14
|
+
<Button className={"mx-2"} variant={"outlined"}
|
|
15
|
+
size={"small"}
|
|
15
16
|
onClick={() => {
|
|
16
17
|
collectionEditor.createCollection({
|
|
17
18
|
initialValues: { path, name: unslugify(path) },
|
|
@@ -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,11 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
|
-
import { EntityCollection, IconForView, SearchIconsView, singular, toSnakeCase, } from "@firecms/core";
|
|
2
|
+
import { EntityCollection, FieldCaption, IconForView, SearchIconsView, singular, toSnakeCase, } from "@firecms/core";
|
|
3
3
|
import {
|
|
4
4
|
Autocomplete,
|
|
5
5
|
AutocompleteItem,
|
|
6
6
|
BooleanSwitchWithLabel,
|
|
7
7
|
Chip,
|
|
8
|
-
|
|
8
|
+
ClearIcon,
|
|
9
|
+
cls,
|
|
9
10
|
Container,
|
|
10
11
|
DebouncedTextField,
|
|
11
12
|
Dialog,
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
useAutoComplete
|
|
21
22
|
} from "@firecms/ui";
|
|
22
23
|
|
|
23
|
-
import { FieldHelperView } from "./properties/FieldHelperView";
|
|
24
24
|
import { Field, getIn, useFormex } from "@firecms/formex";
|
|
25
25
|
|
|
26
26
|
export function CollectionDetailsForm({
|
|
@@ -142,12 +142,12 @@ export function CollectionDetailsForm({
|
|
|
142
142
|
label={"Name"}
|
|
143
143
|
required
|
|
144
144
|
error={showErrors && Boolean(errors.name)}/>
|
|
145
|
-
<
|
|
145
|
+
<FieldCaption error={touched.name && Boolean(errors.name)}>
|
|
146
146
|
{touched.name && Boolean(errors.name) ? errors.name : "Name of in this collection, usually a plural name (e.g. Products)"}
|
|
147
|
-
</
|
|
147
|
+
</FieldCaption>
|
|
148
148
|
</div>
|
|
149
149
|
|
|
150
|
-
<div className={
|
|
150
|
+
<div className={cls("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
|
|
151
151
|
<Field name={"path"}
|
|
152
152
|
as={DebouncedTextField}
|
|
153
153
|
label={"Path"}
|
|
@@ -155,11 +155,11 @@ export function CollectionDetailsForm({
|
|
|
155
155
|
required
|
|
156
156
|
error={showErrors && Boolean(errors.path)}/>
|
|
157
157
|
|
|
158
|
-
<
|
|
158
|
+
<FieldCaption error={touched.path && Boolean(errors.path)}>
|
|
159
159
|
{touched.path && Boolean(errors.path)
|
|
160
160
|
? errors.path
|
|
161
161
|
: isSubcollection ? "Relative path to the parent (no need to include the parent path)" : "Path that this collection is stored in, in the database"}
|
|
162
|
-
</
|
|
162
|
+
</FieldCaption>
|
|
163
163
|
|
|
164
164
|
</div>
|
|
165
165
|
|
|
@@ -190,9 +190,9 @@ export function CollectionDetailsForm({
|
|
|
190
190
|
</AutocompleteItem>;
|
|
191
191
|
})}
|
|
192
192
|
</Autocomplete>
|
|
193
|
-
<
|
|
193
|
+
<FieldCaption>
|
|
194
194
|
{showErrors && Boolean(errors.group) ? errors.group : "Group of the collection"}
|
|
195
|
-
</
|
|
195
|
+
</FieldCaption>
|
|
196
196
|
</div>}
|
|
197
197
|
|
|
198
198
|
<div className={"col-span-12"}>
|
|
@@ -216,9 +216,9 @@ export function CollectionDetailsForm({
|
|
|
216
216
|
disabled={!isNewCollection}
|
|
217
217
|
label={"Collection id"}
|
|
218
218
|
error={showErrors && Boolean(errors.id)}/>
|
|
219
|
-
<
|
|
220
|
-
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
|
|
221
|
-
</
|
|
219
|
+
<FieldCaption error={touched.id && Boolean(errors.id)}>
|
|
220
|
+
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection. Typically the same as the path."}
|
|
221
|
+
</FieldCaption>
|
|
222
222
|
</div>
|
|
223
223
|
|
|
224
224
|
<div className={"col-span-12"}>
|
|
@@ -232,9 +232,38 @@ export function CollectionDetailsForm({
|
|
|
232
232
|
}}
|
|
233
233
|
value={values.singularName ?? ""}
|
|
234
234
|
label={"Singular name"}/>
|
|
235
|
-
<
|
|
235
|
+
<FieldCaption error={showErrors && Boolean(errors.singularName)}>
|
|
236
236
|
{showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
|
|
237
|
-
</
|
|
237
|
+
</FieldCaption>
|
|
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>
|
|
238
267
|
</div>
|
|
239
268
|
<div className={"col-span-12"}>
|
|
240
269
|
<TextField
|
|
@@ -247,9 +276,9 @@ export function CollectionDetailsForm({
|
|
|
247
276
|
aria-describedby="description-helper-text"
|
|
248
277
|
label="Description"
|
|
249
278
|
/>
|
|
250
|
-
<
|
|
279
|
+
<FieldCaption error={showErrors && Boolean(errors.description)}>
|
|
251
280
|
{showErrors && Boolean(errors.description) ? errors.description : "Description of the collection, you can use markdown"}
|
|
252
|
-
</
|
|
281
|
+
</FieldCaption>
|
|
253
282
|
</div>
|
|
254
283
|
|
|
255
284
|
<div className={"col-span-12"}>
|
|
@@ -273,7 +302,7 @@ export function CollectionDetailsForm({
|
|
|
273
302
|
<div className={"col-span-12"}>
|
|
274
303
|
<Select
|
|
275
304
|
name="customId"
|
|
276
|
-
label="
|
|
305
|
+
label="Document IDs generation"
|
|
277
306
|
position={"item-aligned"}
|
|
278
307
|
disabled={customIdValue === "code_defined"}
|
|
279
308
|
onValueChange={(v) => {
|
|
@@ -316,11 +345,11 @@ export function CollectionDetailsForm({
|
|
|
316
345
|
onValueChange={(v) => setFieldValue("collectionGroup", v)}
|
|
317
346
|
value={values.collectionGroup ?? false}
|
|
318
347
|
/>
|
|
319
|
-
<
|
|
348
|
+
<FieldCaption>
|
|
320
349
|
A collection group consists of all collections with the same path. This allows
|
|
321
350
|
you
|
|
322
351
|
to query over multiple collections at once.
|
|
323
|
-
</
|
|
352
|
+
</FieldCaption>
|
|
324
353
|
</div>
|
|
325
354
|
<div className={"col-span-12"}>
|
|
326
355
|
<BooleanSwitchWithLabel
|
|
@@ -329,11 +358,11 @@ export function CollectionDetailsForm({
|
|
|
329
358
|
onValueChange={(v) => setFieldValue("textSearchEnabled", v)}
|
|
330
359
|
value={values.textSearchEnabled ?? false}
|
|
331
360
|
/>
|
|
332
|
-
<
|
|
361
|
+
<FieldCaption>
|
|
333
362
|
Allow text search for this collection. If you have not specified a text search
|
|
334
363
|
delegate, this will use the built-in local text search. This is not recommended
|
|
335
364
|
for large collections, as it may incur in performance and cost issues.
|
|
336
|
-
</
|
|
365
|
+
</FieldCaption>
|
|
337
366
|
</div>
|
|
338
367
|
</div>
|
|
339
368
|
</ExpandablePanel>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
CircularProgressCenter,
|
|
5
5
|
EntityCollection,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
ArrowBackIcon,
|
|
27
27
|
Button,
|
|
28
|
-
|
|
28
|
+
cls,
|
|
29
29
|
coolIconKeys,
|
|
30
30
|
defaultBorderMixin,
|
|
31
31
|
Dialog,
|
|
@@ -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
|
}
|
|
@@ -88,13 +88,13 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
88
88
|
const [formDirty, setFormDirty] = React.useState<boolean>(false);
|
|
89
89
|
const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
|
|
90
90
|
|
|
91
|
-
const handleCancel =
|
|
91
|
+
const handleCancel = () => {
|
|
92
92
|
if (!formDirty) {
|
|
93
93
|
props.handleClose(undefined);
|
|
94
94
|
} else {
|
|
95
95
|
setUnsavedChangesDialogOpen(true);
|
|
96
96
|
}
|
|
97
|
-
}
|
|
97
|
+
};
|
|
98
98
|
|
|
99
99
|
useEffect(() => {
|
|
100
100
|
if (!open) {
|
|
@@ -136,7 +136,7 @@ type EditorView = "welcome"
|
|
|
136
136
|
| "extra_view"
|
|
137
137
|
| "subcollections";
|
|
138
138
|
|
|
139
|
-
export function CollectionEditor
|
|
139
|
+
export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
140
140
|
handleCancel: () => void,
|
|
141
141
|
setFormDirty: (dirty: boolean) => void
|
|
142
142
|
}) {
|
|
@@ -154,14 +154,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
154
154
|
const collectionsInThisLevel = (props.parentCollection ? props.parentCollection.subcollections : collections) ?? [];
|
|
155
155
|
const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
|
|
156
156
|
const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
|
|
157
|
-
const [collection, setCollection] = React.useState<PersistedCollection<
|
|
157
|
+
const [collection, setCollection] = React.useState<PersistedCollection<any> | undefined>();
|
|
158
158
|
const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
|
|
159
159
|
|
|
160
160
|
useEffect(() => {
|
|
161
161
|
try {
|
|
162
162
|
if (navigation.initialised) {
|
|
163
163
|
if (props.editedCollectionId) {
|
|
164
|
-
setCollection(navigation.getCollectionFromPaths
|
|
164
|
+
setCollection(navigation.getCollectionFromPaths([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
|
|
165
165
|
} else {
|
|
166
166
|
setCollection(undefined);
|
|
167
167
|
}
|
|
@@ -170,7 +170,8 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
170
170
|
} catch (e) {
|
|
171
171
|
console.error(e);
|
|
172
172
|
}
|
|
173
|
-
}, [
|
|
173
|
+
}, [props.editedCollectionId, props.parentCollectionIds, navigation.initialised, navigation.getCollectionFromPaths]);
|
|
174
|
+
|
|
174
175
|
if (!topLevelNavigation) {
|
|
175
176
|
throw Error("Internal: Navigation not ready in collection editor");
|
|
176
177
|
}
|
|
@@ -186,14 +187,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
186
187
|
}
|
|
187
188
|
: undefined;
|
|
188
189
|
|
|
189
|
-
const initialValues: PersistedCollection<
|
|
190
|
+
const initialValues: PersistedCollection<any> = initialCollection
|
|
190
191
|
? applyPropertyConfigs(initialCollection, propertyConfigs)
|
|
191
192
|
: {
|
|
192
193
|
id: initialValuesProp?.path ?? randomString(16),
|
|
193
194
|
path: initialValuesProp?.path ?? "",
|
|
194
195
|
name: initialValuesProp?.name ?? "",
|
|
195
196
|
group: initialValuesProp?.group ?? "",
|
|
196
|
-
properties: {} as PropertiesOrBuilders
|
|
197
|
+
properties: {} as PropertiesOrBuilders,
|
|
197
198
|
propertiesOrder: [],
|
|
198
199
|
icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
|
|
199
200
|
ownerId: authController.user?.uid ?? ""
|
|
@@ -293,7 +294,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
293
294
|
});
|
|
294
295
|
};
|
|
295
296
|
|
|
296
|
-
const setNextMode =
|
|
297
|
+
const setNextMode = () => {
|
|
297
298
|
if (currentView === "details") {
|
|
298
299
|
if (importConfig.inUse) {
|
|
299
300
|
setCurrentView("import_data_saving");
|
|
@@ -314,14 +315,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
314
315
|
setCurrentView("details");
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
}
|
|
318
|
+
};
|
|
318
319
|
|
|
319
|
-
const doCollectionInference =
|
|
320
|
+
const doCollectionInference = (collection: PersistedCollection<any>) => {
|
|
320
321
|
if (!collectionInference) return undefined;
|
|
321
322
|
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentCollectionIds ?? []);
|
|
322
|
-
}
|
|
323
|
+
};
|
|
323
324
|
|
|
324
|
-
const inferCollectionFromData =
|
|
325
|
+
const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
|
|
325
326
|
|
|
326
327
|
try {
|
|
327
328
|
if (!doCollectionInference) {
|
|
@@ -365,10 +366,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
365
366
|
});
|
|
366
367
|
return newCollection;
|
|
367
368
|
}
|
|
368
|
-
}
|
|
369
|
+
};
|
|
369
370
|
|
|
370
371
|
const onSubmit = (newCollectionState: PersistedCollection<M>, formexController: FormexController<PersistedCollection<M>>) => {
|
|
371
|
-
console.
|
|
372
|
+
console.debug("Submitting collection", newCollectionState);
|
|
372
373
|
try {
|
|
373
374
|
|
|
374
375
|
if (!isNewCollection) {
|
|
@@ -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
|
|
|
@@ -521,7 +526,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
521
526
|
|
|
522
527
|
<>
|
|
523
528
|
{!isNewCollection && <Tabs value={currentView}
|
|
524
|
-
className={
|
|
529
|
+
className={cls(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
|
|
525
530
|
onValueChange={(v) => setCurrentView(v as EditorView)}>
|
|
526
531
|
<Tab value={"details"}>
|
|
527
532
|
Details
|
|
@@ -536,7 +541,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
536
541
|
|
|
537
542
|
<form noValidate
|
|
538
543
|
onSubmit={formController.handleSubmit}
|
|
539
|
-
className={
|
|
544
|
+
className={cls(
|
|
540
545
|
isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
|
|
541
546
|
"flex-grow flex flex-col relative")}>
|
|
542
547
|
|
|
@@ -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,13 +739,21 @@ 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
|
-
|
|
739
|
-
|
|
740
|
-
|
|
747
|
+
if (properties) {
|
|
748
|
+
Object.keys(properties).forEach((key) => {
|
|
749
|
+
propertiesResult[key] = applyPropertiesConfig(properties[key] as PropertyOrBuilder, propertyConfigs);
|
|
750
|
+
});
|
|
751
|
+
}
|
|
741
752
|
|
|
742
|
-
return {
|
|
753
|
+
return {
|
|
754
|
+
...rest,
|
|
755
|
+
properties: propertiesResult
|
|
756
|
+
};
|
|
743
757
|
}
|
|
744
758
|
|
|
745
759
|
function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
|
|
@@ -759,7 +773,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
759
773
|
Object.keys(internalProperty.properties).forEach((key) => {
|
|
760
774
|
properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
|
|
761
775
|
});
|
|
762
|
-
internalProperty = {
|
|
776
|
+
internalProperty = {
|
|
777
|
+
...internalProperty,
|
|
778
|
+
properties
|
|
779
|
+
};
|
|
763
780
|
}
|
|
764
781
|
|
|
765
782
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { EntityCollection, unslugify, } from "@firecms/core";
|
|
3
|
-
import { Button, Card, Chip, CircularProgress,
|
|
3
|
+
import { Button, Card, Chip, CircularProgress, cls, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
|
|
4
4
|
|
|
5
5
|
import { productsCollectionTemplate } from "./templates/products_template";
|
|
6
6
|
import { blogCollectionTemplate } from "./templates/blog_template";
|
|
@@ -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
|
|
|
@@ -188,9 +188,9 @@ export function TemplateButton({
|
|
|
188
188
|
<Tooltip title={subtitle}>
|
|
189
189
|
<Card
|
|
190
190
|
onClick={onClick}
|
|
191
|
-
className={
|
|
191
|
+
className={cls(
|
|
192
192
|
"my-2 rounded-md border mx-0 p-6 px-4 focus:outline-none transition ease-in-out duration-150 flex flex-row gap-4 items-center",
|
|
193
|
-
"text-gray-700 dark:text-
|
|
193
|
+
"text-gray-700 dark:text-slate-300",
|
|
194
194
|
"hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
|
|
195
195
|
"border-gray-400 dark:border-gray-600 "
|
|
196
196
|
)}
|