@firecms/collection_editor 3.0.0-alpha.46 → 3.0.0-alpha.47
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 +1301 -1277
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +3 -3
- package/dist/types/collection_inference.d.ts +1 -1
- package/dist/types/config_controller.d.ts +5 -5
- package/dist/ui/CollectionViewHeaderAction.d.ts +2 -2
- package/dist/ui/EditorCollectionAction.d.ts +1 -1
- package/dist/ui/PropertyAddColumnComponent.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +3 -0
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -2
- package/dist/useCollectionEditorPlugin.d.ts +3 -2
- package/package.json +4 -4
- package/src/ConfigControllerProvider.tsx +16 -23
- package/src/types/collection_editor_controller.tsx +4 -4
- package/src/types/collection_inference.ts +1 -1
- package/src/types/config_controller.tsx +5 -5
- package/src/types/persisted_collection.ts +1 -1
- package/src/ui/CollectionViewHeaderAction.tsx +4 -4
- package/src/ui/EditorCollectionAction.tsx +5 -5
- package/src/ui/HomePageEditorCollectionAction.tsx +10 -3
- package/src/ui/MissingReferenceWidget.tsx +4 -3
- package/src/ui/NewCollectionCard.tsx +1 -1
- package/src/ui/PropertyAddColumnComponent.tsx +4 -4
- package/src/ui/RootCollectionSuggestions.tsx +10 -2
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +27 -38
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +82 -34
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +3 -3
- package/src/ui/collection_editor/CollectionYupValidation.tsx +1 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +4 -4
- package/src/ui/collection_editor/PropertySelectItem.tsx +1 -1
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +4 -4
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +1 -1
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +5 -6
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +1 -10
- package/src/ui/collection_editor/templates/blog_template.ts +1 -0
- package/src/ui/collection_editor/templates/products_template.ts +1 -0
- package/src/ui/collection_editor/templates/users_template.ts +1 -0
- package/src/useCollectionEditorPlugin.tsx +10 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Autocomplete,
|
|
4
4
|
AutocompleteItem,
|
|
@@ -32,14 +32,14 @@ export function CollectionDetailsForm({
|
|
|
32
32
|
isNewCollection,
|
|
33
33
|
reservedGroups,
|
|
34
34
|
existingPaths,
|
|
35
|
-
|
|
35
|
+
existingIds,
|
|
36
36
|
groups,
|
|
37
37
|
parentCollection
|
|
38
38
|
}: {
|
|
39
39
|
isNewCollection: boolean,
|
|
40
40
|
reservedGroups?: string[];
|
|
41
41
|
existingPaths?: string[];
|
|
42
|
-
|
|
42
|
+
existingIds?: string[];
|
|
43
43
|
groups: string[] | null;
|
|
44
44
|
parentCollection?: EntityCollection;
|
|
45
45
|
}) {
|
|
@@ -57,6 +57,7 @@ export function CollectionDetailsForm({
|
|
|
57
57
|
} = useFormikContext<EntityCollection>();
|
|
58
58
|
|
|
59
59
|
const [iconDialogOpen, setIconDialogOpen] = useState(false);
|
|
60
|
+
const [advancedPanelExpanded, setAdvancedPanelExpanded] = useState(false);
|
|
60
61
|
|
|
61
62
|
const updateName = (name: string) => {
|
|
62
63
|
setFieldValue("name", name);
|
|
@@ -66,6 +67,11 @@ export function CollectionDetailsForm({
|
|
|
66
67
|
setFieldValue("path", toSnakeCase(name));
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
const idTouched = getIn(touched, "id");
|
|
71
|
+
if (!idTouched && isNewCollection && name) {
|
|
72
|
+
setFieldValue("id", toSnakeCase(name));
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
const singularNameTouched = getIn(touched, "singularName");
|
|
70
76
|
if (!singularNameTouched && isNewCollection && name) {
|
|
71
77
|
setFieldValue("singularName", singular(name))
|
|
@@ -73,29 +79,13 @@ export function CollectionDetailsForm({
|
|
|
73
79
|
|
|
74
80
|
};
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let error;
|
|
80
|
-
if (!value) {
|
|
81
|
-
error = "You must specify a path in the database for this collection";
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (errors.id) {
|
|
84
|
+
setAdvancedPanelExpanded(true);
|
|
82
85
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
error = "There is already a collection with the specified path. If you want to have multiple collections referring to the same database path, you need to define an alias in at least one of the collections";
|
|
87
|
-
return error;
|
|
88
|
-
}, [isNewCollection, existingAliases, existingPaths, values.alias]);
|
|
89
|
-
|
|
90
|
-
const validateAlias = useCallback((value: string) => {
|
|
91
|
-
if (!value) return undefined;
|
|
92
|
-
let error;
|
|
93
|
-
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()))
|
|
94
|
-
error = "There is already a collection that uses this value as a path";
|
|
95
|
-
if (isNewCollection && existingAliases?.includes(value.trim().toLowerCase()))
|
|
96
|
-
error = "There is already a collection which uses this alias";
|
|
97
|
-
return error;
|
|
98
|
-
}, [isNewCollection, existingPaths, existingAliases]);
|
|
86
|
+
}, [errors.id]);
|
|
87
|
+
|
|
88
|
+
const collectionIcon = getIconForView(values);
|
|
99
89
|
|
|
100
90
|
const groupOptions = groups?.filter((group) => !reservedGroups?.includes(group));
|
|
101
91
|
|
|
@@ -165,13 +155,12 @@ export function CollectionDetailsForm({
|
|
|
165
155
|
<Field name={"path"}
|
|
166
156
|
as={DebouncedTextField}
|
|
167
157
|
label={"Path"}
|
|
168
|
-
validate={validatePath}
|
|
169
158
|
disabled={!isNewCollection}
|
|
170
159
|
required
|
|
171
160
|
error={touched.path && Boolean(errors.path)}/>
|
|
172
161
|
|
|
173
162
|
<FieldHelperView error={touched.path && Boolean(errors.path)}>
|
|
174
|
-
{touched.path && Boolean(errors.path) ? errors.path : "Path that this collection is stored in"}
|
|
163
|
+
{touched.path && Boolean(errors.path) ? errors.path : "Path that this collection is stored in, in the database"}
|
|
175
164
|
</FieldHelperView>
|
|
176
165
|
|
|
177
166
|
</div>
|
|
@@ -210,6 +199,8 @@ export function CollectionDetailsForm({
|
|
|
210
199
|
|
|
211
200
|
<div className={"col-span-12"}>
|
|
212
201
|
<ExpandablePanel
|
|
202
|
+
expanded={advancedPanelExpanded}
|
|
203
|
+
onExpandedChange={setAdvancedPanelExpanded}
|
|
213
204
|
title={
|
|
214
205
|
<div className="flex flex-row text-gray-500">
|
|
215
206
|
<SettingsIcon/>
|
|
@@ -252,14 +243,13 @@ export function CollectionDetailsForm({
|
|
|
252
243
|
</div>
|
|
253
244
|
|
|
254
245
|
<div className={"col-span-12"}>
|
|
255
|
-
<Field name={"
|
|
246
|
+
<Field name={"id"}
|
|
256
247
|
as={DebouncedTextField}
|
|
257
248
|
disabled={!isNewCollection}
|
|
258
|
-
label={"
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
{touched.alias && Boolean(errors.alias) ? errors.alias : "Use an alias as an ID when you have multiple collections located in the same path"}
|
|
249
|
+
label={"Collection id"}
|
|
250
|
+
error={touched.id && Boolean(errors.id)}/>
|
|
251
|
+
<FieldHelperView error={touched.id && Boolean(errors.id)}>
|
|
252
|
+
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
|
|
263
253
|
</FieldHelperView>
|
|
264
254
|
</div>
|
|
265
255
|
|
|
@@ -284,7 +274,7 @@ export function CollectionDetailsForm({
|
|
|
284
274
|
<div className={"col-span-12"}>
|
|
285
275
|
<Select
|
|
286
276
|
name="customId"
|
|
287
|
-
label="
|
|
277
|
+
label="Data IDs generation"
|
|
288
278
|
position={"item-aligned"}
|
|
289
279
|
disabled={customIdValue === "code_defined"}
|
|
290
280
|
onValueChange={(v) => {
|
|
@@ -306,11 +296,11 @@ export function CollectionDetailsForm({
|
|
|
306
296
|
else if (value === "optional")
|
|
307
297
|
return "Users can define an ID, but it is not required";
|
|
308
298
|
else
|
|
309
|
-
return "ID is generated automatically";
|
|
299
|
+
return "Document ID is generated automatically";
|
|
310
300
|
}}
|
|
311
301
|
>
|
|
312
302
|
<SelectItem value={"false"}>
|
|
313
|
-
ID is generated automatically
|
|
303
|
+
Document ID is generated automatically
|
|
314
304
|
</SelectItem>
|
|
315
305
|
<SelectItem value={"true"}>
|
|
316
306
|
Users must define an ID
|
|
@@ -359,6 +349,5 @@ export function CollectionDetailsForm({
|
|
|
359
349
|
|
|
360
350
|
</Container>
|
|
361
351
|
</div>
|
|
362
|
-
)
|
|
363
|
-
;
|
|
352
|
+
);
|
|
364
353
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
Property,
|
|
25
25
|
PropertyConfig,
|
|
26
26
|
PropertyOrBuilder,
|
|
27
|
+
randomString,
|
|
27
28
|
removeUndefined,
|
|
28
29
|
Tab,
|
|
29
30
|
Tabs,
|
|
@@ -60,7 +61,7 @@ export interface CollectionEditorDialogProps {
|
|
|
60
61
|
}
|
|
61
62
|
editedCollectionPath?: string; // last segment of the path, like `locales`
|
|
62
63
|
fullPath?: string; // full path of this particular collection, like `products/123/locales`
|
|
63
|
-
|
|
64
|
+
parentCollectionIds?: string[]; // path segments of the parent collection, like [`products`]
|
|
64
65
|
handleClose: (collection?: EntityCollection) => void;
|
|
65
66
|
configController: CollectionsConfigController;
|
|
66
67
|
reservedGroups?: string[];
|
|
@@ -139,7 +140,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
139
140
|
initialValues: initialValuesProp,
|
|
140
141
|
configController,
|
|
141
142
|
editedCollectionPath,
|
|
142
|
-
|
|
143
|
+
parentCollectionIds,
|
|
143
144
|
fullPath,
|
|
144
145
|
collectionInference,
|
|
145
146
|
handleClose,
|
|
@@ -164,10 +165,10 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
164
165
|
collections
|
|
165
166
|
} = navigation;
|
|
166
167
|
|
|
167
|
-
const includeTemplates = !initialValuesProp?.path && (
|
|
168
|
+
const includeTemplates = !initialValuesProp?.path && (parentCollectionIds ?? []).length === 0;
|
|
168
169
|
const collectionsInThisLevel = (parentCollection ? parentCollection.subcollections : collections) ?? [];
|
|
169
170
|
const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
|
|
170
|
-
const
|
|
171
|
+
const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
|
|
171
172
|
|
|
172
173
|
const importConfig = useImportConfig();
|
|
173
174
|
|
|
@@ -198,7 +199,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
198
199
|
try {
|
|
199
200
|
if (navigation.initialised) {
|
|
200
201
|
if (editedCollectionPath) {
|
|
201
|
-
setCollection(navigation.getCollectionFromPaths<PersistedCollection<M>>([...(
|
|
202
|
+
setCollection(navigation.getCollectionFromPaths<PersistedCollection<M>>([...(parentCollectionIds ?? []), editedCollectionPath]));
|
|
202
203
|
} else {
|
|
203
204
|
setCollection(undefined);
|
|
204
205
|
}
|
|
@@ -211,12 +212,12 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
211
212
|
}, [navigation.getCollectionFromPaths, editedCollectionPath, initialError, navigation.initialised]);
|
|
212
213
|
|
|
213
214
|
const saveCollection = (updatedCollection: PersistedCollection<M>): Promise<boolean> => {
|
|
214
|
-
const fullPath = updatedCollection.
|
|
215
|
+
const fullPath = updatedCollection.id || updatedCollection.path;
|
|
215
216
|
return configController.saveCollection({
|
|
216
|
-
|
|
217
|
+
id: fullPath,
|
|
217
218
|
collectionData: updatedCollection,
|
|
218
219
|
previousPath: editedCollectionPath,
|
|
219
|
-
|
|
220
|
+
parentCollectionIds
|
|
220
221
|
})
|
|
221
222
|
.then(() => {
|
|
222
223
|
setError(undefined);
|
|
@@ -233,15 +234,25 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
233
234
|
});
|
|
234
235
|
};
|
|
235
236
|
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
237
|
+
const initialCollection = collection
|
|
238
|
+
? {
|
|
239
|
+
...collection,
|
|
240
|
+
id: collection.id ?? collection.path ?? randomString(16)
|
|
241
|
+
}
|
|
242
|
+
: undefined;
|
|
243
|
+
|
|
244
|
+
const initialValues: PersistedCollection<M> = initialCollection
|
|
245
|
+
? applyPropertyConfigs(initialCollection, propertyConfigs)
|
|
246
|
+
: {
|
|
247
|
+
id: initialValuesProp?.path ?? randomString(16),
|
|
248
|
+
path: initialValuesProp?.path ?? "",
|
|
249
|
+
name: initialValuesProp?.name ?? "",
|
|
250
|
+
group: initialValuesProp?.group ?? "",
|
|
251
|
+
properties: {} as PropertiesOrBuilders<M>,
|
|
252
|
+
propertiesOrder: [],
|
|
253
|
+
icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
|
|
254
|
+
ownerId: authController.user?.uid ?? ""
|
|
255
|
+
};
|
|
245
256
|
|
|
246
257
|
const setNextMode = useCallback(() => {
|
|
247
258
|
if (currentView === "details") {
|
|
@@ -268,8 +279,8 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
268
279
|
|
|
269
280
|
const doCollectionInference = useCallback((collection: PersistedCollection<any>) => {
|
|
270
281
|
if (!collectionInference) return undefined;
|
|
271
|
-
return collectionInference?.(collection.path, collection.collectionGroup ?? false,
|
|
272
|
-
}, [collectionInference,
|
|
282
|
+
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentCollectionIds ?? []);
|
|
283
|
+
}, [collectionInference, parentCollectionIds]);
|
|
273
284
|
|
|
274
285
|
const inferCollectionFromData = useCallback(async (newCollection: PersistedCollection<M>) => {
|
|
275
286
|
|
|
@@ -288,7 +299,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
288
299
|
return Promise.resolve(newCollection);
|
|
289
300
|
}
|
|
290
301
|
const values = {
|
|
291
|
-
...(newCollection ?? {})
|
|
302
|
+
...(newCollection ?? {})
|
|
292
303
|
};
|
|
293
304
|
|
|
294
305
|
if (Object.keys(inferredCollection.properties ?? {}).length > 0) {
|
|
@@ -315,7 +326,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
315
326
|
});
|
|
316
327
|
return newCollection;
|
|
317
328
|
}
|
|
318
|
-
}, [
|
|
329
|
+
}, [parentCollectionIds, doCollectionInference]);
|
|
319
330
|
|
|
320
331
|
const onSubmit = (newCollectionState: PersistedCollection<M>, formikHelpers: FormikHelpers<PersistedCollection<M>>) => {
|
|
321
332
|
try {
|
|
@@ -389,22 +400,34 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
389
400
|
<Formik
|
|
390
401
|
initialValues={initialValues}
|
|
391
402
|
validationSchema={(currentView === "properties" || currentView === "subcollections" || currentView === "details") && YupSchema}
|
|
392
|
-
validate={() => {
|
|
403
|
+
validate={(v) => {
|
|
393
404
|
if (currentView === "properties") {
|
|
394
405
|
// return the errors for the properties form
|
|
395
406
|
return propertyErrorsRef.current;
|
|
396
407
|
}
|
|
397
|
-
|
|
408
|
+
const errors: Record<string, any> = {};
|
|
409
|
+
if (currentView === "details") {
|
|
410
|
+
const pathError = validatePath(v.path, isNewCollection, existingPaths, v.id);
|
|
411
|
+
if (pathError) {
|
|
412
|
+
errors.path = pathError;
|
|
413
|
+
}
|
|
414
|
+
const idError = validateId(v.id, isNewCollection, existingPaths, existingIds);
|
|
415
|
+
if (idError) {
|
|
416
|
+
errors.id = idError;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return errors;
|
|
398
420
|
}}
|
|
399
421
|
onSubmit={onSubmit}
|
|
400
422
|
>
|
|
401
|
-
{({
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
423
|
+
{(formikHelpers) => {
|
|
424
|
+
const {
|
|
425
|
+
values,
|
|
426
|
+
setFieldValue,
|
|
427
|
+
isSubmitting,
|
|
428
|
+
dirty,
|
|
429
|
+
submitCount
|
|
430
|
+
} = formikHelpers;
|
|
408
431
|
|
|
409
432
|
const path = values.path ?? editedCollectionPath;
|
|
410
433
|
const updatedFullPath = fullPath?.includes("/") ? fullPath?.split("/").slice(0, -1).join("/") + "/" + path : path; // TODO: this path is wrong
|
|
@@ -432,7 +455,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
432
455
|
});
|
|
433
456
|
}
|
|
434
457
|
|
|
435
|
-
const validValues = Boolean(values.name) && Boolean(values.
|
|
458
|
+
const validValues = Boolean(values.name) && Boolean(values.id);
|
|
436
459
|
|
|
437
460
|
const onImportMappingComplete = () => {
|
|
438
461
|
const updatedProperties = { ...values.properties };
|
|
@@ -443,7 +466,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
443
466
|
setNextMode();
|
|
444
467
|
console.log("onImportMappingComplete", {
|
|
445
468
|
importConfig,
|
|
446
|
-
properties: updatedProperties
|
|
469
|
+
properties: updatedProperties
|
|
447
470
|
})
|
|
448
471
|
};
|
|
449
472
|
|
|
@@ -517,7 +540,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
517
540
|
{currentView === "details" &&
|
|
518
541
|
<CollectionDetailsForm
|
|
519
542
|
existingPaths={existingPaths}
|
|
520
|
-
|
|
543
|
+
existingIds={existingIds}
|
|
521
544
|
groups={groups}
|
|
522
545
|
parentCollection={parentCollection}
|
|
523
546
|
isNewCollection={isNewCollection}/>}
|
|
@@ -528,7 +551,7 @@ export function CollectionEditorDialogInternal<M extends {
|
|
|
528
551
|
configController={configController}
|
|
529
552
|
getUser={getUser}
|
|
530
553
|
collectionInference={collectionInference}
|
|
531
|
-
|
|
554
|
+
parentCollectionIds={parentCollectionIds}
|
|
532
555
|
collection={collection}/>}
|
|
533
556
|
|
|
534
557
|
{currentView === "properties" &&
|
|
@@ -694,3 +717,28 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
694
717
|
return internalProperty;
|
|
695
718
|
|
|
696
719
|
}
|
|
720
|
+
|
|
721
|
+
const validatePath = (value: string, isNewCollection: boolean, existingPaths: string[], idValue?: string) => {
|
|
722
|
+
let error;
|
|
723
|
+
if (!value) {
|
|
724
|
+
error = "You must specify a path in the database for this collection";
|
|
725
|
+
}
|
|
726
|
+
// if (isNewCollection && existingIds?.includes(value.trim().toLowerCase()))
|
|
727
|
+
// error = "There is already a collection which uses this path as an id";
|
|
728
|
+
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()) && !idValue)
|
|
729
|
+
error = "There is already a collection with the specified path. If you want to have multiple collections referring to the same database path, make sure the have different ids";
|
|
730
|
+
return error;
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
const validateId = (value: string, isNewCollection: boolean, existingPaths: string[], existingIds: string[]) => {
|
|
734
|
+
if (!value) return undefined;
|
|
735
|
+
let error;
|
|
736
|
+
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()))
|
|
737
|
+
error = "There is already a collection that uses this value as a path";
|
|
738
|
+
if (isNewCollection && existingIds?.includes(value.trim().toLowerCase()))
|
|
739
|
+
error = "There is already a collection which uses this id";
|
|
740
|
+
// if (error) {
|
|
741
|
+
// setAdvancedPanelExpanded(true);
|
|
742
|
+
// }
|
|
743
|
+
return error;
|
|
744
|
+
};
|
|
@@ -43,7 +43,7 @@ type CollectionEditorFormProps = {
|
|
|
43
43
|
extraIcon: React.ReactNode;
|
|
44
44
|
getUser: (uid: string) => User | null;
|
|
45
45
|
getData?: () => Promise<object[]>;
|
|
46
|
-
doCollectionInference: (collection: PersistedCollection) => Promise<EntityCollection | null> | undefined;
|
|
46
|
+
doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
|
|
47
47
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
48
48
|
collectionEditable: boolean;
|
|
49
49
|
};
|
|
@@ -121,7 +121,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
// find properties in the new collection, not present in the current one
|
|
124
|
-
const newPropertyKeys = Object.keys(newCollection.properties)
|
|
124
|
+
const newPropertyKeys = (newCollection.properties ? Object.keys(newCollection.properties) : [])
|
|
125
125
|
.filter((propertyKey) => !values.properties[propertyKey]);
|
|
126
126
|
if (newPropertyKeys.length === 0) {
|
|
127
127
|
snackbarController.open({
|
|
@@ -133,7 +133,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
133
133
|
// add them to the current collection
|
|
134
134
|
const updatedProperties = {
|
|
135
135
|
...newPropertyKeys.reduce((acc, propertyKey) => {
|
|
136
|
-
acc[propertyKey] = newCollection.properties[propertyKey];
|
|
136
|
+
acc[propertyKey] = (newCollection.properties ?? {})[propertyKey];
|
|
137
137
|
return acc;
|
|
138
138
|
}, {} as { [key: string]: PropertyOrBuilder }),
|
|
139
139
|
...values.properties
|
|
@@ -17,7 +17,7 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
|
|
|
17
17
|
|
|
18
18
|
const snackbarController = useSnackbarController();
|
|
19
19
|
|
|
20
|
-
const code = "
|
|
20
|
+
const code = "import { EntityCollection } from \"firecms\";\n\nconst " + camelCase(collection.name) + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
|
|
21
21
|
return <Dialog open={open}
|
|
22
22
|
onOpenChange={onOpenChange}
|
|
23
23
|
maxWidth={"4xl"}>
|
|
@@ -60,7 +60,7 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
|
|
|
60
60
|
e.preventDefault();
|
|
61
61
|
snackbarController.open({
|
|
62
62
|
type: "success",
|
|
63
|
-
message: `Copied
|
|
63
|
+
message: `Copied`
|
|
64
64
|
})
|
|
65
65
|
return navigator.clipboard.writeText(code);
|
|
66
66
|
}}>
|
|
@@ -95,13 +95,13 @@ function collectionToCode(collection: EntityCollection): object {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
return {
|
|
98
|
+
id: collection.id,
|
|
98
99
|
name: collection.name,
|
|
99
100
|
singularName: collection.singularName,
|
|
100
|
-
description: collection.description,
|
|
101
101
|
path: collection.path,
|
|
102
|
+
description: collection.description,
|
|
102
103
|
editable: true,
|
|
103
104
|
collectionGroup: collection.collectionGroup,
|
|
104
|
-
alias: collection.alias,
|
|
105
105
|
icon: collection.icon,
|
|
106
106
|
group: collection.group,
|
|
107
107
|
customId: collection.customId,
|
|
@@ -33,14 +33,14 @@ export function SubcollectionsEditTab({
|
|
|
33
33
|
configController,
|
|
34
34
|
collectionInference,
|
|
35
35
|
getUser,
|
|
36
|
-
|
|
36
|
+
parentCollectionIds
|
|
37
37
|
}: {
|
|
38
38
|
collection: PersistedCollection,
|
|
39
39
|
parentCollection?: EntityCollection,
|
|
40
40
|
configController: CollectionsConfigController;
|
|
41
41
|
collectionInference?: CollectionInference;
|
|
42
42
|
getUser: (uid: string) => User | null;
|
|
43
|
-
|
|
43
|
+
parentCollectionIds?: string[];
|
|
44
44
|
}) {
|
|
45
45
|
|
|
46
46
|
const { entityViews: contextEntityViews } = useFireCMSContext();
|
|
@@ -202,7 +202,7 @@ export function SubcollectionsEditTab({
|
|
|
202
202
|
onAccept={() => {
|
|
203
203
|
configController.deleteCollection({
|
|
204
204
|
path: subcollectionToDelete,
|
|
205
|
-
|
|
205
|
+
parentCollectionIds: [...(parentCollectionIds ?? []), collection.path]
|
|
206
206
|
});
|
|
207
207
|
setSubcollectionToDelete(undefined);
|
|
208
208
|
}}
|
|
@@ -228,7 +228,7 @@ export function SubcollectionsEditTab({
|
|
|
228
228
|
configController={configController}
|
|
229
229
|
parentCollection={collection}
|
|
230
230
|
collectionInference={collectionInference}
|
|
231
|
-
|
|
231
|
+
parentCollectionIds={[...parentCollectionIds ?? [], values.id]}
|
|
232
232
|
isNewCollection={false}
|
|
233
233
|
{...currentDialog}
|
|
234
234
|
getUser={getUser}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
|
-
import { AddIcon, ArrayProperty, Button,
|
|
2
|
+
import { AddIcon, ArrayProperty, Button, Paper, Property, PropertyConfig, Typography } from "@firecms/core";
|
|
3
3
|
import { getIn, useFormikContext } from "formik";
|
|
4
4
|
import { PropertyFormDialog } from "../PropertyEditView";
|
|
5
5
|
import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath } from "../util";
|
|
@@ -2,7 +2,6 @@ import React from "react";
|
|
|
2
2
|
import { Field, getIn, useFormikContext } from "formik";
|
|
3
3
|
import {
|
|
4
4
|
CircularProgress,
|
|
5
|
-
EntityCollection,
|
|
6
5
|
getIconForView,
|
|
7
6
|
NumberProperty,
|
|
8
7
|
Select,
|
|
@@ -114,7 +113,7 @@ export function CollectionsSelect({
|
|
|
114
113
|
onChange={handleChange}
|
|
115
114
|
label={"Target collection"}
|
|
116
115
|
renderValue={(selected) => {
|
|
117
|
-
const selectedCollection = collections.find(collection => collection.
|
|
116
|
+
const selectedCollection = collections.find(collection => collection.id === selected || collection.path === selected);
|
|
118
117
|
if (!selectedCollection) return null;
|
|
119
118
|
const collectionIcon = getIconForView(selectedCollection);
|
|
120
119
|
return (
|
|
@@ -137,8 +136,8 @@ export function CollectionsSelect({
|
|
|
137
136
|
.map((collection) => {
|
|
138
137
|
const collectionIcon = getIconForView(collection);
|
|
139
138
|
return <SelectItem
|
|
140
|
-
key={`${collection.
|
|
141
|
-
value={collection.
|
|
139
|
+
key={`${collection.id ?? collection.path}-${group}`}
|
|
140
|
+
value={collection.id ?? collection.path}>
|
|
142
141
|
<div className="flex flex-row">
|
|
143
142
|
{collectionIcon}
|
|
144
143
|
<Typography
|
|
@@ -158,8 +157,8 @@ export function CollectionsSelect({
|
|
|
158
157
|
{ungroupedCollections
|
|
159
158
|
.map((collection) => {
|
|
160
159
|
const collectionIcon = getIconForView(collection);
|
|
161
|
-
return <SelectItem key={collection.
|
|
162
|
-
value={collection.
|
|
160
|
+
return <SelectItem key={collection.id ?? collection.path}
|
|
161
|
+
value={collection.id ?? collection.path}>
|
|
163
162
|
<div className="flex flex-row">
|
|
164
163
|
{collectionIcon}
|
|
165
164
|
<Typography
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
ArrayProperty,
|
|
4
|
-
Button,
|
|
5
|
-
getFieldConfig,
|
|
6
|
-
Paper,
|
|
7
|
-
Property,
|
|
8
|
-
PropertyConfig,
|
|
9
|
-
Typography,
|
|
10
|
-
useFireCMSContext
|
|
11
|
-
} from "@firecms/core";
|
|
2
|
+
import { ArrayProperty, Button, getFieldConfig, Paper, Property, PropertyConfig, Typography } from "@firecms/core";
|
|
12
3
|
import { Field, getIn, useFormikContext } from "formik";
|
|
13
4
|
import { PropertyFormDialog } from "../PropertyEditView";
|
|
14
5
|
import { PropertyFieldPreview } from "../PropertyFieldPreview";
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
FireCMSPlugin,
|
|
5
5
|
joinCollectionLists,
|
|
6
6
|
makePropertiesEditable,
|
|
7
|
+
ModifyCollectionProps,
|
|
7
8
|
Properties,
|
|
8
9
|
User
|
|
9
10
|
} from "@firecms/core";
|
|
@@ -26,6 +27,8 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
26
27
|
*/
|
|
27
28
|
collectionConfigController: CollectionsConfigController;
|
|
28
29
|
|
|
30
|
+
modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | undefined;
|
|
31
|
+
|
|
29
32
|
/**
|
|
30
33
|
* Define what actions can be performed on the configuration.
|
|
31
34
|
*/
|
|
@@ -55,6 +58,7 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
55
58
|
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
|
|
58
62
|
/**
|
|
59
63
|
* Use this hook to initialise the Collection Editor plugin.
|
|
60
64
|
* This is likely the only hook you will need to use.
|
|
@@ -69,6 +73,7 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
|
|
|
69
73
|
export function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>
|
|
70
74
|
({
|
|
71
75
|
collectionConfigController,
|
|
76
|
+
modifyCollection,
|
|
72
77
|
configPermissions,
|
|
73
78
|
reservedGroups,
|
|
74
79
|
extraView,
|
|
@@ -84,11 +89,12 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
|
|
|
84
89
|
makePropertiesEditable(c.properties as Properties);
|
|
85
90
|
c.subcollections?.forEach(markAsEditable);
|
|
86
91
|
};
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
const storedCollections = collectionConfigController.collections ?? [];
|
|
93
|
+
storedCollections.forEach(markAsEditable);
|
|
94
|
+
const joinCollectionLists1 = joinCollectionLists(collections, storedCollections, [], modifyCollection);
|
|
95
|
+
return joinCollectionLists1;
|
|
90
96
|
},
|
|
91
|
-
[collectionConfigController.collections]);
|
|
97
|
+
[collectionConfigController.collections, modifyCollection]);
|
|
92
98
|
|
|
93
99
|
return {
|
|
94
100
|
name: "Collection Editor",
|