@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.
Files changed (44) hide show
  1. package/dist/index.es.js +1301 -1277
  2. package/dist/index.es.js.map +1 -1
  3. package/dist/index.umd.js +3 -1
  4. package/dist/index.umd.js.map +1 -1
  5. package/dist/types/collection_editor_controller.d.ts +3 -3
  6. package/dist/types/collection_inference.d.ts +1 -1
  7. package/dist/types/config_controller.d.ts +5 -5
  8. package/dist/ui/CollectionViewHeaderAction.d.ts +2 -2
  9. package/dist/ui/EditorCollectionAction.d.ts +1 -1
  10. package/dist/ui/PropertyAddColumnComponent.d.ts +2 -2
  11. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -2
  12. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +2 -2
  13. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
  14. package/dist/ui/collection_editor/CollectionYupValidation.d.ts +3 -0
  15. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -2
  16. package/dist/useCollectionEditorPlugin.d.ts +3 -2
  17. package/package.json +4 -4
  18. package/src/ConfigControllerProvider.tsx +16 -23
  19. package/src/types/collection_editor_controller.tsx +4 -4
  20. package/src/types/collection_inference.ts +1 -1
  21. package/src/types/config_controller.tsx +5 -5
  22. package/src/types/persisted_collection.ts +1 -1
  23. package/src/ui/CollectionViewHeaderAction.tsx +4 -4
  24. package/src/ui/EditorCollectionAction.tsx +5 -5
  25. package/src/ui/HomePageEditorCollectionAction.tsx +10 -3
  26. package/src/ui/MissingReferenceWidget.tsx +4 -3
  27. package/src/ui/NewCollectionCard.tsx +1 -1
  28. package/src/ui/PropertyAddColumnComponent.tsx +4 -4
  29. package/src/ui/RootCollectionSuggestions.tsx +10 -2
  30. package/src/ui/collection_editor/CollectionDetailsForm.tsx +27 -38
  31. package/src/ui/collection_editor/CollectionEditorDialog.tsx +82 -34
  32. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +3 -3
  33. package/src/ui/collection_editor/CollectionYupValidation.tsx +1 -0
  34. package/src/ui/collection_editor/GetCodeDialog.tsx +4 -4
  35. package/src/ui/collection_editor/PropertySelectItem.tsx +1 -1
  36. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +4 -4
  37. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +1 -1
  38. package/src/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
  39. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +5 -6
  40. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +1 -10
  41. package/src/ui/collection_editor/templates/blog_template.ts +1 -0
  42. package/src/ui/collection_editor/templates/products_template.ts +1 -0
  43. package/src/ui/collection_editor/templates/users_template.ts +1 -0
  44. package/src/useCollectionEditorPlugin.tsx +10 -4
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useState } from "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
- existingAliases,
35
+ existingIds,
36
36
  groups,
37
37
  parentCollection
38
38
  }: {
39
39
  isNewCollection: boolean,
40
40
  reservedGroups?: string[];
41
41
  existingPaths?: string[];
42
- existingAliases?: string[];
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
- const collectionIcon = getIconForView(values);
77
-
78
- const validatePath = useCallback((value: string) => {
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
- if (isNewCollection && existingAliases?.includes(value.trim().toLowerCase()))
84
- error = "There is already a collection which uses this path as an alias";
85
- if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()) && !values.alias)
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={"alias"}
246
+ <Field name={"id"}
256
247
  as={DebouncedTextField}
257
248
  disabled={!isNewCollection}
258
- label={"Alias"}
259
- validate={validateAlias}
260
- error={touched.alias && Boolean(errors.alias)}/>
261
- <FieldHelperView error={touched.alias && Boolean(errors.alias)}>
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="ID generation"
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
- parentPathSegments?: string[]; // path segments of the parent collection, like [`products`]
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
- parentPathSegments,
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 && (parentPathSegments ?? []).length === 0;
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 existingAliases = collectionsInThisLevel.map(col => col.alias?.trim().toLowerCase()).filter(Boolean) as string[];
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>>([...(parentPathSegments ?? []), editedCollectionPath]));
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.alias || updatedCollection.path;
215
+ const fullPath = updatedCollection.id || updatedCollection.path;
215
216
  return configController.saveCollection({
216
- path: fullPath,
217
+ id: fullPath,
217
218
  collectionData: updatedCollection,
218
219
  previousPath: editedCollectionPath,
219
- parentPathSegments
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 initialValues: PersistedCollection<M> = collection ? applyPropertyConfigs(collection, propertyConfigs) : {
237
- path: initialValuesProp?.path ?? "",
238
- name: initialValuesProp?.name ?? "",
239
- group: initialValuesProp?.group ?? "",
240
- properties: {} as PropertiesOrBuilders<M>,
241
- propertiesOrder: [],
242
- icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
243
- ownerId: authController.user?.uid ?? ""
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, parentPathSegments ?? []);
272
- }, [collectionInference, parentPathSegments]);
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
- }, [parentPathSegments, doCollectionInference]);
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
- return undefined;
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
- values,
403
- setFieldValue,
404
- isSubmitting,
405
- dirty,
406
- submitCount
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.path);
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
- existingAliases={existingAliases}
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
- parentPathSegments={parentPathSegments}
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
@@ -1,6 +1,7 @@
1
1
  import * as Yup from "yup";
