@firecms/collection_editor 3.0.0-beta.4.pre.2 → 3.0.0-beta.6

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 (36) hide show
  1. package/LICENSE +113 -21
  2. package/dist/ConfigControllerProvider.d.ts +2 -2
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.es.js +1749 -1730
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +2 -2
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/collection_editor_controller.d.ts +1 -1
  9. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  10. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +2 -2
  11. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
  12. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
  13. package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
  14. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  15. package/dist/useCollectionEditorPlugin.d.ts +6 -9
  16. package/dist/utils/collections.d.ts +6 -0
  17. package/package.json +15 -15
  18. package/src/ConfigControllerProvider.tsx +28 -34
  19. package/src/index.ts +1 -0
  20. package/src/types/collection_editor_controller.tsx +1 -1
  21. package/src/ui/EditorCollectionAction.tsx +0 -51
  22. package/src/ui/EditorCollectionActionStart.tsx +87 -0
  23. package/src/ui/NewCollectionButton.tsx +12 -10
  24. package/src/ui/collection_editor/CollectionDetailsForm.tsx +1 -1
  25. package/src/ui/collection_editor/CollectionEditorDialog.tsx +31 -16
  26. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +2 -2
  27. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +8 -7
  28. package/src/ui/collection_editor/GetCodeDialog.tsx +15 -3
  29. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +1 -1
  30. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +23 -8
  31. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +9 -7
  32. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +9 -3
  33. package/src/useCollectionEditorPlugin.tsx +28 -27
  34. package/src/utils/collections.ts +30 -0
  35. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  36. package/src/ui/RootCollectionSuggestions.tsx +0 -63
@@ -76,7 +76,7 @@ export interface CollectionEditorDialogProps {
76
76
  icon: React.ReactNode
77
77
  };
78
78
  pathSuggestions?: (path?: string) => Promise<string[]>;
79
- getUser: (uid: string) => User | null;
79
+ getUser?: (uid: string) => User | null;
80
80
  getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
81
81
  parentCollection?: PersistedCollection;
82
82
  }
@@ -136,7 +136,7 @@ type EditorView = "welcome"
136
136
  | "extra_view"
137
137
  | "subcollections";
138
138
 
