@firecms/collection_editor 3.0.0-canary.28 → 3.0.0-canary.280
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 +114 -21
- package/README.md +165 -1
- package/dist/ConfigControllerProvider.d.ts +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +10792 -4791
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +11458 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +3 -2
- package/dist/types/collection_inference.d.ts +4 -1
- package/dist/types/config_controller.d.ts +3 -1
- package/dist/types/config_permissions.d.ts +2 -2
- package/dist/types/persisted_collection.d.ts +1 -1
- package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/EditorEntityAction.d.ts +2 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -2
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +2 -2
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +4 -0
- package/dist/ui/collection_editor/EntityActionsSelectDialog.d.ts +4 -0
- package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
- package/dist/ui/collection_editor/PropertyTree.d.ts +11 -12
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +8 -1
- package/dist/ui/collection_editor/import/clean_import_data.d.ts +1 -1
- package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
- package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/useCollectionEditorPlugin.d.ts +8 -11
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +25 -37
- package/src/ConfigControllerProvider.tsx +64 -66
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +6 -5
- package/src/types/collection_inference.ts +4 -1
- package/src/types/config_controller.tsx +4 -1
- package/src/types/config_permissions.ts +1 -1
- package/src/types/persisted_collection.ts +2 -3
- package/src/ui/CollectionViewHeaderAction.tsx +10 -5
- package/src/ui/EditorCollectionAction.tsx +12 -70
- package/src/ui/EditorCollectionActionStart.tsx +87 -0
- package/src/ui/EditorEntityAction.tsx +51 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +21 -14
- package/src/ui/NewCollectionButton.tsx +1 -1
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/PropertyAddColumnComponent.tsx +11 -6
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +157 -50
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +119 -39
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +24 -33
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +46 -41
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +163 -0
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +41 -0
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +11 -7
- package/src/ui/collection_editor/EnumForm.tsx +11 -7
- package/src/ui/collection_editor/GetCodeDialog.tsx +60 -28
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +266 -79
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +8 -10
- package/src/ui/collection_editor/PropertyTree.tsx +184 -138
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +9 -7
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +41 -9
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +43 -10
- package/src/ui/collection_editor/import/clean_import_data.ts +1 -1
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +7 -3
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +4 -9
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
- package/src/ui/collection_editor/templates/pages_template.ts +1 -6
- package/src/ui/collection_editor/utils/strings.ts +13 -6
- package/src/ui/collection_editor/utils/supported_fields.tsx +2 -0
- package/src/ui/collection_editor/utils/update_property_for_widget.ts +37 -6
- package/src/useCollectionEditorPlugin.tsx +38 -32
- package/src/utils/collections.ts +46 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
- package/src/ui/collection_editor/PropertySelectItem.tsx +0 -32
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
CircularProgressCenter,
|
|
5
|
+
ConfirmationDialog,
|
|
6
|
+
Entity,
|
|
5
7
|
EntityCollection,
|
|
6
8
|
ErrorView,
|
|
7
9
|
isPropertyBuilder,
|
|
8
10
|
MapProperty,
|
|
9
11
|
mergeDeep,
|
|
12
|
+
NavigationResult,
|
|
10
13
|
Properties,
|
|
11
14
|
PropertiesOrBuilders,
|
|
12
15
|
Property,
|
|
@@ -15,7 +18,6 @@ import {
|
|
|
15
18
|
randomString,
|
|
16
19
|
removeInitialAndTrailingSlashes,
|
|
17
20
|
removeUndefined,
|
|
18
|
-
TopNavigationResult,
|
|
19
21
|
useAuthController,
|
|
20
22
|
useCustomizationController,
|
|
21
23
|
useNavigationController,
|
|
@@ -25,17 +27,19 @@ import {
|
|
|
25
27
|
import {
|
|
26
28
|
ArrowBackIcon,
|
|
27
29
|
Button,
|
|
28
|
-
|
|
30
|
+
CheckIcon,
|
|
31
|
+
cls,
|
|
29
32
|
coolIconKeys,
|
|
30
33
|
defaultBorderMixin,
|
|
31
34
|
Dialog,
|
|
32
35
|
DialogActions,
|
|
33
36
|
DialogContent,
|
|
34
|
-
|
|
37
|
+
DialogTitle,
|
|
35
38
|
IconButton,
|
|
36
39
|
LoadingButton,
|
|
37
40
|
Tab,
|
|
38
|
-
Tabs
|
|
41
|
+
Tabs,
|
|
42
|
+
Typography
|
|
39
43
|
} from "@firecms/ui";
|
|
40
44
|
import { YupSchema } from "./CollectionYupValidation";
|
|
41
45
|
import { CollectionDetailsForm } from "./CollectionDetailsForm";
|
|
@@ -45,7 +49,7 @@ import { SubcollectionsEditTab } from "./SubcollectionsEditTab";
|
|
|
45
49
|
import { CollectionsConfigController } from "../../types/config_controller";
|
|
46
50
|
import { CollectionEditorWelcomeView } from "./CollectionEditorWelcomeView";
|
|
47
51
|
import { CollectionInference } from "../../types/collection_inference";
|
|
48
|
-
import { getInferenceType, ImportSaveInProgress, useImportConfig } from "@firecms/
|
|
52
|
+
import { getInferenceType, ImportSaveInProgress, useImportConfig } from "@firecms/data_import";
|
|
49
53
|
import { buildEntityPropertiesFromData } from "@firecms/schema_inference";
|
|
50
54
|
import { CollectionEditorImportMapping } from "./import/CollectionEditorImportMapping";
|
|
51
55
|
import { CollectionEditorImportDataPreview } from "./import/CollectionEditorImportDataPreview";
|
|
@@ -53,6 +57,7 @@ import { cleanPropertiesFromImport } from "./import/clean_import_data";
|
|
|
53
57
|
import { PersistedCollection } from "../../types/persisted_collection";
|
|
54
58
|
import { Formex, FormexController, useCreateFormex } from "@firecms/formex";
|
|
55
59
|
import { getFullIdPath } from "./util";
|
|
60
|
+
import { EntityActionsEditTab } from "./EntityActionsEditTab";
|
|
56
61
|
|
|
57
62
|
export interface CollectionEditorDialogProps {
|
|
58
63
|
open: boolean;
|
|
@@ -76,9 +81,10 @@ export interface CollectionEditorDialogProps {
|
|
|
76
81
|
icon: React.ReactNode
|
|
77
82
|
};
|
|
78
83
|
pathSuggestions?: (path?: string) => Promise<string[]>;
|
|
79
|
-
getUser
|
|
84
|
+
getUser?: (uid: string) => User | null;
|
|
80
85
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
81
86
|
parentCollection?: PersistedCollection;
|
|
87
|
+
existingEntities?: Entity<any>[];
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
@@ -88,13 +94,13 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
88
94
|
const [formDirty, setFormDirty] = React.useState<boolean>(false);
|
|
89
95
|
const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
|
|
90
96
|
|
|
91
|
-
const handleCancel =
|
|
97
|
+
const handleCancel = () => {
|
|
92
98
|
if (!formDirty) {
|
|
93
99
|
props.handleClose(undefined);
|
|
94
100
|
} else {
|
|
95
101
|
setUnsavedChangesDialogOpen(true);
|
|
96
102
|
}
|
|
97
|
-
}
|
|
103
|
+
};
|
|
98
104
|
|
|
99
105
|
useEffect(() => {
|
|
100
106
|
if (!open) {
|
|
@@ -112,6 +118,7 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
112
118
|
maxWidth={"7xl"}
|
|
113
119
|
onOpenChange={(open) => !open ? handleCancel() : undefined}
|
|
114
120
|
>
|
|
121
|
+
<DialogTitle hidden>Collection editor</DialogTitle>
|
|
115
122
|
{open && <CollectionEditor {...props}
|
|
116
123
|
handleCancel={handleCancel}
|
|
117
124
|
setFormDirty={setFormDirty}/>}
|
|
@@ -134,7 +141,8 @@ type EditorView = "welcome"
|
|
|
134
141
|
| "properties"
|
|
135
142
|
| "loading"
|
|
136
143
|
| "extra_view"
|
|
137
|
-
| "subcollections"
|
|
144
|
+
| "subcollections"
|
|
145
|
+
| "custom_actions";
|
|
138
146
|
|
|
139
147
|
export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
140
148
|
handleCancel: () => void,
|
|
@@ -170,14 +178,15 @@ export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
|
170
178
|
} catch (e) {
|
|
171
179
|
console.error(e);
|
|
172
180
|
}
|
|
173
|
-
}, [
|
|
181
|
+
}, [props.editedCollectionId, props.parentCollectionIds, navigation.initialised, navigation.getCollectionFromPaths]);
|
|
182
|
+
|
|
174
183
|
if (!topLevelNavigation) {
|
|
175
184
|
throw Error("Internal: Navigation not ready in collection editor");
|
|
176
185
|
}
|
|
177
186
|
|
|
178
187
|
const {
|
|
179
188
|
groups
|
|
180
|
-
}:
|
|
189
|
+
}: NavigationResult = topLevelNavigation;
|
|
181
190
|
|
|
182
191
|
const initialCollection = collection
|
|
183
192
|
? {
|
|
@@ -243,7 +252,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
243
252
|
setCollection,
|
|
244
253
|
initialValues,
|
|
245
254
|
propertyConfigs,
|
|
246
|
-
groups
|
|
255
|
+
groups,
|
|
256
|
+
existingEntities
|
|
247
257
|
}: CollectionEditorDialogProps & {
|
|
248
258
|
handleCancel: () => void,
|
|
249
259
|
setFormDirty: (dirty: boolean) => void,
|
|
@@ -272,6 +282,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
272
282
|
|
|
273
283
|
const saveCollection = (updatedCollection: PersistedCollection<M>): Promise<boolean> => {
|
|
274
284
|
const id = updatedCollection.id || updatedCollection.path;
|
|
285
|
+
|
|
275
286
|
return configController.saveCollection({
|
|
276
287
|
id,
|
|
277
288
|
collectionData: updatedCollection,
|
|
@@ -293,7 +304,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
293
304
|
});
|
|
294
305
|
};
|
|
295
306
|
|
|
296
|
-
const setNextMode =
|
|
307
|
+
const setNextMode = () => {
|
|
297
308
|
if (currentView === "details") {
|
|
298
309
|
if (importConfig.inUse) {
|
|
299
310
|
setCurrentView("import_data_saving");
|
|
@@ -314,14 +325,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
314
325
|
setCurrentView("details");
|
|
315
326
|
}
|
|
316
327
|
|
|
317
|
-
}
|
|
328
|
+
};
|
|
318
329
|
|
|
319
|
-
const doCollectionInference =
|
|
330
|
+
const doCollectionInference = collectionInference ? (collection: PersistedCollection<any>) => {
|
|
320
331
|
if (!collectionInference) return undefined;
|
|
321
|
-
return collectionInference?.(collection.path, collection.collectionGroup ?? false,
|
|
322
|
-
}
|
|
332
|
+
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentPaths ?? [], collection.databaseId);
|
|
333
|
+
} : undefined;
|
|
323
334
|
|
|
324
|
-
const inferCollectionFromData =
|
|
335
|
+
const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
|
|
325
336
|
|
|
326
337
|
try {
|
|
327
338
|
if (!doCollectionInference) {
|
|
@@ -365,15 +376,15 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
365
376
|
});
|
|
366
377
|
return newCollection;
|
|
367
378
|
}
|
|
368
|
-
}
|
|
379
|
+
};
|
|
369
380
|
|
|
370
381
|
const onSubmit = (newCollectionState: PersistedCollection<M>, formexController: FormexController<PersistedCollection<M>>) => {
|
|
371
|
-
console.
|
|
382
|
+
console.debug("Submitting collection", newCollectionState);
|
|
372
383
|
try {
|
|
373
384
|
|
|
374
385
|
if (!isNewCollection) {
|
|
375
386
|
saveCollection(newCollectionState).then(() => {
|
|
376
|
-
formexController.resetForm(
|
|
387
|
+
formexController.resetForm();
|
|
377
388
|
handleClose(newCollectionState);
|
|
378
389
|
});
|
|
379
390
|
return;
|
|
@@ -462,7 +473,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
462
473
|
const formController = useCreateFormex<PersistedCollection<M>>({
|
|
463
474
|
initialValues,
|
|
464
475
|
onSubmit,
|
|
465
|
-
validation
|
|
476
|
+
validation,
|
|
477
|
+
debugId: "COLLECTION_EDITOR"
|
|
466
478
|
});
|
|
467
479
|
|
|
468
480
|
const {
|
|
@@ -479,26 +491,37 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
479
491
|
const pathError = validatePath(path, isNewCollection, existingPaths, values.id);
|
|
480
492
|
|
|
481
493
|
const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
|
|
482
|
-
const resolvedPath = !pathError ? navigation.
|
|
483
|
-
const getDataWithPath = resolvedPath && getData ? () =>
|
|
494
|
+
const resolvedPath = !pathError ? navigation.resolveIdsFrom(updatedFullPath) : undefined;
|
|
495
|
+
const getDataWithPath = resolvedPath && getData ? async () => {
|
|
496
|
+
const data = await getData(resolvedPath, parentPaths ?? []);
|
|
497
|
+
if (existingEntities) {
|
|
498
|
+
const existingData = existingEntities.map(e => e.values);
|
|
499
|
+
data.push(...existingData);
|
|
500
|
+
}
|
|
501
|
+
return data;
|
|
502
|
+
} : undefined;
|
|
484
503
|
|
|
485
504
|
useEffect(() => {
|
|
486
505
|
setFormDirty(dirty);
|
|
487
506
|
}, [dirty]);
|
|
488
507
|
|
|
489
|
-
function onImportDataSet(data: object[]) {
|
|
508
|
+
function onImportDataSet(data: object[], propertiesOrder?: string[]) {
|
|
490
509
|
importConfig.setInUse(true);
|
|
491
510
|
buildEntityPropertiesFromData(data, getInferenceType)
|
|
492
|
-
.then((properties) => {
|
|
511
|
+
.then((properties: Properties) => {
|
|
493
512
|
const res = cleanPropertiesFromImport(properties);
|
|
494
513
|
|
|
495
|
-
setFieldValue("properties", res.properties);
|
|
496
|
-
setFieldValue("propertiesOrder", Object.keys(res.properties));
|
|
497
|
-
|
|
498
514
|
importConfig.setIdColumn(res.idColumn);
|
|
499
515
|
importConfig.setImportData(data);
|
|
500
516
|
importConfig.setHeadersMapping(res.headersMapping);
|
|
517
|
+
const filteredHeadingsOrder = ((propertiesOrder ?? [])
|
|
518
|
+
.filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
|
|
519
|
+
importConfig.setHeadingsOrder(filteredHeadingsOrder);
|
|
501
520
|
importConfig.setOriginProperties(res.properties);
|
|
521
|
+
|
|
522
|
+
const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
|
|
523
|
+
setFieldValue("properties", res.properties);
|
|
524
|
+
setFieldValue("propertiesOrder", mappedHeadings);
|
|
502
525
|
});
|
|
503
526
|
}
|
|
504
527
|
|
|
@@ -514,14 +537,30 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
514
537
|
};
|
|
515
538
|
|
|
516
539
|
const editable = collection?.editable === undefined || collection?.editable === true;
|
|
540
|
+
// @ts-ignore
|
|
541
|
+
const isMergedCollection = collection?.merged ?? false;
|
|
517
542
|
const collectionEditable = editable || isNewCollection;
|
|
518
543
|
|
|
544
|
+
const [deleteRequested, setDeleteRequested] = useState(false);
|
|
545
|
+
|
|
546
|
+
const deleteCollection = () => {
|
|
547
|
+
if (!collection) return;
|
|
548
|
+
configController?.deleteCollection({ id: collection.id }).then(() => {
|
|
549
|
+
setDeleteRequested(false);
|
|
550
|
+
handleCancel();
|
|
551
|
+
snackbarController.open({
|
|
552
|
+
message: "Collection deleted",
|
|
553
|
+
type: "success"
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
};
|
|
557
|
+
|
|
519
558
|
return <DialogContent fullHeight={true}>
|
|
520
559
|
<Formex value={formController}>
|
|
521
560
|
|
|
522
561
|
<>
|
|
523
562
|
{!isNewCollection && <Tabs value={currentView}
|
|
524
|
-
|
|
563
|
+
innerClassName={cls(defaultBorderMixin, "px-4 h-14 w-full justify-end bg-surface-50 dark:bg-surface-950 border-b")}
|
|
525
564
|
onValueChange={(v) => setCurrentView(v as EditorView)}>
|
|
526
565
|
<Tab value={"details"}>
|
|
527
566
|
Details
|
|
@@ -532,11 +571,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
532
571
|
<Tab value={"subcollections"}>
|
|
533
572
|
Additional views
|
|
534
573
|
</Tab>
|
|
574
|
+
<Tab value={"custom_actions"}>
|
|
575
|
+
Custom actions
|
|
576
|
+
</Tab>
|
|
535
577
|
</Tabs>}
|
|
536
578
|
|
|
537
579
|
<form noValidate
|
|
538
580
|
onSubmit={formController.handleSubmit}
|
|
539
|
-
className={
|
|
581
|
+
className={cls(
|
|
540
582
|
isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
|
|
541
583
|
"flex-grow flex flex-col relative")}>
|
|
542
584
|
|
|
@@ -551,9 +593,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
551
593
|
{currentView === "welcome" &&
|
|
552
594
|
<CollectionEditorWelcomeView
|
|
553
595
|
path={path}
|
|
554
|
-
onContinue={(importData) => {
|
|
596
|
+
onContinue={(importData, propertiesOrder) => {
|
|
597
|
+
// console.log("Import data", importData, propertiesOrder)
|
|
555
598
|
if (importData) {
|
|
556
|
-
onImportDataSet(importData);
|
|
599
|
+
onImportDataSet(importData, propertiesOrder);
|
|
557
600
|
setCurrentView("import_data_mapping");
|
|
558
601
|
} else {
|
|
559
602
|
setCurrentView("details");
|
|
@@ -594,7 +637,21 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
594
637
|
groups={groups}
|
|
595
638
|
parentCollectionIds={parentCollectionIds}
|
|
596
639
|
parentCollection={parentCollection}
|
|
597
|
-
isNewCollection={isNewCollection}
|
|
640
|
+
isNewCollection={isNewCollection}>
|
|
641
|
+
{!isNewCollection && isMergedCollection && <div className={"flex flex-col gap-4 mt-8"}>
|
|
642
|
+
<Typography variant={"body2"} color={"secondary"}>This collection is defined in code.
|
|
643
|
+
The changes done in this editor will override the properties defined in code.
|
|
644
|
+
You can delete the overridden values to revert to the state defined in code.
|
|
645
|
+
</Typography>
|
|
646
|
+
<Button color={"neutral"}
|
|
647
|
+
onClick={() => {
|
|
648
|
+
setDeleteRequested(true);
|
|
649
|
+
}}>Reset to code</Button>
|
|
650
|
+
</div>}
|
|
651
|
+
</CollectionDetailsForm>}
|
|
652
|
+
|
|
653
|
+
{currentView === "custom_actions" && collection &&
|
|
654
|
+
<EntityActionsEditTab collection={collection}/>}
|
|
598
655
|
|
|
599
656
|
{currentView === "subcollections" && collection &&
|
|
600
657
|
<SubcollectionsEditTab
|
|
@@ -638,6 +695,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
638
695
|
{isNewCollection && includeTemplates && currentView === "import_data_mapping" &&
|
|
639
696
|
<Button variant={"text"}
|
|
640
697
|
type="button"
|
|
698
|
+
color={"primary"}
|
|
641
699
|
onClick={() => {
|
|
642
700
|
importConfig.setInUse(false);
|
|
643
701
|
return setCurrentView("welcome");
|
|
@@ -649,6 +707,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
649
707
|
{isNewCollection && includeTemplates && currentView === "import_data_preview" &&
|
|
650
708
|
<Button variant={"text"}
|
|
651
709
|
type="button"
|
|
710
|
+
color={"primary"}
|
|
652
711
|
onClick={() => {
|
|
653
712
|
setCurrentView("import_data_mapping");
|
|
654
713
|
}}>
|
|
@@ -658,6 +717,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
658
717
|
|
|
659
718
|
{isNewCollection && includeTemplates && currentView === "details" &&
|
|
660
719
|
<Button variant={"text"}
|
|
720
|
+
color={"primary"}
|
|
661
721
|
type="button"
|
|
662
722
|
onClick={() => setCurrentView("welcome")}>
|
|
663
723
|
<ArrowBackIcon/>
|
|
@@ -666,12 +726,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
666
726
|
|
|
667
727
|
{isNewCollection && currentView === "properties" && <Button variant={"text"}
|
|
668
728
|
type="button"
|
|
729
|
+
color={"primary"}
|
|
669
730
|
onClick={() => setCurrentView("details")}>
|
|
670
731
|
<ArrowBackIcon/>
|
|
671
732
|
Back
|
|
672
733
|
</Button>}
|
|
673
734
|
|
|
674
735
|
<Button variant={"text"}
|
|
736
|
+
color={"primary"}
|
|
675
737
|
onClick={() => {
|
|
676
738
|
handleCancel();
|
|
677
739
|
}}>
|
|
@@ -706,7 +768,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
706
768
|
loading={isSubmitting}
|
|
707
769
|
disabled={isSubmitting || (currentView === "details" && !validValues)}
|
|
708
770
|
startIcon={currentView === "properties"
|
|
709
|
-
? <
|
|
771
|
+
? <CheckIcon/>
|
|
710
772
|
: undefined}
|
|
711
773
|
>
|
|
712
774
|
{currentView === "details" && "Next"}
|
|
@@ -728,12 +790,24 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
728
790
|
|
|
729
791
|
</Formex>
|
|
730
792
|
|
|
793
|
+
<ConfirmationDialog
|
|
794
|
+
open={deleteRequested}
|
|
795
|
+
onAccept={deleteCollection}
|
|
796
|
+
onCancel={() => setDeleteRequested(false)}
|
|
797
|
+
title={<>Delete the stored config?</>}
|
|
798
|
+
body={<> This will <b>not
|
|
799
|
+
delete any data</b>, only
|
|
800
|
+
the stored config, and reset to the code state.</>}/>
|
|
801
|
+
|
|
731
802
|
</DialogContent>
|
|
732
803
|
|
|
733
804
|
}
|
|
734
805
|
|
|
735
806
|
function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
|
|
736
|
-
const {
|
|
807
|
+
const {
|
|
808
|
+
properties,
|
|
809
|
+
...rest
|
|
810
|
+
} = collection;
|
|
737
811
|
const propertiesResult: PropertiesOrBuilders<any> = {};
|
|
738
812
|
if (properties) {
|
|
739
813
|
Object.keys(properties).forEach((key) => {
|
|
@@ -741,7 +815,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
|
|
|
741
815
|
});
|
|
742
816
|
}
|
|
743
817
|
|
|
744
|
-
return {
|
|
818
|
+
return {
|
|
819
|
+
...rest,
|
|
820
|
+
properties: propertiesResult
|
|
821
|
+
};
|
|
745
822
|
}
|
|
746
823
|
|
|
747
824
|
function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
|
|
@@ -761,7 +838,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
761
838
|
Object.keys(internalProperty.properties).forEach((key) => {
|
|
762
839
|
properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
|
|
763
840
|
});
|
|
764
|
-
internalProperty = {
|
|
841
|
+
internalProperty = {
|
|
842
|
+
...internalProperty,
|
|
843
|
+
properties
|
|
844
|
+
};
|
|
765
845
|
}
|
|
766
846
|
|
|
767
847
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
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";
|
|
7
7
|
import { usersCollectionTemplate } from "./templates/users_template";
|
|
8
|
-
import { ImportFileUpload } from "@firecms/
|
|
8
|
+
import { ImportFileUpload } from "@firecms/data_import";
|
|
9
9
|
import { pagesCollectionTemplate } from "./templates/pages_template";
|
|
10
10
|
import { useFormex } from "@firecms/formex";
|
|
11
11
|
|
|
@@ -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
|
|
|
@@ -37,18 +37,6 @@ export function CollectionEditorWelcomeView({
|
|
|
37
37
|
}
|
|
38
38
|
}, [existingCollectionPaths, path, pathSuggestions]);
|
|
39
39
|
|
|
40
|
-
// const {
|
|
41
|
-
// values,
|
|
42
|
-
// setFieldValue,
|
|
43
|
-
// setValues,
|
|
44
|
-
// handleChange,
|
|
45
|
-
// touched,
|
|
46
|
-
// errors,
|
|
47
|
-
// setFieldTouched,
|
|
48
|
-
// isSubmitting,
|
|
49
|
-
// submitCount
|
|
50
|
-
// } = useFormex<EntityCollection>();
|
|
51
|
-
|
|
52
40
|
const {
|
|
53
41
|
values,
|
|
54
42
|
setFieldValue,
|
|
@@ -56,6 +44,11 @@ export function CollectionEditorWelcomeView({
|
|
|
56
44
|
submitCount
|
|
57
45
|
} = useFormex<EntityCollection>();
|
|
58
46
|
|
|
47
|
+
const noSuggestions = !loadingPathSuggestions && (filteredPathSuggestions ?? [])?.length === 0;
|
|
48
|
+
if (!noSuggestions) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
59
52
|
return (
|
|
60
53
|
<div className={"overflow-auto my-auto"}>
|
|
61
54
|
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
@@ -96,12 +89,11 @@ export function CollectionEditorWelcomeView({
|
|
|
96
89
|
{suggestion}
|
|
97
90
|
</Chip>
|
|
98
91
|
))}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
92
|
+
{(filteredPathSuggestions ?? []).length === 0 && !loadingPathSuggestions && <Typography
|
|
93
|
+
variant={"caption"}
|
|
94
|
+
color={"secondary"}>
|
|
95
|
+
No existing paths found
|
|
96
|
+
</Typography>}
|
|
105
97
|
|
|
106
98
|
</div>
|
|
107
99
|
|
|
@@ -116,28 +108,29 @@ export function CollectionEditorWelcomeView({
|
|
|
116
108
|
<div className={"flex gap-4"}>
|
|
117
109
|
<TemplateButton title={"Products"}
|
|
118
110
|
subtitle={"A collection of products with images, prices and stock"}
|
|
119
|
-
icon={<Icon size={"small"}
|
|
111
|
+
icon={<Icon size={"small"}
|
|
112
|
+
iconKey={productsCollectionTemplate.icon! as string}/>}
|
|
120
113
|
onClick={() => {
|
|
121
114
|
setValues(productsCollectionTemplate);
|
|
122
115
|
onContinue();
|
|
123
116
|
}}/>
|
|
124
117
|
<TemplateButton title={"Users"}
|
|
125
118
|
subtitle={"A collection of users with emails, names and roles"}
|
|
126
|
-
icon={<Icon size={"small"} iconKey={usersCollectionTemplate.icon!}/>}
|
|
119
|
+
icon={<Icon size={"small"} iconKey={usersCollectionTemplate.icon! as string}/>}
|
|
127
120
|
onClick={() => {
|
|
128
121
|
setValues(usersCollectionTemplate);
|
|
129
122
|
onContinue();
|
|
130
123
|
}}/>
|
|
131
124
|
<TemplateButton title={"Blog posts"}
|
|
132
125
|
subtitle={"A collection of blog posts with images, authors and complex content"}
|
|
133
|
-
icon={<Icon size={"small"} iconKey={blogCollectionTemplate.icon!}/>}
|
|
126
|
+
icon={<Icon size={"small"} iconKey={blogCollectionTemplate.icon! as string}/>}
|
|
134
127
|
onClick={() => {
|
|
135
128
|
setValues(blogCollectionTemplate);
|
|
136
129
|
onContinue();
|
|
137
130
|
}}/>
|
|
138
131
|
<TemplateButton title={"Pages"}
|
|
139
132
|
subtitle={"A collection of pages with images, authors and complex content"}
|
|
140
|
-
icon={<Icon size={"small"} iconKey={pagesCollectionTemplate.icon!}/>}
|
|
133
|
+
icon={<Icon size={"small"} iconKey={pagesCollectionTemplate.icon! as string}/>}
|
|
141
134
|
onClick={() => {
|
|
142
135
|
setValues(pagesCollectionTemplate);
|
|
143
136
|
onContinue();
|
|
@@ -154,7 +147,7 @@ export function CollectionEditorWelcomeView({
|
|
|
154
147
|
● Create a collection from a file (csv, json, xls, xslx...)
|
|
155
148
|
</Typography>
|
|
156
149
|
|
|
157
|
-
<ImportFileUpload onDataAdded={(data) => onContinue(data)}/>
|
|
150
|
+
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
|
|
158
151
|
|
|
159
152
|
</div>}
|
|
160
153
|
|
|
@@ -185,14 +178,15 @@ export function TemplateButton({
|
|
|
185
178
|
}) {
|
|
186
179
|
|
|
187
180
|
return (
|
|
188
|
-
<Tooltip title={subtitle}
|
|
181
|
+
<Tooltip title={subtitle}
|
|
182
|
+
asChild={true}>
|
|
189
183
|
<Card
|
|
190
184
|
onClick={onClick}
|
|
191
|
-
className={
|
|
185
|
+
className={cls(
|
|
192
186
|
"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-
|
|
187
|
+
"text-surface-700 dark:text-surface-accent-300",
|
|
194
188
|
"hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
|
|
195
|
-
"border-
|
|
189
|
+
"border-surface-400 dark:border-surface-600 "
|
|
196
190
|
)}
|
|
197
191
|
>
|
|
198
192
|
{icon}
|
|
@@ -201,9 +195,6 @@ export function TemplateButton({
|
|
|
201
195
|
<Typography variant={"subtitle1"}>
|
|
202
196
|
{title}
|
|
203
197
|
</Typography>
|
|
204
|
-
{/*<Typography>*/}
|
|
205
|
-
{/* {subtitle}*/}
|
|
206
|
-
{/*</Typography>*/}
|
|
207
198
|
|
|
208
199
|
</div>
|
|
209
200
|
</Card>
|