@firecms/collection_editor 3.0.0-canary.8 → 3.0.0-canary.80

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 (49) 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 +1928 -1847
  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 +4 -2
  9. package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
  10. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  11. package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
  12. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +4 -3
  13. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
  14. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
  15. package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
  16. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  17. package/dist/useCollectionEditorPlugin.d.ts +6 -9
  18. package/dist/utils/collections.d.ts +6 -0
  19. package/package.json +19 -35
  20. package/src/ConfigControllerProvider.tsx +65 -62
  21. package/src/index.ts +1 -0
  22. package/src/types/collection_editor_controller.tsx +6 -4
  23. package/src/ui/CollectionViewHeaderAction.tsx +6 -3
  24. package/src/ui/EditorCollectionAction.tsx +9 -63
  25. package/src/ui/EditorCollectionActionStart.tsx +87 -0
  26. package/src/ui/HomePageEditorCollectionAction.tsx +16 -11
  27. package/src/ui/NewCollectionButton.tsx +12 -10
  28. package/src/ui/NewCollectionCard.tsx +3 -3
  29. package/src/ui/PropertyAddColumnComponent.tsx +6 -3
  30. package/src/ui/collection_editor/CollectionDetailsForm.tsx +34 -4
  31. package/src/ui/collection_editor/CollectionEditorDialog.tsx +56 -31
  32. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +4 -4
  33. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +27 -24
  34. package/src/ui/collection_editor/GetCodeDialog.tsx +15 -3
  35. package/src/ui/collection_editor/PropertyEditView.tsx +5 -4
  36. package/src/ui/collection_editor/PropertyFieldPreview.tsx +3 -6
  37. package/src/ui/collection_editor/PropertySelectItem.tsx +2 -2
  38. package/src/ui/collection_editor/PropertyTree.tsx +3 -3
  39. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
  40. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +25 -9
  41. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +9 -7
  42. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +14 -8
  43. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +1 -1
  44. package/src/ui/collection_editor/properties/MapPropertyField.tsx +5 -5
  45. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  46. package/src/useCollectionEditorPlugin.tsx +28 -27
  47. package/src/utils/collections.ts +30 -0
  48. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  49. package/src/ui/RootCollectionSuggestions.tsx +0 -63
@@ -1,16 +1,7 @@
1
- import equal from "react-fast-compare"
2
-
3
- import {
4
- CollectionActionsProps,
5
- mergeDeep,
6
- useAuthController,
7
- useNavigationController,
8
- useSnackbarController
9
- } from "@firecms/core";
10
- import { Button, IconButton, SaveIcon, SettingsIcon, Tooltip, UndoIcon, } from "@firecms/ui";
1
+ import { CollectionActionsProps, useAuthController, useNavigationController } from "@firecms/core";
2
+ import { IconButton, SettingsIcon, Tooltip, } from "@firecms/ui";
11
3
 
12
4
  import { useCollectionEditorController } from "../useCollectionEditorController";
13
- import { useCollectionsConfigController } from "../useCollectionsConfigController";
14
5
  import { PersistedCollection } from "../types/persisted_collection";
15
6
 