139
- export function CollectionEditor<M extends Record<string, any>>(props: CollectionEditorDialogProps & {
139
+ export function CollectionEditor(props: CollectionEditorDialogProps & {
140
140
  handleCancel: () => void,
141
141
  setFormDirty: (dirty: boolean) => void
142
142
  }) {
@@ -154,14 +154,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
154
154
  const collectionsInThisLevel = (props.parentCollection ? props.parentCollection.subcollections : collections) ?? [];
155
155
  const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
156
156
  const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
157
- const [collection, setCollection] = React.useState<PersistedCollection<M> | undefined>();
157
+ const [collection, setCollection] = React.useState<PersistedCollection<any> | undefined>();
158
158
  const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
159
159
 
160
160
  useEffect(() => {
161
161
  try {
162
162
  if (navigation.initialised) {
163
163
  if (props.editedCollectionId) {
164
- setCollection(navigation.getCollectionFromPaths<PersistedCollection<M>>([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
164
+ setCollection(navigation.getCollectionFromPaths([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
165
165
  } else {
166
166
  setCollection(undefined);
167
167
  }
@@ -170,7 +170,8 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
170
170
  } catch (e) {
171
171
  console.error(e);
172
172
  }
173
- }, [navigation.getCollectionFromPaths, props.editedCollectionId, props.parentCollectionIds, navigation.initialised]);
173
+ }, [props.editedCollectionId, props.parentCollectionIds, navigation]);
174
+
174
175
  if (!topLevelNavigation) {
175
176
  throw Error("Internal: Navigation not ready in collection editor");
176
177
  }
@@ -186,14 +187,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
186
187
  }
187
188
  : undefined;
188
189
 
189
- const initialValues: PersistedCollection<M> = initialCollection
190
+ const initialValues: PersistedCollection<any> = initialCollection
190
191
  ? applyPropertyConfigs(initialCollection, propertyConfigs)
191
192
  : {
192
193
  id: initialValuesProp?.path ?? randomString(16),
193
194
  path: initialValuesProp?.path ?? "",
194
195
  name: initialValuesProp?.name ?? "",
195
196
  group: initialValuesProp?.group ?? "",
196
- properties: {} as PropertiesOrBuilders<M>,
197
+ properties: {} as PropertiesOrBuilders,
197
198
  propertiesOrder: [],
198
199
  icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
199
200
  ownerId: authController.user?.uid ?? ""
@@ -486,19 +487,23 @@ function CollectionEditorInternal<M extends Record<string, any>>({
486
487
  setFormDirty(dirty);
487
488
  }, [dirty]);
488
489
 
489
- function onImportDataSet(data: object[]) {
490
+ function onImportDataSet(data: object[], propertiesOrder?: string[]) {
490
491
  importConfig.setInUse(true);
491
492
  buildEntityPropertiesFromData(data, getInferenceType)
492
493
  .then((properties) => {
493
494
  const res = cleanPropertiesFromImport(properties);
494
495
 
495
- setFieldValue("properties", res.properties);
496
- setFieldValue("propertiesOrder", Object.keys(res.properties));
497
-
498
496
  importConfig.setIdColumn(res.idColumn);
499
497
  importConfig.setImportData(data);
500
498
  importConfig.setHeadersMapping(res.headersMapping);
499
+ const filteredHeadingsOrder = ((propertiesOrder ?? [])
500
+ .filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
501
+ importConfig.setHeadingsOrder(filteredHeadingsOrder);
501
502
  importConfig.setOriginProperties(res.properties);
503
+
504
+ const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
505
+ setFieldValue("properties", res.properties);
506
+ setFieldValue("propertiesOrder", mappedHeadings);
502
507
  });
503
508
  }
504
509
 
@@ -551,9 +556,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
551
556
  {currentView === "welcome" &&
552
557
  <CollectionEditorWelcomeView
553
558
  path={path}
554
- onContinue={(importData) => {
559
+ onContinue={(importData, propertiesOrder) => {
560
+ // console.log("Import data", importData, propertiesOrder)
555
561
  if (importData) {
556
- onImportDataSet(importData);
562
+ onImportDataSet(importData, propertiesOrder);
557
563
  setCurrentView("import_data_mapping");
558
564
  } else {
559
565
  setCurrentView("details");
@@ -733,7 +739,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
733
739
  }
734
740
 
735
741
  function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
736
- const { properties, ...rest } = collection;
742
+ const {
743
+ properties,
744
+ ...rest
745
+ } = collection;
737
746
  const propertiesResult: PropertiesOrBuilders<any> = {};
738
747
  if (properties) {
739
748
  Object.keys(properties).forEach((key) => {
@@ -741,7 +750,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
741
750
  });
742
751
  }
743
752
 
744
- return { ...rest, properties: propertiesResult };
753
+ return {
754
+ ...rest,
755
+ properties: propertiesResult
756
+ };
745
757
  }
746
758
 
747
759
  function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
@@ -761,7 +773,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
761
773
  Object.keys(internalProperty.properties).forEach((key) => {
762
774
  properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
763
775
  });
764
- internalProperty = { ...internalProperty, properties };
776
+ internalProperty = {
777
+ ...internalProperty,
778
+ properties
779
+ };
765
780
  }
766
781
 
767
782
  }
@@ -19,7 +19,7 @@ export function CollectionEditorWelcomeView({
19
19
  path: string;
20
20
  pathSuggestions?: (path: string) => Promise<string[]>;
21
21
  parentCollection?: EntityCollection;
22
- onContinue: (importData?: object[]) => void;
22
+ onContinue: (importData?: object[], propertiesOrder?: string[]) => void;
23
23
  existingCollectionPaths?: string[];
24
24
  }) {
25
25
 
@@ -154,7 +154,7 @@ export function CollectionEditorWelcomeView({
154
154
  ● Create a collection from a file (csv, json, xls, xslx...)
155
155
  </Typography>
156
156
 
157
- <ImportFileUpload onDataAdded={(data) => onContinue(data)}/>
157
+ <ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
158
158
 
159
159
  </div>}
160
160
 
@@ -43,7 +43,7 @@ type CollectionEditorFormProps = {
43
43
  setDirty?: (dirty: boolean) => void;
44
44
  reservedGroups?: string[];
45
45
  extraIcon: React.ReactNode;
46
- getUser: (uid: string) => User | null;
46
+ getUser?: (uid: string) => User | null;
47
47
  getData?: () => Promise<object[]>;
48
48
  doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
49
49
  propertyConfigs: Record<string, PropertyConfig>;
@@ -301,7 +301,7 @@ export function CollectionPropertiesEditorForm({
301
301
  ? values.propertiesOrder
302
302
  : Object.keys(values.properties)) as string[];
303
303
 
304
- const owner = useMemo(() => values.ownerId ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
304
+ const owner = useMemo(() => values.ownerId && getUser ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
305
305
 
306
306
  const onPropertyClick = useCallback((propertyKey: string, namespace?: string) => {
307
307
  console.debug("CollectionEditor: onPropertyClick", {
@@ -496,11 +496,12 @@ export function CollectionPropertiesEditorForm({
496
496
  collectionEditable={collectionEditable}
497
497
  existingPropertyKeys={values.propertiesOrder as string[]}/>
498
498
 
499
- <GetCodeDialog
500
- collection={values}
501
- open={codeDialogOpen}
502
- onOpenChange={setCodeDialogOpen}/>
503
-
499
+ <ErrorBoundary>
500
+ <GetCodeDialog
501
+ collection={values}
502
+ open={codeDialogOpen}
503
+ onOpenChange={setCodeDialogOpen}/>
504
+ </ErrorBoundary>
504
505
  </>
505
506
  );
506
507
  }
@@ -5,11 +5,17 @@ import JSON5 from "json5";
5
5
  import { Highlight, themes } from "prism-react-renderer"
6
6
  import { camelCase } from "./utils/strings";
7
7
 
8
- export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange: (open: boolean) => void, collection: any, open: any }) {
8
+ export function GetCodeDialog({
9
+ collection,
10
+ onOpenChange,
11
+ open
12
+ }: { onOpenChange: (open: boolean) => void, collection: any, open: any }) {
9
13
 
10
14
  const snackbarController = useSnackbarController();
11
15
 
12
- const code = "import { EntityCollection } from \"firecms\";\n\nconst " + (collection.name ? camelCase(collection.name) : "my") + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
16
+ const code = collection
17
+ ? "import { EntityCollection } from \"firecms\";\n\nconst " + (collection?.name ? camelCase(collection.name) : "my") + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t")
18
+ : "No collection selected";
13
19
  return <Dialog open={open}
14
20
  onOpenChange={onOpenChange}
15
21
  maxWidth={"4xl"}>
@@ -29,7 +35,13 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
29
35
  code={code}
30
36
  language="typescript"
31
37
  >
32
- {({ className, style, tokens, getLineProps, getTokenProps }) => (
38
+ {({
39
+ className,
40
+ style,
41
+ tokens,
42
+ getLineProps,
43
+ getTokenProps
44
+ }) => (
33
45
  <pre style={style} className={"p-4 rounded text-sm"}>
34
46
  {tokens.map((line, i) => (
35
47
  <div key={i} {...getLineProps({ line })}>
@@ -41,7 +41,7 @@ export function SubcollectionsEditTab({
41
41
  parentCollection?: EntityCollection,
42
42
  configController: CollectionsConfigController;
43
43
  collectionInference?: CollectionInference;
44
- getUser: (uid: string) => User | null;
44
+ getUser?: (uid: string) => User | null;
45
45
  parentCollectionIds?: string[];
46
46
  }) {
47
47
 
@@ -1,21 +1,33 @@
1
- import { convertDataToEntity, getPropertiesMapping, ImportConfig } from "@firecms/data_import_export";
2
- import { EntityCollectionTable, Properties, useSelectionController } from "@firecms/core";
3
- import { useEffect } from "react";
1
+ import { convertDataToEntity, ImportConfig } from "@firecms/data_import_export";
2
+ import { CircularProgressCenter, EntityCollectionTable, Properties, useSelectionController } from "@firecms/core";
3
+ import { useEffect, useState } from "react";
4
4
  import { Typography } from "@firecms/ui";
5
5
 
6
- export function CollectionEditorImportDataPreview({ importConfig, properties, propertiesOrder }: {
6
+ export function CollectionEditorImportDataPreview({
7
+ importConfig,
8
+ properties,
9
+ propertiesOrder
10
+ }: {
7
11
  importConfig: ImportConfig,
8
12
  properties: Properties,
9
13
  propertiesOrder: string[]
10
14
  }) {
11
15
 
12
- useEffect(() => {
13
- const propertiesMapping = getPropertiesMapping(importConfig.originProperties, properties);
14
- const mappedData = importConfig.importData.map(d => convertDataToEntity(d, importConfig.idColumn, importConfig.headersMapping, properties, propertiesMapping, "TEMP_PATH"));
16
+ const [loading, setLoading] = useState<boolean>(false);
17
+
18
+ async function loadEntities() {
19
+ // const propertiesMapping = getPropertiesMapping(importConfig.originProperties, properties, importConfig.headersMapping);
20
+ const mappedData = importConfig.importData.map(d => convertDataToEntity(d, importConfig.idColumn, importConfig.headersMapping, properties, "TEMP_PATH", importConfig.defaultValues));
15
21
  importConfig.setEntities(mappedData);
22
+ }
23
+
24
+ useEffect(() => {
25
+ loadEntities().finally(() => setLoading(false));
16
26
  }, []);
17
27
 
18
28
  const selectionController = useSelectionController();
29
+ if (loading)
30
+ return <CircularProgressCenter/>
19
31
 
20
32
  return <EntityCollectionTable
21
33
  title={<div>
@@ -31,7 +43,10 @@ export function CollectionEditorImportDataPreview({ importConfig, properties, pr
31
43
  filterable={false}
32
44
  sortable={false}
33
45
  selectionController={selectionController}
34
- displayedColumnIds={propertiesOrder.map(p => ({ key: p, disabled: false }))}
46
+ displayedColumnIds={propertiesOrder.map(p => ({
47
+ key: p,
48
+ disabled: false
49
+ }))}
35
50
  properties={properties}/>
36
51
 
37
52
  }
@@ -6,7 +6,7 @@ import {
6
6
  } from "@firecms/data_import_export";
7
7
  import { getIn, useFormex } from "@firecms/formex";
8
8
 
9
- import { PropertyConfigBadge, getFieldConfig, getFieldId, Properties, Property, PropertyConfig, } from "@firecms/core";
9
+ import { getFieldConfig, getFieldId, Properties, Property, PropertyConfig, PropertyConfigBadge, } from "@firecms/core";
10
10
  import { Container, Select, Tooltip, Typography } from "@firecms/ui";
11
11
  import React, { useState } from "react";
12
12
  import { OnPropertyChangedParams, PropertyFormDialog, PropertyWithId } from "../PropertyEditView";
@@ -143,18 +143,20 @@ export function CollectionEditorImportMapping({
143
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
- <Typography variant="h6" className={"mt-4"}>Data property mapping</Typography>
146
+ <Typography variant="h6" className={"my-4 ml-3.5"}>Data property mapping</Typography>
147
147
 
148
- <DataNewPropertiesMapping headersMapping={importConfig.headersMapping}
149
- idColumn={importConfig.idColumn}
150
- originProperties={importConfig.originProperties}
148
+ <DataNewPropertiesMapping importConfig={importConfig}
151
149
  destinationProperties={values.properties as Properties}
152
- onIdPropertyChanged={(value) => importConfig.setIdColumn(value ?? undefined)}
153
150
  buildPropertyView={({
154
151
  property,
155
152
  propertyKey,
156
- importKey
153
+ importKey,
154
+ isIdColumn
157
155
  }) => {
156
+ if (isIdColumn) {
157
+ return <Typography> This column will be used as ID</Typography>
158
+ }
159
+
158
160
  return <ImportNewPropertyFieldPreview
159
161
  property={property}
160
162
  propertyKey={propertyKey}
@@ -2,11 +2,17 @@ import React, { useCallback, useState } from "react";
2
2
  import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
3
3
  import { getIn, useFormex } from "@firecms/formex";
4
4
  import { PropertyFormDialog } from "../PropertyEditView";
5
- import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath } from "../util";
5
+ import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath, namespaceToPropertiesPath } from "../util";
6
6
  import { PropertyTree } from "../PropertyTree";
7
7
  import { ArrayProperty, Property, PropertyConfig } from "@firecms/core";
8
8
 
9
- export function BlockPropertyField({ disabled, getData, allowDataInference, propertyConfigs, collectionEditable }: {
9
+ export function BlockPropertyField({
10
+ disabled,
11
+ getData,
12
+ allowDataInference,
13
+ propertyConfigs,
14
+ collectionEditable
15
+ }: {
10
16
  disabled: boolean;
11
17
  getData?: () => Promise<object[]>;
12
18
  allowDataInference: boolean;
@@ -50,7 +56,7 @@ export function BlockPropertyField({ disabled, getData, allowDataInference, prop
50
56
 
51
57
  setFieldValue(`oneOf.${idToPropertiesPath(fullId)}`, undefined, false);
52
58
  const propertiesOrderPath = `oneOf.${namespaceToPropertiesOrderPath(namespace)}`;
53
- const currentPropertiesOrder: string[] = getIn(values, propertiesOrderPath);
59
+ const currentPropertiesOrder: string[] = getIn(values, propertiesOrderPath) ?? Object.keys(getIn(values, namespaceToPropertiesPath(namespace)));
54
60
  setFieldValue(propertiesOrderPath, currentPropertiesOrder.filter((p) => p !== propertyKey), false);
55
61
 
56
62
  setPropertyDialogOpen(false);
@@ -4,16 +4,16 @@ import { ConfigControllerProvider } from "./ConfigControllerProvider";
4
4
  import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
5
5
  import { EditorCollectionAction } from "./ui/EditorCollectionAction";
6
6
  import { HomePageEditorCollectionAction } from "./ui/HomePageEditorCollectionAction";
7
- import { NewCollectionCard } from "./ui/NewCollectionCard";
8
7
  import { PersistedCollection } from "./types/persisted_collection";
9
8
  import { CollectionInference } from "./types/collection_inference";
10
9
  import { CollectionsConfigController } from "./types/config_controller";
11
- import { RootCollectionSuggestions } from "./ui/RootCollectionSuggestions";
12
10
  import { CollectionViewHeaderAction } from "./ui/CollectionViewHeaderAction";
13
11
  import { PropertyAddColumnComponent } from "./ui/PropertyAddColumnComponent";
14
12
  import { NewCollectionButton } from "./ui/NewCollectionButton";
15
- import { AddIcon, Button, Typography } from "@firecms/ui";
13
+ import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
16
14
  import { useCollectionEditorController } from "./useCollectionEditorController";
15
+ import { EditorCollectionActionStart } from "./ui/EditorCollectionActionStart";
16
+ import { NewCollectionCard } from "./ui/NewCollectionCard";
17
17
 
18
18
  export interface CollectionConfigControllerProps<EC extends PersistedCollection = PersistedCollection, UserType extends User = User> {
19
19
 
@@ -32,7 +32,7 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
32
32
  * names when creating collections.
33
33
  * e.g. ["admin"]
34
34
  */
35
- reservedGroups: string[];
35
+ reservedGroups?: string[];
36
36
 
37
37
  extraView?: {
38
38
  View: React.ComponentType<{
@@ -41,18 +41,16 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
41
41
  icon: React.ReactNode
42
42
  };
43
43
 
44
- pathSuggestions?: (path: string) => Promise<string[]>;
44
+ getPathSuggestions?: (path?: string) => Promise<string[]>;
45
45
 
46
46
  collectionInference?: CollectionInference;
47
47
 
48
48
  getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
49
49
 
50
- getUser: (uid: string) => UserType | null;
50
+ getUser?: (uid: string) => UserType | null;
51
51
 
52
52
  onAnalyticsEvent?: (event: string, params?: object) => void;
53
53
 
54
- introMode?: "new_project" | "existing_project";
55
-
56
54
  }
57
55
 
58
56
  /**
@@ -62,18 +60,17 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
62
60
  * @param configPermissions
63
61
  * @param reservedGroups
64
62
  * @param extraView
65
- * @param pathSuggestions
63
+ * @param getPathsSuggestions
66
64
  * @param getUser
67
65
  * @param collectionInference
68
66
  */
69
67
  export function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>
70
68
  ({
71
69
  collectionConfigController,
72
- introMode,
73
70
  configPermissions,
74
71
  reservedGroups,
75
72
  extraView,
76
- pathSuggestions,
73
+ getPathSuggestions,
77
74
  getUser,
78
75
  collectionInference,
79
76
  getData,
@@ -81,7 +78,7 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
81
78
  }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection> {
82
79
 
83
80
  return {
84
- name: "Collection Editor",
81
+ key: "collection_editor",
85
82
  loading: collectionConfigController.loading,
86
83
  provider: {
87
84
  Component: ConfigControllerProvider,
@@ -91,7 +88,7 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
91
88
  collectionInference,
92
89
  reservedGroups,
93
90
  extraView,
94
- pathSuggestions,
91
+ getPathSuggestions,
95
92
  getUser,
96
93
  getData,
97
94
  onAnalyticsEvent
@@ -99,12 +96,13 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
99
96
  },
100
97
  homePage: {
101
98
  additionalActions: <NewCollectionButton/>,
102
- additionalChildrenStart: introMode ? <IntroWidget introMode={introMode}/> : undefined,
103
- additionalChildrenEnd: <RootCollectionSuggestions introMode={introMode}/>,
99
+ additionalChildrenStart: <IntroWidget/>,
100
+ // additionalChildrenEnd: <RootCollectionSuggestions introMode={introMode}/>,
104
101
  CollectionActions: HomePageEditorCollectionAction,
105
- AdditionalCards: introMode ? undefined : NewCollectionCard,
102
+ AdditionalCards: NewCollectionCard,
106
103
  },
107
104
  collectionView: {
105
+ CollectionActionsStart: EditorCollectionActionStart,
108
106
  CollectionActions: EditorCollectionAction,
109
107
  HeaderAction: CollectionViewHeaderAction,
110
108
  AddColumnComponent: PropertyAddColumnComponent
@@ -112,9 +110,7 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
112
110
  };
113
111
  }
114
112
 
115
- export function IntroWidget({ introMode }: {
116
- introMode?: "new_project" | "existing_project";
117
- }) {
113
+ export function IntroWidget({}: {}) {
118
114
 
119
115
  const navigation = useNavigationController();
120
116
  if (!navigation.topLevelNavigation)
@@ -129,17 +125,19 @@ export function IntroWidget({ introMode }: {
129
125
  }).createCollections
130
126
  : true;
131
127
 
128
+ if (!navigation.initialised || navigation.collections === undefined || (navigation.collections ?? []).length > 0) {
129
+ return null;
130
+ }
131
+
132
132
  return (
133
- <div className={"mt-8 flex flex-col mt-8 p-2"}>
134
- <Typography variant={"h4"} className="mb-4">Welcome</Typography>
135
- <Typography paragraph={true}>Your admin panel is ready ✌️</Typography>
136
- <Typography paragraph={true}>
133
+ <Paper
134
+ className={"my-4 px-4 py-6 flex flex-col bg-white dark:bg-slate-800 gap-2"}>
135
+ <Typography variant={"subtitle2"} className={"uppercase"}>No collections found</Typography>
136
+ <Typography>
137
137
  Start building collections in FireCMS easily. Map them to your existing
138
- database data, import from files, or use our templates. Simplify your data management process
139
- now.
138
+ database data, import from files, or use our templates.
140
139
  </Typography>
141
140
  {canCreateCollections && <Button
142
- className={"mt-4"}
143
141
  onClick={collectionEditorController && canCreateCollections
144
142
  ? () => collectionEditorController.createCollection({
145
143
  parentCollectionIds: [],
@@ -149,6 +147,9 @@ export function IntroWidget({ introMode }: {
149
147
  : undefined}>
150
148
  <AddIcon/>Create your first collection
151
149
  </Button>}
152
- </div>
150
+ <Typography variant={"caption"} color={"secondary"}>
151
+ You can also define collections programmatically.
152
+ </Typography>
153
+ </Paper>
153
154
  );
154
155
  }
@@ -0,0 +1,30 @@
1
+ import {
2
+ EntityCollection,
3
+ joinCollectionLists,
4
+ makePropertiesEditable,
5
+ ModifyCollectionProps,
6
+ Properties
7
+ } from "@firecms/core";
8
+ import { PersistedCollection } from "../types/persisted_collection";
9
+
10
+ /**
11
+ * Function in charge of merging collections defined in code with those stored in the backend.
12
+ */
13
+ export const mergeCollections = (baseCollections: EntityCollection[],
14
+ backendCollections: PersistedCollection[],
15
+ modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void
16
+ ) => {
17
+
18
+ const markAsEditable = (c: PersistedCollection) => {
19
+ makePropertiesEditable(c.properties as Properties);
20
+ c.subcollections?.forEach(markAsEditable);
21
+ };
22
+ const storedCollections = backendCollections ?? [];
23
+ storedCollections.forEach(markAsEditable);
24
+
25
+ console.debug("Collections specified in code:", baseCollections);
26
+ console.debug("Collections stored in the backend", storedCollections);
27
+ const result = joinCollectionLists(baseCollections, storedCollections, [], modifyCollection);
28
+ console.debug("Collections after joining:", result);
29
+ return result;
30
+ }
@@ -1,3 +0,0 @@
1
- export declare function RootCollectionSuggestions({ introMode }: {
2
- introMode?: "new_project" | "existing_project";
3
- }): import("react/jsx-runtime").JSX.Element;
@@ -1,63 +0,0 @@
1
- import { unslugify, useAuthController, useNavigationController } from "@firecms/core";
2
- import { AddIcon, Chip, CircularProgress, Collapse, Typography, } from "@firecms/ui";
3
- import { useCollectionEditorController } from "../useCollectionEditorController";
4
- import React from "react";
5
-
6
- export function RootCollectionSuggestions({ introMode }: { introMode?: "new_project" | "existing_project" }) {
7
-
8
- const authController = useAuthController();
9
- const navigationController = useNavigationController();
10
-
11
- const collectionEditorController = useCollectionEditorController();
12
- const canCreateCollections = collectionEditorController.configPermissions
13
- ? collectionEditorController.configPermissions({
14
- user: authController.user
15
- }).createCollections
16
- : true;
17
-
18
- const rootPathSuggestions = collectionEditorController.rootPathSuggestions;
19
-
20
- const showSuggestions = (rootPathSuggestions ?? []).length > 3 || ((navigationController.collections ?? []).length === 0 && (rootPathSuggestions ?? []).length > 0);
21
- const forceShowSuggestions = introMode === "existing_project";
22
- return <Collapse
23
- in={forceShowSuggestions || showSuggestions}>
24
-
25
- <div
26
- className={"flex flex-col gap-1 p-2 my-4"}>
27
-
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>}
35
-
36
- <div
37
- className={"flex flex-row gap-1 overflow-scroll no-scrollbar "}>
38
- {(rootPathSuggestions ?? []).map((path) => {
39
- return (
40
- <div key={path}>
41
- <Chip
42
- icon={<AddIcon size={"small"}/>}
43
- colorScheme={"cyanLighter"}
44
- onClick={collectionEditorController && canCreateCollections
45
- ? () => collectionEditorController.createCollection({
46
- initialValues: { path, name: unslugify(path) },
47
- parentCollectionIds: [],
48
- redirect: true,
49
- sourceClick: "root_collection_suggestion"
50
- })
51
- : undefined}
52
- size="small">
53
- {path}
54
- </Chip>
55
- </div>
56
- );
57
- })}
58
- {rootPathSuggestions === undefined && <CircularProgress size={"small"}/>}
59
- {rootPathSuggestions?.length === 0 && <Typography variant={"caption"}>No suggestions</Typography>}
60
- </div>
61
- </div>
62
- </Collapse>
63
- }