@firecms/collection_editor 3.0.0-beta.2-pre.4 → 3.0.0-beta.2
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/dist/index.es.js +1273 -1222
- 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/ui/RootCollectionSuggestions.d.ts +3 -1
- package/dist/useCollectionEditorPlugin.d.ts +6 -3
- package/package.json +7 -7
- package/src/ui/RootCollectionSuggestions.tsx +16 -9
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +25 -25
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +6 -9
- package/src/ui/collection_editor/GetCodeDialog.tsx +1 -1
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +69 -66
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +1 -1
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +10 -7
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +18 -16
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +1 -1
- package/src/useCollectionEditorPlugin.tsx +50 -16
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { FireCMSPlugin, User } from "@firecms/core";
|
|
3
3
|
import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
|
|
4
4
|
import { PersistedCollection } from "./types/persisted_collection";
|
|
5
5
|
import { CollectionInference } from "./types/collection_inference";
|
|
@@ -9,7 +9,6 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
9
9
|
* Firebase app where the configuration is saved.
|
|
10
10
|
*/
|
|
11
11
|
collectionConfigController: CollectionsConfigController;
|
|
12
|
-
modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void;
|
|
13
12
|
/**
|
|
14
13
|
* Define what actions can be performed on the configuration.
|
|
15
14
|
*/
|
|
@@ -31,6 +30,7 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
31
30
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
32
31
|
getUser: (uid: string) => UserType | null;
|
|
33
32
|
onAnalyticsEvent?: (event: string, params?: object) => void;
|
|
33
|
+
introMode?: "new_project" | "existing_project";
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Use this hook to initialise the Collection Editor plugin.
|
|
@@ -43,4 +43,7 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
43
43
|
* @param getUser
|
|
44
44
|
* @param collectionInference
|
|
45
45
|
*/
|
|
46
|
-
export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController,
|
|
46
|
+
export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController, introMode, configPermissions, reservedGroups, extraView, pathSuggestions, getUser, collectionInference, getData, onAnalyticsEvent }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection>;
|
|
47
|
+
export declare function IntroWidget({ introMode }: {
|
|
48
|
+
introMode?: "new_project" | "existing_project";
|
|
49
|
+
}): import("react/jsx-runtime").JSX.Element;
|
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.2
|
|
4
|
+
"version": "3.0.0-beta.2",
|
|
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.2
|
|
11
|
-
"@firecms/formex": "^3.0.0-beta.2
|
|
12
|
-
"@firecms/schema_inference": "^3.0.0-beta.2
|
|
13
|
-
"@firecms/ui": "^3.0.0-beta.2
|
|
10
|
+
"@firecms/data_import_export": "^3.0.0-beta.2",
|
|
11
|
+
"@firecms/formex": "^3.0.0-beta.2",
|
|
12
|
+
"@firecms/schema_inference": "^3.0.0-beta.2",
|
|
13
|
+
"@firecms/ui": "^3.0.0-beta.2",
|
|
14
14
|
"json5": "^2.2.3",
|
|
15
15
|
"prism-react-renderer": "^2.3.1"
|
|
16
16
|
},
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"react-router-dom": "^6.22.0",
|
|
64
64
|
"ts-jest": "^29.1.2",
|
|
65
65
|
"typescript": "^5.3.3",
|
|
66
|
-
"vite": "^
|
|
66
|
+
"vite": "^5.1.1",
|
|
67
67
|
"vite-plugin-fonts": "^0.7.0"
|
|
68
68
|
},
|
|
69
69
|
"files": [
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"publishConfig": {
|
|
74
74
|
"access": "public"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "9c3561bc09311f2339bb6fa224c88a62d3b19617"
|
|
77
77
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { unslugify, useAuthController, useNavigationController } from "@firecms/core";
|
|
2
|
-
import { AddIcon, Chip, Collapse, Typography, } from "@firecms/ui";
|
|
2
|
+
import { AddIcon, Chip, CircularProgress, Collapse, Typography, } from "@firecms/ui";
|
|
3
3
|
import { useCollectionEditorController } from "../useCollectionEditorController";
|
|
4
4
|
import React from "react";
|
|
5
5
|
|
|
6
|
-
export function RootCollectionSuggestions() {
|
|
6
|
+
export function RootCollectionSuggestions({ introMode }: { introMode?: "new_project" | "existing_project" }) {
|
|
7
7
|
|
|
8
8
|
const authController = useAuthController();
|
|
9
9
|
const navigationController = useNavigationController();
|
|
@@ -15,22 +15,27 @@ export function RootCollectionSuggestions() {
|
|
|
15
15
|
}).createCollections
|
|
16
16
|
: true;
|
|
17
17
|
|
|
18
|
-
const rootPathSuggestions = collectionEditorController.rootPathSuggestions
|
|
18
|
+
const rootPathSuggestions = collectionEditorController.rootPathSuggestions;
|
|
19
19
|
|
|
20
|
-
const showSuggestions = rootPathSuggestions.length > 3 || ((navigationController.collections ?? []).length === 0 && rootPathSuggestions.length > 0);
|
|
20
|
+
const showSuggestions = (rootPathSuggestions ?? []).length > 3 || ((navigationController.collections ?? []).length === 0 && (rootPathSuggestions ?? []).length > 0);
|
|
21
|
+
const forceShowSuggestions = introMode === "existing_project";
|
|
21
22
|
return <Collapse
|
|
22
|
-
in={showSuggestions}>
|
|
23
|
+
in={forceShowSuggestions || showSuggestions}>
|
|
23
24
|
|
|
24
25
|
<div
|
|
25
26
|
className={"flex flex-col gap-1 p-2 my-4"}>
|
|
26
27
|
|
|
27
|
-
<Typography variant={"body2"} color={"secondary"}>
|
|
28
|
-
Create a collection from your data:
|
|
29
|
-
</Typography>
|
|
28
|
+
{!introMode && <Typography variant={"body2"} color={"secondary"}>
|
|
29
|
+
Create a collection <b>automatically</b> from your data:
|
|
30
|
+
</Typography>}
|
|
31
|
+
|
|
32
|
+
{introMode === "existing_project" && <Typography>
|
|
33
|
+
You will see your <b>database collections</b> here, a few seconds after project creation
|
|
34
|
+
</Typography>}
|
|
30
35
|
|
|
31
36
|
<div
|
|
32
37
|
className={"flex flex-row gap-1 overflow-scroll no-scrollbar "}>
|
|
33
|
-
{rootPathSuggestions.map((path) => {
|
|
38
|
+
{(rootPathSuggestions ?? []).map((path) => {
|
|
34
39
|
return (
|
|
35
40
|
<div key={path}>
|
|
36
41
|
<Chip
|
|
@@ -50,6 +55,8 @@ export function RootCollectionSuggestions() {
|
|
|
50
55
|
</div>
|
|
51
56
|
);
|
|
52
57
|
})}
|
|
58
|
+
{rootPathSuggestions === undefined && <CircularProgress size={"small"}/>}
|
|
59
|
+
{rootPathSuggestions?.length === 0 && <Typography variant={"caption"}>No suggestions</Typography>}
|
|
53
60
|
</div>
|
|
54
61
|
</div>
|
|
55
62
|
</Collapse>
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from "@firecms/ui";
|
|
22
22
|
|
|
23
23
|
import { FieldHelperView } from "./properties/FieldHelperView";
|
|
24
|
-
import {
|
|
24
|
+
import { Field, getIn, useFormex } from "@firecms/formex";
|
|
25
25
|
|
|
26
26
|
export function CollectionDetailsForm({
|
|
27
27
|
isNewCollection,
|
|
@@ -70,7 +70,7 @@ export function CollectionDetailsForm({
|
|
|
70
70
|
|
|
71
71
|
const singularNameTouched = getIn(touched, "singularName");
|
|
72
72
|
if (!singularNameTouched && isNewCollection && name) {
|
|
73
|
-
setFieldValue("singularName", singular(name))
|
|
73
|
+
setFieldValue("singularName", singular(name));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
};
|
|
@@ -96,17 +96,17 @@ export function CollectionDetailsForm({
|
|
|
96
96
|
const isSubcollection = !!parentCollection;
|
|
97
97
|
|
|
98
98
|
let customIdValue: "true" | "false" | "optional" | "code_defined" | undefined;
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
customIdValue = "optional";
|
|
108
|
-
}
|
|
99
|
+
if (typeof values.customId === "object") {
|
|
100
|
+
customIdValue = "code_defined";
|
|
101
|
+
} else if (values.customId === true) {
|
|
102
|
+
customIdValue = "true";
|
|
103
|
+
} else if (values.customId === false) {
|
|
104
|
+
customIdValue = "false";
|
|
105
|
+
} else if (values.customId === "optional") {
|
|
106
|
+
customIdValue = "optional";
|
|
109
107
|
}
|
|
108
|
+
|
|
109
|
+
const showErrors = submitCount > 0;
|
|
110
110
|
return (
|
|
111
111
|
<div className={"overflow-auto my-auto"}>
|
|
112
112
|
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
@@ -141,7 +141,7 @@ export function CollectionDetailsForm({
|
|
|
141
141
|
onChange={(e: any) => updateName(e.target.value)}
|
|
142
142
|
label={"Name"}
|
|
143
143
|
required
|
|
144
|
-
error={
|
|
144
|
+
error={showErrors && Boolean(errors.name)}/>
|
|
145
145
|
<FieldHelperView 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
|
</FieldHelperView>
|
|
@@ -153,7 +153,7 @@ export function CollectionDetailsForm({
|
|
|
153
153
|
label={"Path"}
|
|
154
154
|
disabled={!isNewCollection}
|
|
155
155
|
required
|
|
156
|
-
error={
|
|
156
|
+
error={showErrors && Boolean(errors.path)}/>
|
|
157
157
|
|
|
158
158
|
<FieldHelperView error={touched.path && Boolean(errors.path)}>
|
|
159
159
|
{touched.path && Boolean(errors.path)
|
|
@@ -165,7 +165,7 @@ export function CollectionDetailsForm({
|
|
|
165
165
|
|
|
166
166
|
{!isSubcollection && <div className={"col-span-12 sm:col-span-4 relative"}>
|
|
167
167
|
|
|
168
|
-
<TextField error={
|
|
168
|
+
<TextField error={showErrors && Boolean(errors.group)}
|
|
169
169
|
disabled={isSubmitting}
|
|
170
170
|
value={values.group ?? ""}
|
|
171
171
|
autoComplete="off"
|
|
@@ -191,7 +191,7 @@ export function CollectionDetailsForm({
|
|
|
191
191
|
})}
|
|
192
192
|
</Autocomplete>
|
|
193
193
|
<FieldHelperView>
|
|
194
|
-
{
|
|
194
|
+
{showErrors && Boolean(errors.group) ? errors.group : "Group of the collection"}
|
|
195
195
|
</FieldHelperView>
|
|
196
196
|
</div>}
|
|
197
197
|
|
|
@@ -215,7 +215,7 @@ export function CollectionDetailsForm({
|
|
|
215
215
|
as={DebouncedTextField}
|
|
216
216
|
disabled={!isNewCollection}
|
|
217
217
|
label={"Collection id"}
|
|
218
|
-
error={
|
|
218
|
+
error={showErrors && Boolean(errors.id)}/>
|
|
219
219
|
<FieldHelperView error={touched.id && Boolean(errors.id)}>
|
|
220
220
|
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
|
|
221
221
|
</FieldHelperView>
|
|
@@ -223,8 +223,8 @@ export function CollectionDetailsForm({
|
|
|
223
223
|
|
|
224
224
|
<div className={"col-span-12"}>
|
|
225
225
|
<TextField
|
|
226
|
-
error={
|
|
227
|
-
|
|
226
|
+
error={showErrors && Boolean(errors.singularName)}
|
|
227
|
+
name={"singularName"}
|
|
228
228
|
aria-describedby={"singularName-helper"}
|
|
229
229
|
onChange={(e) => {
|
|
230
230
|
setFieldTouched("singularName", true);
|
|
@@ -232,14 +232,14 @@ export function CollectionDetailsForm({
|
|
|
232
232
|
}}
|
|
233
233
|
value={values.singularName ?? ""}
|
|
234
234
|
label={"Singular name"}/>
|
|
235
|
-
<FieldHelperView error={
|
|
236
|
-
{
|
|
235
|
+
<FieldHelperView error={showErrors && Boolean(errors.singularName)}>
|
|
236
|
+
{showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
|
|
237
237
|
</FieldHelperView>
|
|
238
238
|
</div>
|
|
239
239
|
<div className={"col-span-12"}>
|
|
240
240
|
<TextField
|
|
241
|
-
error={
|
|
242
|
-
|
|
241
|
+
error={showErrors && Boolean(errors.description)}
|
|
242
|
+
name="description"
|
|
243
243
|
value={values.description ?? ""}
|
|
244
244
|
onChange={handleChange}
|
|
245
245
|
multiline
|
|
@@ -247,8 +247,8 @@ export function CollectionDetailsForm({
|
|
|
247
247
|
aria-describedby="description-helper-text"
|
|
248
248
|
label="Description"
|
|
249
249
|
/>
|
|
250
|
-
<FieldHelperView error={
|
|
251
|
-
{
|
|
250
|
+
<FieldHelperView error={showErrors && Boolean(errors.description)}>
|
|
251
|
+
{showErrors && Boolean(errors.description) ? errors.description : "Description of the collection, you can use markdown"}
|
|
252
252
|
</FieldHelperView>
|
|
253
253
|
</div>
|
|
254
254
|
|
|
@@ -374,7 +374,6 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
374
374
|
if (!isNewCollection) {
|
|
375
375
|
saveCollection(newCollectionState).then(() => {
|
|
376
376
|
formexController.resetForm({ values: initialValues });
|
|
377
|
-
// setNextMode();
|
|
378
377
|
handleClose(newCollectionState);
|
|
379
378
|
});
|
|
380
379
|
return;
|
|
@@ -482,7 +481,6 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
482
481
|
const resolvedPath = !pathError ? navigation.resolveAliasesFrom(updatedFullPath) : undefined;
|
|
483
482
|
const getDataWithPath = resolvedPath && getData ? () => getData(resolvedPath, parentPaths ?? []) : undefined;
|
|
484
483
|
|
|
485
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
486
484
|
useEffect(() => {
|
|
487
485
|
setFormDirty(dirty);
|
|
488
486
|
}, [dirty]);
|
|
@@ -552,9 +550,9 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
552
550
|
{currentView === "welcome" &&
|
|
553
551
|
<CollectionEditorWelcomeView
|
|
554
552
|
path={path}
|
|
555
|
-
onContinue={(
|
|
556
|
-
if (
|
|
557
|
-
onImportDataSet(
|
|
553
|
+
onContinue={(importData) => {
|
|
554
|
+
if (importData) {
|
|
555
|
+
onImportDataSet(importData);
|
|
558
556
|
setCurrentView("import_data_mapping");
|
|
559
557
|
} else {
|
|
560
558
|
setCurrentView("details");
|
|
@@ -577,12 +575,13 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
577
575
|
{currentView === "import_data_saving" && importConfig &&
|
|
578
576
|
<ImportSaveInProgress importConfig={importConfig}
|
|
579
577
|
collection={values}
|
|
580
|
-
onImportSuccess={(importedCollection) => {
|
|
581
|
-
handleClose(importedCollection);
|
|
578
|
+
onImportSuccess={async (importedCollection) => {
|
|
582
579
|
snackbarController.open({
|
|
583
580
|
type: "info",
|
|
584
581
|
message: "Data imported successfully"
|
|
585
582
|
});
|
|
583
|
+
await saveCollection(values);
|
|
584
|
+
handleClose(importedCollection);
|
|
586
585
|
}}
|
|
587
586
|
/>}
|
|
588
587
|
|
|
@@ -649,7 +648,6 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
649
648
|
<Button variant={"text"}
|
|
650
649
|
type="button"
|
|
651
650
|
onClick={() => {
|
|
652
|
-
saveCollection(values);
|
|
653
651
|
setCurrentView("import_data_mapping");
|
|
654
652
|
}}>
|
|
655
653
|
<ArrowBackIcon/>
|
|
@@ -718,7 +716,6 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
718
716
|
color="primary"
|
|
719
717
|
type="submit"
|
|
720
718
|
loading={isSubmitting}
|
|
721
|
-
// disabled={isSubmitting || !dirty}
|
|
722
719
|
>
|
|
723
720
|
Update collection
|
|
724
721
|
</LoadingButton>}
|
|
@@ -99,7 +99,7 @@ function collectionToCode(collection: EntityCollection): object {
|
|
|
99
99
|
customId: collection.customId,
|
|
100
100
|
initialFilter: collection.initialFilter,
|
|
101
101
|
initialSort: collection.initialSort,
|
|
102
|
-
properties: Object.entries(collection.properties)
|
|
102
|
+
properties: Object.entries(collection.properties ?? {})
|
|
103
103
|
.map(([key, value]) => ({
|
|
104
104
|
[key]: propertyCleanup(value)
|
|
105
105
|
}))
|
|
@@ -53,7 +53,7 @@ export function SubcollectionsEditTab({
|
|
|
53
53
|
|
|
54
54
|
const [currentDialog, setCurrentDialog] = React.useState<{
|
|
55
55
|
isNewCollection: boolean,
|
|
56
|
-
|
|
56
|
+
editedCollectionId?: string,
|
|
57
57
|
}>();
|
|
58
58
|
|
|
59
59
|
const {
|
|
@@ -78,52 +78,53 @@ export function SubcollectionsEditTab({
|
|
|
78
78
|
Subcollections of {values.name}
|
|
79
79
|
</Typography>
|
|
80
80
|
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
81
|
+
<Paper className={"flex flex-col gap-4 p-2 w-full"}>
|
|
82
|
+
{subcollections && subcollections.length > 0 && <Table>
|
|
83
|
+
<TableBody>
|
|
84
|
+
{subcollections.map((subcollection) => (
|
|
85
|
+
<TableRow key={subcollection.path}
|
|
86
|
+
onClick={() => setCurrentDialog({
|
|
87
|
+
isNewCollection: false,
|
|
88
|
+
editedCollectionId: subcollection.id
|
|
89
|
+
})}>
|
|
90
|
+
<TableCell
|
|
91
|
+
align="left">
|
|
92
|
+
<Typography variant={"subtitle2"} className={"flex-grow"}>
|
|
93
|
+
{subcollection.name}
|
|
94
|
+
</Typography>
|
|
95
|
+
</TableCell>
|
|
96
|
+
<TableCell
|
|
97
|
+
align="right">
|
|
98
|
+
<Tooltip title={"Remove"}>
|
|
99
|
+
<IconButton size="small"
|
|
100
|
+
onClick={(e) => {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
setSubcollectionToDelete(subcollection.id);
|
|
104
|
+
}}
|
|
105
|
+
color="inherit">
|
|
106
|
+
<DeleteIcon size={"small"}/>
|
|
107
|
+
</IconButton>
|
|
108
|
+
</Tooltip>
|
|
109
|
+
</TableCell>
|
|
110
|
+
</TableRow>
|
|
111
|
+
))}
|
|
112
|
+
</TableBody>
|
|
113
|
+
</Table>}
|
|
114
|
+
|
|
115
|
+
<Button
|
|
116
|
+
onClick={() => {
|
|
117
|
+
setCurrentDialog({
|
|
118
|
+
isNewCollection: true
|
|
119
|
+
});
|
|
120
|
+
}}
|
|
121
|
+
variant={"text"}
|
|
122
|
+
startIcon={<AddIcon/>}>
|
|
123
|
+
Add subcollection
|
|
124
|
+
</Button>
|
|
125
|
+
|
|
126
|
+
</Paper>
|
|
116
127
|
|
|
117
|
-
<Button
|
|
118
|
-
onClick={() => {
|
|
119
|
-
setCurrentDialog({
|
|
120
|
-
isNewCollection: true
|
|
121
|
-
});
|
|
122
|
-
}}
|
|
123
|
-
variant={"outlined"}
|
|
124
|
-
startIcon={<AddIcon/>}>
|
|
125
|
-
Add subcollection
|
|
126
|
-
</Button>
|
|
127
128
|
</div>
|
|
128
129
|
|
|
129
130
|
<div className={"flex-grow flex flex-col gap-4 items-start"}>
|
|
@@ -131,7 +132,19 @@ export function SubcollectionsEditTab({
|
|
|
131
132
|
Custom views
|
|
132
133
|
</Typography>
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
|
|
136
|
+
{totalEntityViews === 0 &&
|
|
137
|
+
<Alert action={<Button variant="text"
|
|
138
|
+
size={"small"}
|
|
139
|
+
href={"https://firecms.co/docs/customization_quickstart"}
|
|
140
|
+
component={"a"}
|
|
141
|
+
rel="noopener noreferrer"
|
|
142
|
+
target="_blank">More info</Button>}>
|
|
143
|
+
Define your own custom views by uploading it with the CLI.
|
|
144
|
+
</Alert>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
{<>
|
|
135
148
|
<Paper className={"flex flex-col gap-4 p-2 w-full"}>
|
|
136
149
|
<Table>
|
|
137
150
|
<TableBody>
|
|
@@ -175,29 +188,19 @@ export function SubcollectionsEditTab({
|
|
|
175
188
|
))}
|
|
176
189
|
</TableBody>
|
|
177
190
|
</Table>
|
|
191
|
+
|
|
192
|
+
<Button
|
|
193
|
+
onClick={() => {
|
|
194
|
+
setAddEntityViewDialogOpen(true);
|
|
195
|
+
}}
|
|
196
|
+
variant={"text"}
|
|
197
|
+
startIcon={<AddIcon/>}>
|
|
198
|
+
Add custom entity view
|
|
199
|
+
</Button>
|
|
178
200
|
</Paper>
|
|
179
201
|
|
|
180
202
|
</>}
|
|
181
203
|
|
|
182
|
-
{totalEntityViews === 0 &&
|
|
183
|
-
<Alert action={<Button variant="text"
|
|
184
|
-
size={"small"}
|
|
185
|
-
href={"https://firecms.co/docs/customization_quickstart"}
|
|
186
|
-
component={"a"}
|
|
187
|
-
rel="noopener noreferrer"
|
|
188
|
-
target="_blank">More info</Button>}>
|
|
189
|
-
Define your own custom views by uploading it with the CLI.
|
|
190
|
-
</Alert>
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
<Button
|
|
194
|
-
onClick={() => {
|
|
195
|
-
setAddEntityViewDialogOpen(true);
|
|
196
|
-
}}
|
|
197
|
-
variant={"outlined"}
|
|
198
|
-
startIcon={<AddIcon/>}>
|
|
199
|
-
Add custom entity view
|
|
200
|
-
</Button>
|
|
201
204
|
|
|
202
205
|
</div>
|
|
203
206
|
|
|
@@ -140,7 +140,7 @@ export function CollectionEditorImportMapping({
|
|
|
140
140
|
|
|
141
141
|
return (
|
|
142
142
|
|
|
143
|
-
<div className={"overflow-auto my-auto
|
|
143
|
+
<div className={"overflow-auto my-auto"}>
|
|
144
144
|
<Container maxWidth={"6xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
145
145
|
|
|
146
146
|
<Typography variant="h6" className={"mt-4"}>Data property mapping</Typography>
|
|
@@ -23,19 +23,22 @@ export function BlockPropertyField({ disabled, getData, allowDataInference, prop
|
|
|
23
23
|
const [selectedPropertyKey, setSelectedPropertyKey] = useState<string | undefined>();
|
|
24
24
|
const [selectedPropertyNamespace, setSelectedPropertyNamespace] = useState<string | undefined>();
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const onPropertyChanged = ({
|
|
27
|
+
id,
|
|
28
|
+
property
|
|
29
|
+
}: { id?: string, property: Property }) => {
|
|
30
30
|
if (!id)
|
|
31
31
|
throw Error();
|
|
32
|
+
|
|
32
33
|
setFieldValue("oneOf.properties", {
|
|
33
34
|
...(values.oneOf?.properties ?? {}),
|
|
34
35
|
[id]: property
|
|
35
36
|
}, false);
|
|
36
|
-
|
|
37
|
+
const currentPropertiesOrder = values.oneOf?.propertiesOrder ?? Object.keys(values.oneOf?.properties ?? {});
|
|
38
|
+
const newPropertiesOrder = currentPropertiesOrder.includes(id) ? currentPropertiesOrder : [...currentPropertiesOrder, id];
|
|
39
|
+
setFieldValue("oneOf.propertiesOrder", newPropertiesOrder, false);
|
|
37
40
|
setPropertyDialogOpen(false);
|
|
38
|
-
}
|
|
41
|
+
};
|
|
39
42
|
|
|
40
43
|
const selectedPropertyFullId = selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined;
|
|
41
44
|
const selectedProperty = selectedPropertyFullId ? getIn(values.oneOf?.properties, selectedPropertyFullId.replaceAll(".", ".properties.")) : undefined;
|
|
@@ -127,7 +130,7 @@ export function BlockPropertyField({ disabled, getData, allowDataInference, prop
|
|
|
127
130
|
existingProperty={Boolean(selectedPropertyKey)}
|
|
128
131
|
autoUpdateId={!selectedPropertyKey}
|
|
129
132
|
autoOpenTypeSelect={!selectedPropertyKey}
|
|
130
|
-
onPropertyChanged={
|
|
133
|
+
onPropertyChanged={onPropertyChanged}
|
|
131
134
|
existingPropertyKeys={selectedPropertyKey ? undefined : values.oneOf?.propertiesOrder}
|
|
132
135
|
propertyConfigs={propertyConfigs}/>}
|
|
133
136
|
|
|
@@ -25,19 +25,23 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
25
25
|
const [selectedPropertyNamespace, setSelectedPropertyNamespace] = useState<string | undefined>();
|
|
26
26
|
|
|
27
27
|
const propertiesOrder = values.propertiesOrder ?? Object.keys(values.properties ?? {});
|
|
28
|
-
const onPropertyCreated =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const onPropertyCreated = ({
|
|
29
|
+
id,
|
|
30
|
+
property
|
|
31
|
+
}: { id?: string, property: Property }) => {
|
|
32
32
|
if (!id)
|
|
33
33
|
throw Error();
|
|
34
34
|
setFieldValue("properties", {
|
|
35
35
|
...(values.properties ?? {}),
|
|
36
36
|
[id]: property
|
|
37
37
|
}, false);
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
const currentPropertiesOrder = values.propertiesOrder ?? Object.keys(values.properties ?? {});
|
|
40
|
+
const newPropertiesOrder = currentPropertiesOrder.includes(id) ? currentPropertiesOrder : [...currentPropertiesOrder, id];
|
|
41
|
+
setFieldValue("propertiesOrder", newPropertiesOrder, false);
|
|
42
|
+
|
|
39
43
|
setPropertyDialogOpen(false);
|
|
40
|
-
}
|
|
44
|
+
};
|
|
41
45
|
|
|
42
46
|
const deleteProperty = useCallback((propertyKey?: string, namespace?: string) => {
|
|
43
47
|
const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
|
|
@@ -60,15 +64,6 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
60
64
|
const selectedPropertyFullId = selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined;
|
|
61
65
|
const selectedProperty = selectedPropertyFullId ? getIn(values.properties, selectedPropertyFullId.replaceAll(".", ".properties.")) : undefined;
|
|
62
66
|
|
|
63
|
-
const addChildButton = <Button
|
|
64
|
-
color="primary"
|
|
65
|
-
variant={"outlined"}
|
|
66
|
-
onClick={() => setPropertyDialogOpen(true)}
|
|
67
|
-
startIcon={<AddIcon/>}
|
|
68
|
-
>
|
|
69
|
-
Add property to {values.name ?? "this group"}
|
|
70
|
-
</Button>;
|
|
71
|
-
|
|
72
67
|
const empty = !propertiesOrder || propertiesOrder.length < 1;
|
|
73
68
|
|
|
74
69
|
const onPropertyMove = useCallback((propertiesOrder: string[], namespace?: string) => {
|
|
@@ -80,7 +75,14 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
80
75
|
<div className={"col-span-12"}>
|
|
81
76
|
<div className="flex justify-between items-end my-4">
|
|
82
77
|
<Typography variant={"subtitle2"}>Properties in this group</Typography>
|
|
83
|
-
|
|
78
|
+
<Button
|
|
79
|
+
color="primary"
|
|
80
|
+
variant={"outlined"}
|
|
81
|
+
onClick={() => setPropertyDialogOpen(true)}
|
|
82
|
+
startIcon={<AddIcon/>}
|
|
83
|
+
>
|
|
84
|
+
Add property to {values.name ?? "this group"}
|
|
85
|
+
</Button>
|
|
84
86
|
</div>
|
|
85
87
|
<Paper className="p-2 pl-8">
|
|
86
88
|
<PropertyTree
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { ArrayProperty, getFieldConfig, Property, PropertyConfig } from "@firecms/core";
|
|
3
3
|
import { Button, Paper, Typography } from "@firecms/ui";
|
|
4
4
|
import { Field, getIn, useFormex } from "@firecms/formex";
|