2
2
 
3
3
  export const YupSchema = Yup.object().shape({
4
+ id: Yup.string().required("Required"),
4
5
  name: Yup.string().required("Required"),
5
6
  path: Yup.string().required("Required")
6
7
  });
@@ -17,7 +17,7 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
17
17
 
18
18
  const snackbarController = useSnackbarController();
19
19
 
20
- const code = "const " + camelCase(collection.name) + "Collection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
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,
@@ -1,4 +1,4 @@
1
- import { cn, PropertyConfig, FieldConfigBadge, SelectItem, Typography } from "@firecms/core";
1
+ import { cn, FieldConfigBadge, PropertyConfig, SelectItem, Typography } from "@firecms/core";
2
2
 
3
3
  export interface PropertySelectItemProps {
4
4
  value: string;
@@ -33,14 +33,14 @@ export function SubcollectionsEditTab({
33
33
  configController,
34
34
  collectionInference,
35
35
  getUser,
36
- parentPathSegments
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
- parentPathSegments?: string[];
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
- parentPathSegments: [...(parentPathSegments ?? []), collection.path]
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
- parentPathSegments={[...parentPathSegments ?? [], values.path]}
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, PropertyConfig, Paper, Property, Typography } from "@firecms/core";
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";
@@ -3,10 +3,10 @@ import {
3
3
  AddIcon,
4
4
  BooleanSwitchWithLabel,
5
5
  Button,
6
- PropertyConfig,
7
6
  MapProperty,
8
7
  Paper,
9
8
  Property,
9
+ PropertyConfig,
10
10
  Typography
11
11
  } from "@firecms/core";
12
12
  import { PropertyFormDialog } from "../PropertyEditView";
@@ -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.alias === selected || collection.path === selected);
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.alias ?? collection.path}-${group}`}
141
- value={collection.alias ?? collection.path}>
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.alias ?? collection.path}
162
- value={collection.alias ?? collection.path}>
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";
@@ -1,6 +1,7 @@
1
1
  import { EntityCollection, makePropertiesEditable } from "@firecms/core";
2
2
 
3
3
  export const blogCollectionTemplate:EntityCollection = {
4
+ id: "blog",
4
5
  path: "blog",
5
6
  name: "Blog",
6
7
  singularName: "Blog entry",
@@ -1,6 +1,7 @@
1
1
  import { EntityCollection, makePropertiesEditable } from "@firecms/core";
2
2
 
3
3
  export const productsCollectionTemplate: EntityCollection = {
4
+ id: "products",
4
5
  path: "products",
5
6
  name: "Products",
6
7
  singularName: "Product",
@@ -1,6 +1,7 @@
1
1
  import { EntityCollection, makePropertiesEditable } from "@firecms/core";
2
2
 
3
3
  export const usersCollectionTemplate: EntityCollection = {
4
+ id: "users",
4
5
  path: "users",
5
6
  name: "Users",
6
7
  singularName: "User",
@@ -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 editableCollections = collectionConfigController.collections ?? [];
88
- editableCollections.forEach(markAsEditable);
89
- return joinCollectionLists(editableCollections, collections);
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",