@firecms/collection_editor 3.0.0-canary.17 → 3.0.0-canary.170

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 (73) hide show
  1. package/LICENSE +114 -21
  2. package/dist/ConfigControllerProvider.d.ts +11 -2
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.es.js +10011 -4759
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +10712 -3
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/collection_editor_controller.d.ts +14 -2
  9. package/dist/types/collection_inference.d.ts +1 -1
  10. package/dist/types/config_permissions.d.ts +2 -2
  11. package/dist/types/persisted_collection.d.ts +1 -1
  12. package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
  13. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  14. package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
  15. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +4 -3
  16. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
  17. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
  18. package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
  19. package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
  20. package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
  21. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  22. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  23. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  24. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  25. package/dist/useCollectionEditorPlugin.d.ts +17 -11
  26. package/dist/utils/collections.d.ts +6 -0
  27. package/package.json +24 -35
  28. package/src/ConfigControllerProvider.tsx +76 -64
  29. package/src/index.ts +1 -0
  30. package/src/types/collection_editor_controller.tsx +14 -4
  31. package/src/types/collection_inference.ts +1 -1
  32. package/src/types/config_permissions.ts +1 -1
  33. package/src/types/persisted_collection.ts +2 -3
  34. package/src/ui/CollectionViewHeaderAction.tsx +10 -5
  35. package/src/ui/EditorCollectionAction.tsx +10 -63
  36. package/src/ui/EditorCollectionActionStart.tsx +88 -0
  37. package/src/ui/HomePageEditorCollectionAction.tsx +19 -13
  38. package/src/ui/NewCollectionButton.tsx +12 -10
  39. package/src/ui/NewCollectionCard.tsx +3 -3
  40. package/src/ui/PropertyAddColumnComponent.tsx +11 -6
  41. package/src/ui/collection_editor/CollectionDetailsForm.tsx +83 -10
  42. package/src/ui/collection_editor/CollectionEditorDialog.tsx +66 -37
  43. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +8 -7
  44. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +39 -36
  45. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +6 -5
  46. package/src/ui/collection_editor/EnumForm.tsx +10 -6
  47. package/src/ui/collection_editor/GetCodeDialog.tsx +55 -25
  48. package/src/ui/collection_editor/LayoutModeSwitch.tsx +53 -0
  49. package/src/ui/collection_editor/PropertyEditView.tsx +257 -80
  50. package/src/ui/collection_editor/PropertyFieldPreview.tsx +7 -10
  51. package/src/ui/collection_editor/PropertyTree.tsx +9 -7
  52. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
  53. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +3 -5
  54. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +26 -9
  55. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +42 -9
  56. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
  57. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
  58. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
  59. package/src/ui/collection_editor/properties/MapPropertyField.tsx +7 -6
  60. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  61. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +2 -0
  62. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  63. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
  64. package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -10
  65. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
  66. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
  67. package/src/ui/collection_editor/templates/pages_template.ts +1 -6
  68. package/src/useCollectionEditorPlugin.tsx +41 -31
  69. package/src/utils/collections.ts +34 -0
  70. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  71. package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
  72. package/src/ui/RootCollectionSuggestions.tsx +0 -63
  73. package/src/ui/collection_editor/PropertySelectItem.tsx +0 -32