16
7
  export function EditorCollectionAction({
@@ -23,8 +14,6 @@ export function EditorCollectionAction({
23
14
  const authController = useAuthController();
24
15
  const navigationController = useNavigationController();
25
16
  const collectionEditorController = useCollectionEditorController();
26
- const configController = useCollectionsConfigController();
27
- const snackbarController = useSnackbarController();
28
17
 
29
18
  const parentCollection = navigationController.getCollectionFromIds(parentCollectionIds);
30
19
 
@@ -35,68 +24,25 @@ export function EditorCollectionAction({
35
24
  }).editCollections
36
25
  : true;
37
26
 
38
- let saveDefaultFilterButton = null;
39
- if (!equal(getObjectOrNull(tableController.filterValues), getObjectOrNull(collection.initialFilter)) ||
40
- !equal(getObjectOrNull(tableController.sortBy), getObjectOrNull(collection.initialSort))) {
41
- saveDefaultFilterButton = <>
42
- {(collection.initialFilter || collection.initialSort) && <Tooltip
43
- title={"Reset to default filter and sort"}>
44
- <Button
45
- color={"primary"}
46
- size={"small"}
47
- variant={"text"}
48
- onClick={() => {
49
- tableController.clearFilter?.();
50
- if (collection?.initialFilter)
51
- tableController.setFilterValues?.(collection?.initialFilter);
52
- if (collection?.initialSort)
53
- tableController.setSortBy?.(collection?.initialSort);
54
- }}>
55
- <UndoIcon/>
56
- </Button>
57
- </Tooltip>}
58
-
59
- <Tooltip
60
- title={tableController.sortBy || tableController.filterValues ? "Save default filter and sort" : "Clear default filter and sort"}>
61
- <Button
62
- color={"primary"}
63
- size={"small"}
64
- variant={"outlined"}
65
- onClick={() => configController
66
- ?.saveCollection({
67
- id: collection.id,
68
- parentCollectionIds,
69
- collectionData: mergeDeep(collection as PersistedCollection,
70
- {
71
- initialFilter: tableController.filterValues ?? null,
72
- initialSort: tableController.sortBy ?? null
73
- })
74
- }).then(() => {
75
- snackbarController.open({
76
- type: "success",
77
- message: "Default config saved"
78
- });
79
- })}>
80
- <SaveIcon/>
81
- </Button>
82
- </Tooltip>
83
- </>;
84
- }
85
-
86
27
  const editorButton = <Tooltip
87
28
  title={canEditCollection ? "Edit collection" : "You don't have permissions to edit this collection"}>
88
29
  <IconButton
89
30
  color={"primary"}
90
31
  disabled={!canEditCollection}
91
32
  onClick={canEditCollection
92
- ? () => collectionEditorController?.editCollection({ id: collection.id, fullPath, parentCollectionIds, parentCollection: parentCollection as PersistedCollection })
33
+ ? () => collectionEditorController?.editCollection({
34
+ id: collection.id,
35
+ fullPath,
36
+ parentCollectionIds,
37
+ parentCollection: parentCollection as PersistedCollection,
38
+ existingEntities: tableController?.data ?? []
39
+ })
93
40
  : undefined}>
94
41
  <SettingsIcon/>
95
42
  </IconButton>
96
43
  </Tooltip>;
97
44
 
98
45
  return <>
99
- {canEditCollection && saveDefaultFilterButton}
100
46
  {editorButton}
101
47
  </>
102
48
 
@@ -0,0 +1,87 @@
1
+ import equal from "react-fast-compare"
2
+
3
+ import { CollectionActionsProps, mergeDeep, useAuthController, useSnackbarController } from "@firecms/core";
4
+ import { Button, SaveIcon, Tooltip, UndoIcon, } from "@firecms/ui";
5
+
6
+ import { useCollectionEditorController } from "../useCollectionEditorController";
7
+ import { useCollectionsConfigController } from "../useCollectionsConfigController";
8
+ import { PersistedCollection } from "../types/persisted_collection";
9
+
10
+ export function EditorCollectionActionStart({
11
+ path: fullPath,
12
+ parentCollectionIds,
13
+ collection,
14
+ tableController
15
+ }: CollectionActionsProps) {
16
+
17
+ const authController = useAuthController();
18
+ const collectionEditorController = useCollectionEditorController();
19
+ const configController = useCollectionsConfigController();
20
+ const snackbarController = useSnackbarController();
21
+
22
+ const canEditCollection = collectionEditorController.configPermissions
23
+ ? collectionEditorController.configPermissions({
24
+ user: authController.user,
25
+ collection
26
+ }).editCollections
27
+ : true;
28
+
29
+ let saveDefaultFilterButton = null;
30
+ if (!equal(getObjectOrNull(tableController.filterValues), getObjectOrNull(collection.initialFilter)) ||
31
+ !equal(getObjectOrNull(tableController.sortBy), getObjectOrNull(collection.initialSort))) {
32
+ saveDefaultFilterButton = <>
33
+ <Tooltip
34
+ title={tableController.sortBy || tableController.filterValues ? "Save default filter and sort" : "Clear default filter and sort"}>
35
+ <Button
36
+ color={"primary"}
37
+ size={"small"}
38
+ variant={"outlined"}
39
+ onClick={() => configController
40
+ ?.saveCollection({
41
+ id: collection.id,
42
+ parentCollectionIds,
43
+ collectionData: mergeDeep(collection as PersistedCollection,
44
+ {
45
+ initialFilter: tableController.filterValues ?? null,
46
+ initialSort: tableController.sortBy ?? null
47
+ })
48
+ }).then(() => {
49
+ snackbarController.open({
50
+ type: "success",
51
+ message: "Default config saved"
52
+ });
53
+ })}>
54
+ <SaveIcon/>
55
+ </Button>
56
+ </Tooltip>
57
+
58
+ {(collection.initialFilter || collection.initialSort) && <Tooltip
59
+ title={"Reset to default filter and sort"}>
60
+ <Button
61
+ color={"primary"}
62
+ size={"small"}
63
+ variant={"text"}
64
+ onClick={() => {
65
+ tableController.clearFilter?.();
66
+ if (collection?.initialFilter)
67
+ tableController.setFilterValues?.(collection?.initialFilter);
68
+ if (collection?.initialSort)
69
+ tableController.setSortBy?.(collection?.initialSort);
70
+ }}>
71
+ <UndoIcon/>
72
+ </Button>
73
+ </Tooltip>}
74
+ </>;
75
+ }
76
+
77
+ return <>
78
+ {canEditCollection && saveDefaultFilterButton}
79
+ </>
80
+
81
+ }
82
+
83
+ function getObjectOrNull(o?: object): object | null {
84
+ if (o && Object.keys(o).length === 0)
85
+ return o
86
+ return o ?? null;
87
+ }
@@ -6,7 +6,7 @@ import {
6
6
  } from "@firecms/core";
7
7
  import { DeleteIcon, IconButton, Menu, MenuItem, MoreVertIcon, SettingsIcon, } from "@firecms/ui";
8
8
  import { useCollectionEditorController } from "../useCollectionEditorController";
9
- import { useCallback, useState } from "react";
9
+ import { useState } from "react";
10
10
  import { useCollectionsConfigController } from "../useCollectionsConfigController";
11
11
 
12
12
  export function HomePageEditorCollectionAction({
@@ -24,13 +24,16 @@ export function HomePageEditorCollectionAction({
24
24
  collection
25
25
  });
26
26
 
27
- const onEditCollectionClicked = useCallback(() => {
28
- collectionEditorController?.editCollection({ id: collection.id, parentCollectionIds: [] });
29
- }, [collectionEditorController, path]);
27
+ const onEditCollectionClicked = () => {
28
+ collectionEditorController?.editCollection({
29
+ id: collection.id,
30
+ parentCollectionIds: []
31
+ });
32
+ };
30
33
 
31
34
  const [deleteRequested, setDeleteRequested] = useState(false);
32
35
 
33
- const deleteCollection = useCallback(() => {
36
+ const deleteCollection = () => {
34
37
  configController?.deleteCollection({ id: collection.id }).then(() => {
35
38
  setDeleteRequested(false);
36
39
  snackbarController.open({
@@ -38,7 +41,7 @@ export function HomePageEditorCollectionAction({
38
41
  type: "success"
39
42
  });
40
43
  });
41
- }, [path, configController]);
44
+ };
42
45
 
43
46
  return <>
44
47
 
@@ -49,11 +52,13 @@ export function HomePageEditorCollectionAction({
49
52
  <MoreVertIcon size={"small"}/>
50
53
  </IconButton>}
51
54
  >
52
- <MenuItem onClick={(event) => {
53
- event.preventDefault();
54
- event.stopPropagation();
55
- setDeleteRequested(true);
56
- }}>
55
+ <MenuItem
56
+ dense={true}
57
+ onClick={(event) => {
58
+ event.preventDefault();
59
+ event.stopPropagation();
60
+ setDeleteRequested(true);
61
+ }}>
57
62
  <DeleteIcon/>
58
63
  Delete
59
64
  </MenuItem>
@@ -3,14 +3,16 @@ import { useCollectionEditorController } from "../useCollectionEditorController"
3
3
 
4
4
  export function NewCollectionButton() {
5
5
  const collectionEditorController = useCollectionEditorController();
6
- return <Button className={"min-w-fit"}
7
- variant={"outlined"}
8
- onClick={() => collectionEditorController.createCollection({
9
- parentCollectionIds: [],
10
- redirect: true,
11
- sourceClick: "new_collection_button"
12
- })}>
13
- <AddIcon/>
14
- New collection
15
- </Button>
6
+ return <div className={"bg-gray-50 dark:bg-gray-900 min-w-fit rounded"}>
7
+ <Button className={"min-w-fit"}
8
+ variant={"outlined"}
9
+ onClick={() => collectionEditorController.createCollection({
10
+ parentCollectionIds: [],
11
+ redirect: true,
12
+ sourceClick: "new_collection_button"
13
+ })}>
14
+ <AddIcon/>
15
+ New collection
16
+ </Button>
17
+ </div>
16
18
  }
@@ -1,5 +1,5 @@
1
1
  import { PluginHomePageAdditionalCardsProps, useAuthController } from "@firecms/core";
2
- import { AddIcon, Card, cn, Typography } from "@firecms/ui";
2
+ import { AddIcon, Card, cls, Typography } from "@firecms/ui";
3
3
  import { useCollectionEditorController } from "../useCollectionEditorController";
4
4
 
5
5
  export function NewCollectionCard({
@@ -20,7 +20,7 @@ export function NewCollectionCard({
20
20
  : true;
21
21
 
22
22
  return (
23
- <Card className={cn("h-full p-4 min-h-[124px]")}
23
+ <Card className={cls("h-full p-4 min-h-[124px]")}
24
24
  onClick={collectionEditorController && canCreateCollections
25
25
  ? () => collectionEditorController.createCollection({
26
26
  initialValues: group ? { group } : undefined,
@@ -31,7 +31,7 @@ export function NewCollectionCard({
31
31
  : undefined}>
32
32
 
33
33
  <div
34
- className="flex flex-col items-start h-full w-full items-center justify-center h-full w-full flex-grow flex-col">
34
+ className="flex items-center justify-center h-full w-full flex-grow flex-col">
35
35
  <AddIcon color="primary" size={"large"}/>
36
36
  <Typography color="primary"
37
37
  variant={"caption"}
@@ -1,4 +1,4 @@
1
- import { getDefaultPropertiesOrder, useAuthController } from "@firecms/core";
1
+ import { EntityTableController, getDefaultPropertiesOrder, useAuthController } from "@firecms/core";
2
2
  import { AddIcon, Tooltip } from "@firecms/ui";
3
3
  import { useCollectionEditorController } from "../useCollectionEditorController";
4
4
  import { PersistedCollection } from "../types/persisted_collection";
@@ -6,11 +6,13 @@ import { PersistedCollection } from "../types/persisted_collection";
6
6
  export function PropertyAddColumnComponent({
7
7
  fullPath,
8
8
  parentCollectionIds,
9
- collection
9
+ collection,
10
+ tableController
10
11
  }: {
11
12
  fullPath: string,
12
13
  parentCollectionIds: string[],
13
14
  collection: PersistedCollection;
15
+ tableController: EntityTableController;
14
16
  }) {
15
17
 
16
18
  const authController = useAuthController();
@@ -32,7 +34,8 @@ export function PropertyAddColumnComponent({
32
34
  editedCollectionId: collection.id,
33
35
  parentCollectionIds,
34
36
  currentPropertiesOrder: getDefaultPropertiesOrder(collection),
35
- collection
37
+ collection,
38
+ existingEntities: tableController.data
36
39
  });
37
40
  }}>
38
41
  <AddIcon color={"inherit"}/>
@@ -5,7 +5,8 @@ import {
5
5
  AutocompleteItem,
6
6
  BooleanSwitchWithLabel,
7
7
  Chip,
8
- cn,
8
+ ClearIcon,
9
+ cls,
9
10
  Container,
10
11
  DebouncedTextField,
11
12
  Dialog,
@@ -146,7 +147,7 @@ export function CollectionDetailsForm({
146
147
  </FieldCaption>
147
148
  </div>
148
149
 
149
- <div className={cn("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
150
+ <div className={cls("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
150
151
  <Field name={"path"}
151
152
  as={DebouncedTextField}
152
153
  label={"Path"}
@@ -216,7 +217,7 @@ export function CollectionDetailsForm({
216
217
  label={"Collection id"}
217
218
  error={showErrors && Boolean(errors.id)}/>
218
219
  <FieldCaption error={touched.id && Boolean(errors.id)}>
219
- {touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
220
+ {touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection. Typically the same as the path."}
220
221
  </FieldCaption>
221
222
  </div>
222
223
 
@@ -235,6 +236,35 @@ export function CollectionDetailsForm({
235
236
  {showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
236
237
  </FieldCaption>
237
238
  </div>
239
+ <div className={"col-span-12"}>
240
+ <TextField
241
+ error={showErrors && Boolean(errors.sideDialogWidth)}
242
+ name={"sideDialogWidth"}
243
+ type={"number"}
244
+ aria-describedby={"sideDialogWidth-helper"}
245
+ onChange={(e) => {
246
+ setFieldTouched("sideDialogWidth", true);
247
+ const value = e.target.value;
248
+ if (!value) {
249
+ setFieldValue("sideDialogWidth", null);
250
+ } else if (!isNaN(Number(value))) {
251
+ setFieldValue("sideDialogWidth", Number(value));
252
+ }
253
+ }}
254
+ endAdornment={<IconButton
255
+ size={"small"}
256
+ onClick={() => {
257
+ setFieldValue("sideDialogWidth", null);
258
+ }}
259
+ disabled={!values.sideDialogWidth}>
260
+ <ClearIcon size={"small"}/>
261
+ </IconButton>}
262
+ value={values.sideDialogWidth ?? ""}
263
+ label={"Side dialog width"}/>
264
+ <FieldCaption error={showErrors && Boolean(errors.singularName)}>
265
+ {showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define the width (in pixels) of entities side dialog. Default is 768px"}
266
+ </FieldCaption>
267
+ </div>
238
268
  <div className={"col-span-12"}>
239
269
  <TextField
240
270
  error={showErrors && Boolean(errors.description)}
@@ -272,7 +302,7 @@ export function CollectionDetailsForm({
272
302
  <div className={"col-span-12"}>
273
303
  <Select
274
304
  name="customId"
275
- label="Data IDs generation"
305
+ label="Document IDs generation"
276
306
  position={"item-aligned"}
277
307
  disabled={customIdValue === "code_defined"}
278
308
  onValueChange={(v) => {
@@ -1,7 +1,8 @@
1
1
  import * as React from "react";
2
- import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { useEffect, useRef, useState } from "react";
3
3
  import {
4
4
  CircularProgressCenter,
5
+ Entity,
5
6
  EntityCollection,
6
7
  ErrorView,
7
8
  isPropertyBuilder,
@@ -25,7 +26,7 @@ import {
25
26
  import {
26
27
  ArrowBackIcon,
27
28
  Button,
28
- cn,
29
+ cls,
29
30
  coolIconKeys,
30
31
  defaultBorderMixin,
31
32
  Dialog,
@@ -76,9 +77,10 @@ export interface CollectionEditorDialogProps {
76
77
  icon: React.ReactNode
77
78
  };
78
79
  pathSuggestions?: (path?: string) => Promise<string[]>;
79
- getUser: (uid: string) => User | null;
80
+ getUser?: (uid: string) => User | null;
80
81
  getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
81
82
  parentCollection?: PersistedCollection;
83
+ existingEntities?: Entity<any>[];
82
84
  }
83
85
 
84
86
  export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
@@ -88,13 +90,13 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
88
90
  const [formDirty, setFormDirty] = React.useState<boolean>(false);
89
91
  const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
90
92
 
91
- const handleCancel = useCallback(() => {
93
+ const handleCancel = () => {
92
94
  if (!formDirty) {
93
95
  props.handleClose(undefined);
94
96
  } else {
95
97
  setUnsavedChangesDialogOpen(true);
96
98
  }
97
- }, [formDirty, props.handleClose]);
99
+ };
98
100
 
99
101
  useEffect(() => {
100
102
  if (!open) {
@@ -136,7 +138,7 @@ type EditorView = "welcome"
136
138
  | "extra_view"
137
139
  | "subcollections";
138
140
 
139
- export function CollectionEditor<M extends Record<string, any>>(props: CollectionEditorDialogProps & {
141
+ export function CollectionEditor(props: CollectionEditorDialogProps & {
140
142
  handleCancel: () => void,
141
143
  setFormDirty: (dirty: boolean) => void
142
144
  }) {
@@ -154,14 +156,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
154
156
  const collectionsInThisLevel = (props.parentCollection ? props.parentCollection.subcollections : collections) ?? [];
155
157
  const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
156
158
  const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
157
- const [collection, setCollection] = React.useState<PersistedCollection<M> | undefined>();
159
+ const [collection, setCollection] = React.useState<PersistedCollection<any> | undefined>();
158
160
  const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
159
161
 
160
162
  useEffect(() => {
161
163
  try {
162
164
  if (navigation.initialised) {
163
165
  if (props.editedCollectionId) {
164
- setCollection(navigation.getCollectionFromPaths<PersistedCollection<M>>([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
166
+ setCollection(navigation.getCollectionFromPaths([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
165
167
  } else {
166
168
  setCollection(undefined);
167
169
  }
@@ -170,7 +172,8 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
170
172
  } catch (e) {
171
173
  console.error(e);
172
174
  }
173
- }, [navigation.getCollectionFromPaths, props.editedCollectionId, props.parentCollectionIds, navigation.initialised]);
175
+ }, [props.editedCollectionId, props.parentCollectionIds, navigation.initialised, navigation.getCollectionFromPaths]);
176
+
174
177
  if (!topLevelNavigation) {
175
178
  throw Error("Internal: Navigation not ready in collection editor");
176
179
  }
@@ -186,14 +189,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
186
189
  }
187
190
  : undefined;
188
191
 
189
- const initialValues: PersistedCollection<M> = initialCollection
192
+ const initialValues: PersistedCollection<any> = initialCollection
190
193
  ? applyPropertyConfigs(initialCollection, propertyConfigs)
191
194
  : {
192
195
  id: initialValuesProp?.path ?? randomString(16),
193
196
  path: initialValuesProp?.path ?? "",
194
197
  name: initialValuesProp?.name ?? "",
195
198
  group: initialValuesProp?.group ?? "",
196
- properties: {} as PropertiesOrBuilders<M>,
199
+ properties: {} as PropertiesOrBuilders,
197
200
  propertiesOrder: [],
198
201
  icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
199
202
  ownerId: authController.user?.uid ?? ""
@@ -243,7 +246,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
243
246
  setCollection,
244
247
  initialValues,
245
248
  propertyConfigs,
246
- groups
249
+ groups,
250
+ existingEntities
247
251
  }: CollectionEditorDialogProps & {
248
252
  handleCancel: () => void,
249
253
  setFormDirty: (dirty: boolean) => void,
@@ -293,7 +297,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
293
297
  });
294
298
  };
295
299
 
296
- const setNextMode = useCallback(() => {
300
+ const setNextMode = () => {
297
301
  if (currentView === "details") {
298
302
  if (importConfig.inUse) {
299
303
  setCurrentView("import_data_saving");
@@ -314,14 +318,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
314
318
  setCurrentView("details");
315
319
  }
316
320
 
317
- }, [currentView, importConfig.inUse, extraView]);
321
+ };
318
322
 
319
- const doCollectionInference = useCallback((collection: PersistedCollection<any>) => {
323
+ const doCollectionInference = (collection: PersistedCollection<any>) => {
320
324
  if (!collectionInference) return undefined;
321
325
  return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentCollectionIds ?? []);
322
- }, [collectionInference, parentCollectionIds]);
326
+ };
323
327
 
324
- const inferCollectionFromData = useCallback(async (newCollection: PersistedCollection<M>) => {
328
+ const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
325
329
 
326
330
  try {
327
331
  if (!doCollectionInference) {
@@ -365,10 +369,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
365
369
  });
366
370
  return newCollection;
367
371
  }
368
- }, [parentCollectionIds, doCollectionInference]);
372
+ };
369
373
 
370
374
  const onSubmit = (newCollectionState: PersistedCollection<M>, formexController: FormexController<PersistedCollection<M>>) => {
371
- console.log("Submitting collection", newCollectionState);
375
+ console.debug("Submitting collection", newCollectionState);
372
376
  try {
373
377
 
374
378
  if (!isNewCollection) {
@@ -480,25 +484,36 @@ function CollectionEditorInternal<M extends Record<string, any>>({
480
484
 
481
485
  const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
482
486
  const resolvedPath = !pathError ? navigation.resolveAliasesFrom(updatedFullPath) : undefined;
483
- const getDataWithPath = resolvedPath && getData ? () => getData(resolvedPath, parentPaths ?? []) : undefined;
487
+ const getDataWithPath = resolvedPath && getData ? async () => {
488
+ const data = await getData(resolvedPath, parentPaths ?? []);
489
+ if (existingEntities) {
490
+ const existingData = existingEntities.map(e => e.values);
491
+ data.push(...existingData);
492
+ }
493
+ return data;
494
+ } : undefined;
484
495
 
485
496
  useEffect(() => {
486
497
  setFormDirty(dirty);
487
498
  }, [dirty]);
488
499
 
489
- function onImportDataSet(data: object[]) {
500
+ function onImportDataSet(data: object[], propertiesOrder?: string[]) {
490
501
  importConfig.setInUse(true);
491
502
  buildEntityPropertiesFromData(data, getInferenceType)
492
503
  .then((properties) => {
493
504
  const res = cleanPropertiesFromImport(properties);
494
505
 
495
- setFieldValue("properties", res.properties);
496
- setFieldValue("propertiesOrder", Object.keys(res.properties));
497
-
498
506
  importConfig.setIdColumn(res.idColumn);
499
507
  importConfig.setImportData(data);
500
508
  importConfig.setHeadersMapping(res.headersMapping);
509
+ const filteredHeadingsOrder = ((propertiesOrder ?? [])
510
+ .filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
511
+ importConfig.setHeadingsOrder(filteredHeadingsOrder);
501
512
  importConfig.setOriginProperties(res.properties);
513
+
514
+ const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
515
+ setFieldValue("properties", res.properties);
516
+ setFieldValue("propertiesOrder", mappedHeadings);
502
517
  });
503
518
  }
504
519
 
@@ -521,7 +536,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
521
536
 
522
537
  <>
523
538
  {!isNewCollection && <Tabs value={currentView}
524
- className={cn(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
539
+ className={cls(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
525
540
  onValueChange={(v) => setCurrentView(v as EditorView)}>
526
541
  <Tab value={"details"}>
527
542
  Details
@@ -536,7 +551,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
536
551
 
537
552
  <form noValidate
538
553
  onSubmit={formController.handleSubmit}
539
- className={cn(
554
+ className={cls(
540
555
  isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
541
556
  "flex-grow flex flex-col relative")}>
542
557
 
@@ -551,9 +566,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
551
566
  {currentView === "welcome" &&
552
567
  <CollectionEditorWelcomeView
553
568
  path={path}
554
- onContinue={(importData) => {
569
+ onContinue={(importData, propertiesOrder) => {
570
+ // console.log("Import data", importData, propertiesOrder)
555
571
  if (importData) {
556
- onImportDataSet(importData);
572
+ onImportDataSet(importData, propertiesOrder);
557
573
  setCurrentView("import_data_mapping");
558
574
  } else {
559
575
  setCurrentView("details");
@@ -733,7 +749,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
733
749
  }
734
750
 
735
751
  function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
736
- const { properties, ...rest } = collection;
752
+ const {
753
+ properties,
754
+ ...rest
755
+ } = collection;
737
756
  const propertiesResult: PropertiesOrBuilders<any> = {};
738
757
  if (properties) {
739
758
  Object.keys(properties).forEach((key) => {
@@ -741,7 +760,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
741
760
  });
742
761
  }
743
762
 
744
- return { ...rest, properties: propertiesResult };
763
+ return {
764
+ ...rest,
765
+ properties: propertiesResult
766
+ };
745
767
  }
746
768
 
747
769
  function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
@@ -761,7 +783,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
761
783
  Object.keys(internalProperty.properties).forEach((key) => {
762
784
  properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
763
785
  });
764
- internalProperty = { ...internalProperty, properties };
786
+ internalProperty = {
787
+ ...internalProperty,
788
+ properties
789
+ };
765
790
  }
766
791
 
767
792
  }