@@ -5,7 +5,8 @@ import {
5
5
  AutocompleteItem,
6
6
  BooleanSwitchWithLabel,
7
7
  Chip,
8
- cn,
8
+ CloseIcon,
9
+ cls,
9
10
  Container,
10
11
  DebouncedTextField,
11
12
  Dialog,
@@ -21,6 +22,8 @@ import {
21
22
  } from "@firecms/ui";
22
23
 
23
24
  import { Field, getIn, useFormex } from "@firecms/formex";
25
+ import { useCollectionEditorController } from "../../useCollectionEditorController";
26
+ import { LayoutModeSwitch } from "./LayoutModeSwitch";
24
27
 
25
28
  export function CollectionDetailsForm({
26
29
  isNewCollection,
@@ -51,9 +54,15 @@ export function CollectionDetailsForm({
51
54
  submitCount
52
55
  } = useFormex<EntityCollection>();
53
56
 
57
+ const collectionEditor = useCollectionEditorController();
58
+
54
59
  const [iconDialogOpen, setIconDialogOpen] = useState(false);
55
60
  const [advancedPanelExpanded, setAdvancedPanelExpanded] = useState(false);
56
61
 
62
+ const updateDatabaseId = (databaseId: string) => {
63
+ setFieldValue("databaseId", databaseId ?? undefined);
64
+ }
65
+
57
66
  const updateName = (name: string) => {
58
67
  setFieldValue("name", name);
59
68
 
@@ -80,6 +89,8 @@ export function CollectionDetailsForm({
80
89
  }
81
90
  }, [errors.id]);
82
91
 
92
+ const DatabaseField = collectionEditor.components?.DatabaseField ?? DefaultDatabaseField;
93
+
83
94
  const collectionIcon = <IconForView collectionOrView={values}/>;
84
95
 
85
96
  const groupOptions = groups?.filter((group) => !reservedGroups?.includes(group));
@@ -112,11 +123,15 @@ export function CollectionDetailsForm({
112
123
 
113
124
  <div>
114
125
  <div
115
- className="flex flex-row py-2 pt-3 items-center">
126
+ className="flex flex-row gap-2 py-2 pt-3 items-center">
116
127
  <Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
117
128
  {isNewCollection ? "New collection" : `${values?.name} collection`}
118
129
  </Typography>
119
- <Tooltip title={"Change icon"}>
130
+ <DatabaseField databaseId={values.databaseId}
131
+ onDatabaseIdUpdate={updateDatabaseId}/>
132
+
133
+ <Tooltip title={"Change icon"}
134
+ asChild={true}>
120
135
  <IconButton
121
136
  shape={"square"}
122
137
  onClick={() => setIconDialogOpen(true)}>
@@ -139,14 +154,15 @@ export function CollectionDetailsForm({
139
154
  value={values.name ?? ""}
140
155
  onChange={(e: any) => updateName(e.target.value)}
141
156
  label={"Name"}
157
+ autoFocus={true}
142
158
  required
143
159
  error={showErrors && Boolean(errors.name)}/>
144
160
  <FieldCaption error={touched.name && Boolean(errors.name)}>
145
- {touched.name && Boolean(errors.name) ? errors.name : "Name of in this collection, usually a plural name (e.g. Products)"}
161
+ {touched.name && Boolean(errors.name) ? errors.name : "Name of this collection, usually a plural name (e.g. Products)"}
146
162
  </FieldCaption>
147
163
  </div>
148
164
 
149
- <div className={cn("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
165
+ <div className={cls("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
150
166
  <Field name={"path"}
151
167
  as={DebouncedTextField}
152
168
  label={"Path"}
@@ -190,16 +206,23 @@ export function CollectionDetailsForm({
190
206
  })}
191
207
  </Autocomplete>
192
208
  <FieldCaption>
193
- {showErrors && Boolean(errors.group) ? errors.group : "Group of the collection"}
209
+ {showErrors && Boolean(errors.group) ? errors.group : "Group in the home page"}
194
210
  </FieldCaption>
211
+
212
+
195
213
  </div>}
196
214
 
197
- <div className={"col-span-12"}>
215
+ <LayoutModeSwitch
216
+ className={"col-span-12"}
217
+ value={values.openEntityMode ?? "side_panel"}
218
+ onChange={(value) => setFieldValue("openEntityMode", value)}/>
219
+
220
+ <div className={"col-span-12 mt-8"}>
198
221
  <ExpandablePanel
199
222
  expanded={advancedPanelExpanded}
200
223
  onExpandedChange={setAdvancedPanelExpanded}
201
224
  title={
202
- <div className="flex flex-row text-gray-500">
225
+ <div className="flex flex-row text-surface-500">
203
226
  <SettingsIcon/>
204
227
  <Typography variant={"subtitle2"}
205
228
  className="ml-2">
@@ -216,7 +239,7 @@ export function CollectionDetailsForm({
216
239
  label={"Collection id"}
217
240
  error={showErrors && Boolean(errors.id)}/>
218
241
  <FieldCaption error={touched.id && Boolean(errors.id)}>
219
- {touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection"}
242
+ {touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection. Typically the same as the path."}
220
243
  </FieldCaption>
221
244
  </div>
222
245
 
@@ -235,6 +258,35 @@ export function CollectionDetailsForm({
235
258
  {showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
236
259
  </FieldCaption>
237
260
  </div>
261
+ <div className={"col-span-12"}>
262
+ <TextField
263
+ error={showErrors && Boolean(errors.sideDialogWidth)}
264
+ name={"sideDialogWidth"}
265
+ type={"number"}
266
+ aria-describedby={"sideDialogWidth-helper"}
267
+ onChange={(e) => {
268
+ setFieldTouched("sideDialogWidth", true);
269
+ const value = e.target.value;
270
+ if (!value) {
271
+ setFieldValue("sideDialogWidth", null);
272
+ } else if (!isNaN(Number(value))) {
273
+ setFieldValue("sideDialogWidth", Number(value));
274
+ }
275
+ }}
276
+ endAdornment={<IconButton
277
+ size={"small"}
278
+ onClick={() => {
279
+ setFieldValue("sideDialogWidth", null);
280
+ }}
281
+ disabled={!values.sideDialogWidth}>
282
+ <CloseIcon size={"small"}/>
283
+ </IconButton>}
284
+ value={values.sideDialogWidth ?? ""}
285
+ label={"Side dialog width"}/>
286
+ <FieldCaption error={showErrors && Boolean(errors.singularName)}>
287
+ {showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define the width (in pixels) of entities side dialog. Default is 768px"}
288
+ </FieldCaption>
289
+ </div>
238
290
  <div className={"col-span-12"}>
239
291
  <TextField
240
292
  error={showErrors && Boolean(errors.description)}
@@ -254,6 +306,8 @@ export function CollectionDetailsForm({
254
306
  <div className={"col-span-12"}>
255
307
  <Select
256
308
  name="defaultSize"
309
+ size={"large"}
310
+ fullWidth={true}
257
311
  label="Default row size"
258
312
  position={"item-aligned"}
259
313
  onChange={handleChange}
@@ -272,8 +326,10 @@ export function CollectionDetailsForm({
272
326
  <div className={"col-span-12"}>
273
327
  <Select
274
328
  name="customId"
275
- label="Data IDs generation"
329
+ label="Document IDs generation"
276
330
  position={"item-aligned"}
331
+ size={"large"}
332
+ fullWidth={true}
277
333
  disabled={customIdValue === "code_defined"}
278
334
  onValueChange={(v) => {
279
335
  if (v === "code_defined")
@@ -363,3 +419,20 @@ export function CollectionDetailsForm({
363
419
  </div>
364
420
  );
365
421
  }
422
+
423
+ function DefaultDatabaseField({
424
+ databaseId,
425
+ onDatabaseIdUpdate
426
+ }: { databaseId?: string, onDatabaseIdUpdate: (databaseId: string) => void }) {
427
+
428
+ return <Tooltip title={"Database ID"}
429
+ side={"top"}
430
+ align={"start"}>
431
+ <TextField size={"small"}
432
+ invisible={true}
433
+ inputClassName={"text-end"}
434
+ value={databaseId ?? ""}
435
+ onChange={(e: any) => onDatabaseIdUpdate(e.target.value)}
436
+ placeholder={"(default)"}></TextField>
437
+ </Tooltip>
438
+ }
@@ -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,13 +26,14 @@ import {
25
26
  import {
26
27
  ArrowBackIcon,
27
28
  Button,
28
- cn,
29
+ CheckIcon,
30
+ cls,
29
31
  coolIconKeys,
30
32
  defaultBorderMixin,
31
33
  Dialog,
32
34
  DialogActions,
33
35
  DialogContent,
34
- DoneIcon,
36
+ DialogTitle,
35
37
  IconButton,
36
38
  LoadingButton,
37
39
  Tab,
@@ -76,9 +78,10 @@ export interface CollectionEditorDialogProps {
76
78
  icon: React.ReactNode
77
79
  };
78
80
  pathSuggestions?: (path?: string) => Promise<string[]>;
79
- getUser: (uid: string) => User | null;
81
+ getUser?: (uid: string) => User | null;
80
82
  getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
81
83
  parentCollection?: PersistedCollection;
84
+ existingEntities?: Entity<any>[];
82
85
  }
83
86
 
84
87
  export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
@@ -88,13 +91,13 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
88
91
  const [formDirty, setFormDirty] = React.useState<boolean>(false);
89
92
  const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
90
93
 
91
- const handleCancel = useCallback(() => {
94
+ const handleCancel = () => {
92
95
  if (!formDirty) {
93
96
  props.handleClose(undefined);
94
97
  } else {
95
98
  setUnsavedChangesDialogOpen(true);
96
99
  }
97
- }, [formDirty, props.handleClose]);
100
+ };
98
101
 
99
102
  useEffect(() => {
100
103
  if (!open) {
@@ -112,6 +115,7 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
112
115
  maxWidth={"7xl"}
113
116
  onOpenChange={(open) => !open ? handleCancel() : undefined}
114
117
  >
118
+ <DialogTitle hidden>Collection editor</DialogTitle>
115
119
  {open && <CollectionEditor {...props}
116
120
  handleCancel={handleCancel}
117
121
  setFormDirty={setFormDirty}/>}
@@ -136,7 +140,7 @@ type EditorView = "welcome"
136
140
  | "extra_view"
137
141
  | "subcollections";
138
142
 
139
- export function CollectionEditor<M extends Record<string, any>>(props: CollectionEditorDialogProps & {
143
+ export function CollectionEditor(props: CollectionEditorDialogProps & {
140
144
  handleCancel: () => void,
141
145
  setFormDirty: (dirty: boolean) => void
142
146
  }) {
@@ -154,14 +158,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
154
158
  const collectionsInThisLevel = (props.parentCollection ? props.parentCollection.subcollections : collections) ?? [];
155
159
  const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
156
160
  const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
157
- const [collection, setCollection] = React.useState<PersistedCollection<M> | undefined>();
161
+ const [collection, setCollection] = React.useState<PersistedCollection<any> | undefined>();
158
162
  const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
159
163
 
160
164
  useEffect(() => {
161
165
  try {
162
166
  if (navigation.initialised) {
163
167
  if (props.editedCollectionId) {
164
- setCollection(navigation.getCollectionFromPaths<PersistedCollection<M>>([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
168
+ setCollection(navigation.getCollectionFromPaths([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
165
169
  } else {
166
170
  setCollection(undefined);
167
171
  }
@@ -170,7 +174,8 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
170
174
  } catch (e) {
171
175
  console.error(e);
172
176
  }
173
- }, [navigation.getCollectionFromPaths, props.editedCollectionId, props.parentCollectionIds, navigation.initialised]);
177
+ }, [props.editedCollectionId, props.parentCollectionIds, navigation.initialised, navigation.getCollectionFromPaths]);
178
+
174
179
  if (!topLevelNavigation) {
175
180
  throw Error("Internal: Navigation not ready in collection editor");
176
181
  }
@@ -186,14 +191,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
186
191
  }
187
192
  : undefined;
188
193
 
189
- const initialValues: PersistedCollection<M> = initialCollection
194
+ const initialValues: PersistedCollection<any> = initialCollection
190
195
  ? applyPropertyConfigs(initialCollection, propertyConfigs)
191
196
  : {
192
197
  id: initialValuesProp?.path ?? randomString(16),
193
198
  path: initialValuesProp?.path ?? "",
194
199
  name: initialValuesProp?.name ?? "",
195
200
  group: initialValuesProp?.group ?? "",
196
- properties: {} as PropertiesOrBuilders<M>,
201
+ properties: {} as PropertiesOrBuilders,
197
202
  propertiesOrder: [],
198
203
  icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
199
204
  ownerId: authController.user?.uid ?? ""
@@ -243,7 +248,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
243
248
  setCollection,
244
249
  initialValues,
245
250
  propertyConfigs,
246
- groups
251
+ groups,
252
+ existingEntities
247
253
  }: CollectionEditorDialogProps & {
248
254
  handleCancel: () => void,
249
255
  setFormDirty: (dirty: boolean) => void,
@@ -272,6 +278,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
272
278
 
273
279
  const saveCollection = (updatedCollection: PersistedCollection<M>): Promise<boolean> => {
274
280
  const id = updatedCollection.id || updatedCollection.path;
281
+
275
282
  return configController.saveCollection({
276
283
  id,
277
284
  collectionData: updatedCollection,
@@ -293,7 +300,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
293
300
  });
294
301
  };
295
302
 
296
- const setNextMode = useCallback(() => {
303
+ const setNextMode = () => {
297
304
  if (currentView === "details") {
298
305
  if (importConfig.inUse) {
299
306
  setCurrentView("import_data_saving");
@@ -314,14 +321,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
314
321
  setCurrentView("details");
315
322
  }
316
323
 
317
- }, [currentView, importConfig.inUse, extraView]);
324
+ };
318
325
 
319
- const doCollectionInference = useCallback((collection: PersistedCollection<any>) => {
326
+ const doCollectionInference = (collection: PersistedCollection<any>) => {
320
327
  if (!collectionInference) return undefined;
321
- return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentCollectionIds ?? []);
322
- }, [collectionInference, parentCollectionIds]);
328
+ return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentPaths ?? []);
329
+ };
323
330
 
324
- const inferCollectionFromData = useCallback(async (newCollection: PersistedCollection<M>) => {
331
+ const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
325
332
 
326
333
  try {
327
334
  if (!doCollectionInference) {
@@ -365,15 +372,15 @@ function CollectionEditorInternal<M extends Record<string, any>>({
365
372
  });
366
373
  return newCollection;
367
374
  }
368
- }, [parentCollectionIds, doCollectionInference]);
375
+ };
369
376
 
370
377
  const onSubmit = (newCollectionState: PersistedCollection<M>, formexController: FormexController<PersistedCollection<M>>) => {
371
- console.log("Submitting collection", newCollectionState);
378
+ console.debug("Submitting collection", newCollectionState);
372
379
  try {
373
380
 
374
381
  if (!isNewCollection) {
375
382
  saveCollection(newCollectionState).then(() => {
376
- formexController.resetForm({ values: initialValues });
383
+ formexController.resetForm();
377
384
  handleClose(newCollectionState);
378
385
  });
379
386
  return;
@@ -462,7 +469,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
462
469
  const formController = useCreateFormex<PersistedCollection<M>>({
463
470
  initialValues,
464
471
  onSubmit,
465
- validation
472
+ validation,
473
+ debugId: "COLLECTION_EDITOR"
466
474
  });
467
475
 
468
476
  const {
@@ -479,26 +487,37 @@ function CollectionEditorInternal<M extends Record<string, any>>({
479
487
  const pathError = validatePath(path, isNewCollection, existingPaths, values.id);
480
488
 
481
489
  const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
482
- const resolvedPath = !pathError ? navigation.resolveAliasesFrom(updatedFullPath) : undefined;
483
- const getDataWithPath = resolvedPath && getData ? () => getData(resolvedPath, parentPaths ?? []) : undefined;
490
+ const resolvedPath = !pathError ? navigation.resolveIdsFrom(updatedFullPath) : undefined;
491
+ const getDataWithPath = resolvedPath && getData ? async () => {
492
+ const data = await getData(resolvedPath, parentPaths ?? []);
493
+ if (existingEntities) {
494
+ const existingData = existingEntities.map(e => e.values);
495
+ data.push(...existingData);
496
+ }
497
+ return data;
498
+ } : undefined;
484
499
 
485
500
  useEffect(() => {
486
501
  setFormDirty(dirty);
487
502
  }, [dirty]);
488
503
 
489
- function onImportDataSet(data: object[]) {
504
+ function onImportDataSet(data: object[], propertiesOrder?: string[]) {
490
505
  importConfig.setInUse(true);
491
506
  buildEntityPropertiesFromData(data, getInferenceType)
492
507
  .then((properties) => {
493
508
  const res = cleanPropertiesFromImport(properties);
494
509
 
495
- setFieldValue("properties", res.properties);
496
- setFieldValue("propertiesOrder", Object.keys(res.properties));
497
-
498
510
  importConfig.setIdColumn(res.idColumn);
499
511
  importConfig.setImportData(data);
500
512
  importConfig.setHeadersMapping(res.headersMapping);
513
+ const filteredHeadingsOrder = ((propertiesOrder ?? [])
514
+ .filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
515
+ importConfig.setHeadingsOrder(filteredHeadingsOrder);
501
516
  importConfig.setOriginProperties(res.properties);
517
+
518
+ const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
519
+ setFieldValue("properties", res.properties);
520
+ setFieldValue("propertiesOrder", mappedHeadings);
502
521
  });
503
522
  }
504
523
 
@@ -521,7 +540,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
521
540
 
522
541
  <>
523
542
  {!isNewCollection && <Tabs value={currentView}
524
- className={cn(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
543
+ innerClassName={cls(defaultBorderMixin, "px-4 h-14 w-full justify-end bg-surface-50 dark:bg-surface-950 border-b")}
525
544
  onValueChange={(v) => setCurrentView(v as EditorView)}>
526
545
  <Tab value={"details"}>
527
546
  Details
@@ -536,7 +555,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
536
555
 
537
556
  <form noValidate
538
557
  onSubmit={formController.handleSubmit}
539
- className={cn(
558
+ className={cls(
540
559
  isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
541
560
  "flex-grow flex flex-col relative")}>
542
561
 
@@ -551,9 +570,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
551
570
  {currentView === "welcome" &&
552
571
  <CollectionEditorWelcomeView
553
572
  path={path}
554
- onContinue={(importData) => {
573
+ onContinue={(importData, propertiesOrder) => {
574
+ // console.log("Import data", importData, propertiesOrder)
555
575
  if (importData) {
556
- onImportDataSet(importData);
576
+ onImportDataSet(importData, propertiesOrder);
557
577
  setCurrentView("import_data_mapping");
558
578
  } else {
559
579
  setCurrentView("details");
@@ -706,7 +726,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
706
726
  loading={isSubmitting}
707
727
  disabled={isSubmitting || (currentView === "details" && !validValues)}
708
728
  startIcon={currentView === "properties"
709
- ? <DoneIcon/>
729
+ ? <CheckIcon/>
710
730
  : undefined}
711
731
  >
712
732
  {currentView === "details" && "Next"}
@@ -733,7 +753,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
733
753
  }
734
754
 
735
755
  function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
736
- const { properties, ...rest } = collection;
756
+ const {
757
+ properties,
758
+ ...rest
759
+ } = collection;
737
760
  const propertiesResult: PropertiesOrBuilders<any> = {};
738
761
  if (properties) {
739
762
  Object.keys(properties).forEach((key) => {
@@ -741,7 +764,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
741
764
  });
742
765
  }
743
766
 
744
- return { ...rest, properties: propertiesResult };
767
+ return {
768
+ ...rest,
769
+ properties: propertiesResult
770
+ };
745
771
  }
746
772
 
747
773
  function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
@@ -761,7 +787,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
761
787
  Object.keys(internalProperty.properties).forEach((key) => {
762
788
  properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
763
789
  });
764
- internalProperty = { ...internalProperty, properties };
790
+ internalProperty = {
791
+ ...internalProperty,
792
+ properties
793
+ };
765
794
  }
766
795
 
767
796
  }
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
  import { EntityCollection, unslugify, } from "@firecms/core";
3
- import { Button, Card, Chip, CircularProgress, cn, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
3
+ import { Button, Card, Chip, CircularProgress, cls, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
4
4
 
5
5
  import { productsCollectionTemplate } from "./templates/products_template";
6
6
  import { blogCollectionTemplate } from "./templates/blog_template";
@@ -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
 
@@ -185,14 +185,15 @@ export function TemplateButton({
185
185
  }) {
186
186
 
187
187
  return (
188
- <Tooltip title={subtitle}>
188
+ <Tooltip title={subtitle}
189
+ asChild={true}>
189
190
  <Card
190
191
  onClick={onClick}
191
- className={cn(
192
+ className={cls(
192
193
  "my-2 rounded-md border mx-0 p-6 px-4 focus:outline-none transition ease-in-out duration-150 flex flex-row gap-4 items-center",
193
- "text-gray-700 dark:text-slate-300",
194
+ "text-surface-700 dark:text-surface-accent-300",
194
195
  "hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
195
- "border-gray-400 dark:border-gray-600 "
196
+ "border-surface-400 dark:border-surface-600 "
196
197
  )}
197
198
  >
198
199
  {icon}