@firecms/collection_editor 3.0.0-alpha.16 → 3.0.0-alpha.18

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 (90) hide show
  1. package/dist/ConfigControllerProvider.d.ts +1 -1
  2. package/dist/components/RootCollectionSuggestions.d.ts +1 -0
  3. package/dist/components/collection_editor/CollectionEditorDialog.d.ts +7 -3
  4. package/dist/components/collection_editor/CollectionPropertiesEditorForm.d.ts +5 -3
  5. package/dist/components/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  6. package/dist/components/collection_editor/PropertyEditView.d.ts +5 -4
  7. package/dist/components/collection_editor/PropertyFieldPreview.d.ts +4 -3
  8. package/dist/components/collection_editor/PropertySelectItem.d.ts +2 -2
  9. package/dist/components/collection_editor/PropertyTree.d.ts +6 -5
  10. package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +3 -1
  11. package/dist/components/collection_editor/properties/BlockPropertyField.d.ts +3 -1
  12. package/dist/components/collection_editor/properties/MapPropertyField.d.ts +3 -1
  13. package/dist/components/collection_editor/properties/RepeatPropertyField.d.ts +3 -1
  14. package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +2 -3
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.es.js +2221 -1889
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.umd.js +1 -1
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/types/collection_editor_controller.d.ts +6 -1
  21. package/dist/types/config_controller.d.ts +2 -2
  22. package/dist/types/persisted_collection.d.ts +3 -2
  23. package/dist/utils/entities.d.ts +3 -4
  24. package/dist/utils/icons.d.ts +1 -2
  25. package/dist/utils/join_collections.d.ts +14 -0
  26. package/package.json +10 -5
  27. package/src/ConfigControllerProvider.tsx +177 -0
  28. package/src/components/EditorCollectionAction.tsx +95 -0
  29. package/src/components/HomePageEditorCollectionAction.tsx +81 -0
  30. package/src/components/NewCollectionCard.tsx +45 -0
  31. package/src/components/RootCollectionSuggestions.tsx +53 -0
  32. package/src/components/collection_editor/CollectionDetailsForm.tsx +312 -0
  33. package/src/components/collection_editor/CollectionEditorDialog.tsx +640 -0
  34. package/src/components/collection_editor/CollectionEditorWelcomeView.tsx +212 -0
  35. package/src/components/collection_editor/CollectionPropertiesEditorForm.tsx +450 -0
  36. package/src/components/collection_editor/CollectionYupValidation.tsx +6 -0
  37. package/src/components/collection_editor/EntityCustomViewsSelectDialog.tsx +29 -0
  38. package/src/components/collection_editor/EnumForm.tsx +354 -0
  39. package/src/components/collection_editor/PropertyEditView.tsx +535 -0
  40. package/src/components/collection_editor/PropertyFieldPreview.tsx +205 -0
  41. package/src/components/collection_editor/PropertySelectItem.tsx +31 -0
  42. package/src/components/collection_editor/PropertyTree.tsx +228 -0
  43. package/src/components/collection_editor/SelectIcons.tsx +72 -0
  44. package/src/components/collection_editor/SubcollectionsEditTab.tsx +239 -0
  45. package/src/components/collection_editor/UnsavedChangesDialog.tsx +47 -0
  46. package/src/components/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
  47. package/src/components/collection_editor/import/CollectionEditorImportMapping.tsx +236 -0
  48. package/src/components/collection_editor/import/clean_import_data.ts +53 -0
  49. package/src/components/collection_editor/properties/BlockPropertyField.tsx +131 -0
  50. package/src/components/collection_editor/properties/BooleanPropertyField.tsx +36 -0
  51. package/src/components/collection_editor/properties/CommonPropertyFields.tsx +112 -0
  52. package/src/components/collection_editor/properties/DateTimePropertyField.tsx +86 -0
  53. package/src/components/collection_editor/properties/EnumPropertyField.tsx +116 -0
  54. package/src/components/collection_editor/properties/FieldHelperView.tsx +13 -0
  55. package/src/components/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  56. package/src/components/collection_editor/properties/MapPropertyField.tsx +154 -0
  57. package/src/components/collection_editor/properties/NumberPropertyField.tsx +38 -0
  58. package/src/components/collection_editor/properties/ReferencePropertyField.tsx +184 -0
  59. package/src/components/collection_editor/properties/RepeatPropertyField.tsx +115 -0
  60. package/src/components/collection_editor/properties/StoragePropertyField.tsx +194 -0
  61. package/src/components/collection_editor/properties/StringPropertyField.tsx +85 -0
  62. package/src/components/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
  63. package/src/components/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  64. package/src/components/collection_editor/properties/validation/GeneralPropertyValidation.tsx +49 -0
  65. package/src/components/collection_editor/properties/validation/NumberPropertyValidation.tsx +99 -0
  66. package/src/components/collection_editor/properties/validation/StringPropertyValidation.tsx +131 -0
  67. package/src/components/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  68. package/src/components/collection_editor/templates/blog_template.ts +115 -0
  69. package/src/components/collection_editor/templates/products_template.ts +89 -0
  70. package/src/components/collection_editor/templates/users_template.ts +34 -0
  71. package/src/components/collection_editor/util.ts +21 -0
  72. package/src/components/collection_editor/utils/supported_fields.tsx +28 -0
  73. package/src/components/collection_editor/utils/update_property_for_widget.ts +258 -0
  74. package/src/components/collection_editor/utils/useTraceUpdate.tsx +23 -0
  75. package/src/index.ts +31 -0
  76. package/src/types/collection_editor_controller.tsx +31 -0
  77. package/src/types/collection_inference.ts +3 -0
  78. package/src/types/config_controller.tsx +30 -0
  79. package/src/types/config_permissions.ts +20 -0
  80. package/src/types/persisted_collection.ts +7 -0
  81. package/src/useCollectionEditorController.tsx +9 -0
  82. package/src/useCollectionEditorPlugin.tsx +103 -0
  83. package/src/useCollectionsConfigController.tsx +9 -0
  84. package/src/utils/arrays.ts +3 -0
  85. package/src/utils/entities.ts +38 -0
  86. package/src/utils/icons.ts +17 -0
  87. package/src/utils/join_collections.ts +144 -0
  88. package/src/utils/synonyms.ts +1952 -0
  89. package/src/vite-env.d.ts +1 -0
  90. package/dist/types/editable_properties.d.ts +0 -10
@@ -0,0 +1,640 @@
1
+ import * as React from "react";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import {
4
+ ArrowBackIcon,
5
+ Button,
6
+ CircularProgressCenter,
7
+ CMSType,
8
+ cn,
9
+ coolIconKeys,
10
+ defaultBorderMixin,
11
+ Dialog,
12
+ DialogActions,
13
+ DialogContent,
14
+ DoneIcon,
15
+ EntityCollection,
16
+ ErrorView,
17
+ IconButton,
18
+ LoadingButton,
19
+ Properties,
20
+ removeUndefined,
21
+ Tab,
22
+ Tabs,
23
+ TopNavigationResult,
24
+ useAuthController,
25
+ useFireCMSContext,
26
+ useNavigationContext,
27
+ User,
28
+ useSnackbarController
29
+ } from "@firecms/core";
30
+ import { Form, Formik, FormikHelpers } from "formik";
31
+ import { YupSchema } from "./CollectionYupValidation";
32
+ import { CollectionDetailsForm } from "./CollectionDetailsForm";
33
+ import { CollectionPropertiesEditorForm } from "./CollectionPropertiesEditorForm";
34
+ import { UnsavedChangesDialog } from "./UnsavedChangesDialog";
35
+ import { PersistedCollection } from "../../types/persisted_collection";
36
+ import { SubcollectionsEditTab } from "./SubcollectionsEditTab";
37
+ import { CollectionsConfigController } from "../../types/config_controller";
38
+ import { CollectionEditorWelcomeView } from "./CollectionEditorWelcomeView";
39
+ import { CollectionInference } from "../../types/collection_inference";
40
+ import { getInferenceType, ImportSaveInProgress, useImportConfig } from "@firecms/data_import";
41
+ import { buildEntityPropertiesFromData } from "@firecms/schema_inference";
42
+ import { CollectionEditorImportMapping } from "./import/CollectionEditorImportMapping";
43
+ import { CollectionEditorImportDataPreview } from "./import/CollectionEditorImportDataPreview";
44
+ import { cleanPropertiesFromImport } from "./import/clean_import_data";
45
+
46
+ export interface CollectionEditorDialogProps {
47
+ open: boolean;
48
+ isNewCollection: boolean;
49
+ initialValues?: {
50
+ group?: string,
51
+ path?: string,
52
+ name?: string,
53
+ }
54
+ editedCollectionPath?: string; // last segment of the path, like `locales`
55
+ fullPath?: string; // full path of this particular collection, like `products/123/locales`
56
+ parentPathSegments?: string[]; // path segments of the parent collection, like [`products`]
57
+ handleClose: (collection?: EntityCollection) => void;
58
+ configController: CollectionsConfigController;
59
+ reservedGroups?: string[];
60
+ collectionInference?: CollectionInference;
61
+ extraView?: {
62
+ View: React.ComponentType<{
63
+ path: string
64
+ }>,
65
+ icon: React.ReactNode
66
+ };
67
+ pathSuggestions?: (path?: string) => Promise<string[]>;
68
+ getUser: (uid: string) => User | null;
69
+ getData?: (path: string) => Promise<object[]>;
70
+ parentCollection?: EntityCollection;
71
+ }
72
+
73
+ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
74
+
75
+ const open = props.open;
76
+
77
+ const [formDirty, setFormDirty] = React.useState<boolean>(false);
78
+ const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
79
+
80
+ const handleCancel = useCallback(() => {
81
+ if (!formDirty) {
82
+ props.handleClose(undefined);
83
+ } else {
84
+ setUnsavedChangesDialogOpen(true);
85
+ }
86
+ }, [formDirty, props.handleClose]);
87
+
88
+ useEffect(() => {
89
+ if (!open) {
90
+ setFormDirty(false);
91
+ setUnsavedChangesDialogOpen(false);
92
+ }
93
+ }, [open]);
94
+
95
+ return (
96
+ <Dialog
97
+ open={open}
98
+ fullWidth={true}
99
+ fullHeight={true}
100
+ scrollable={false}
101
+ maxWidth={"7xl"}
102
+ onOpenChange={(open) => !open ? handleCancel() : undefined}
103
+ >
104
+ {open && <CollectionEditorDialogInternal {...props}
105
+ handleCancel={handleCancel}
106
+ setFormDirty={setFormDirty}/>}
107
+
108
+ <UnsavedChangesDialog
109
+ open={unsavedChangesDialogOpen}
110
+ handleOk={() => props.handleClose(undefined)}
111
+ handleCancel={() => setUnsavedChangesDialogOpen(false)}
112
+ body={"There are unsaved changes in this collection"}/>
113
+
114
+ </Dialog>
115
+ );
116
+ }
117
+
118
+ type EditorView = "welcome"
119
+ | "details"
120
+ | "import_data_mapping"
121
+ | "import_data_preview"
122
+ | "import_data_saving"
123
+ | "properties"
124
+ | "loading"
125
+ | "extra_view"
126
+ | "subcollections";
127
+
128
+ export function CollectionEditorDialogInternal<M extends {
129
+ [Key: string]: CMSType
130
+ }>({
131
+ isNewCollection,
132
+ initialValues: initialValuesProp,
133
+ configController,
134
+ editedCollectionPath,
135
+ parentPathSegments,
136
+ fullPath,
137
+ collectionInference,
138
+ handleClose,
139
+ reservedGroups,
140
+ extraView,
141
+ handleCancel,
142
+ setFormDirty,
143
+ pathSuggestions,
144
+ getUser,
145
+ parentCollection,
146
+ getData
147
+ }: CollectionEditorDialogProps & {
148
+ handleCancel: () => void,
149
+ setFormDirty: (dirty: boolean) => void
150
+ }
151
+ ) {
152
+
153
+ const { fields: customFields } = useFireCMSContext();
154
+ const navigation = useNavigationContext();
155
+ const {
156
+ topLevelNavigation,
157
+ collections
158
+ } = navigation;
159
+
160
+ const includeTemplates = !initialValuesProp?.path && (parentPathSegments ?? []).length === 0;
161
+ const collectionsInThisLevel = (parentCollection ? parentCollection.subcollections : collections) ?? [];
162
+ const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
163
+ const existingAliases = collectionsInThisLevel.map(col => col.alias?.trim().toLowerCase()).filter(Boolean) as string[];
164
+
165
+ const importConfig = useImportConfig();
166
+
167
+ if (!topLevelNavigation) {
168
+ throw Error("Internal: Navigation not ready in collection editor");
169
+ }
170
+
171
+ const {
172
+ groups
173
+ }: TopNavigationResult = topLevelNavigation;
174
+
175
+ const snackbarController = useSnackbarController();
176
+ const authController = useAuthController();
177
+
178
+ // Use this ref to store which properties have errors
179
+ const propertyErrorsRef = useRef({});
180
+
181
+ const initialView = isNewCollection ? (includeTemplates ? "welcome" : "details") : "properties";
182
+ const [currentView, setCurrentView] = useState<EditorView>(initialView); // this view can edit either the details view or the properties one
183
+
184
+ const [error, setError] = React.useState<Error | undefined>();
185
+
186
+ const [collection, setCollection] = React.useState<PersistedCollection<M> | undefined>();
187
+ const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
188
+ const [initialError, setInitialError] = React.useState<Error | undefined>();
189
+
190
+ useEffect(() => {
191
+ try {
192
+ if (navigation.initialised) {
193
+ if (editedCollectionPath) {
194
+ setCollection(navigation.getCollectionFromPaths<PersistedCollection<M>>([...(parentPathSegments ?? []), editedCollectionPath]));
195
+ } else {
196
+ setCollection(undefined);
197
+ }
198
+ setInitialLoadingCompleted(true);
199
+ }
200
+ } catch (e) {
201
+ console.error(e);
202
+ setInitialError(initialError);
203
+ }
204
+ }, [navigation.getCollectionFromPaths, editedCollectionPath, initialError, navigation.initialised]);
205
+
206
+ const saveCollection = (updatedCollection: PersistedCollection<M>): Promise<boolean> => {
207
+ const fullPath = updatedCollection.alias || updatedCollection.path;
208
+ return configController.saveCollection({
209
+ path: fullPath,
210
+ collectionData: updatedCollection,
211
+ previousPath: editedCollectionPath,
212
+ parentPathSegments
213
+ })
214
+ .then(() => {
215
+ setError(undefined);
216
+ return true;
217
+ })
218
+ .catch((e) => {
219
+ setError(e);
220
+ console.error(e);
221
+ snackbarController.open({
222
+ type: "error",
223
+ message: "Error persisting collection: " + (e.message ?? "Details in the console")
224
+ });
225
+ return false;
226
+ });
227
+ };
228
+
229
+ const initialValues: PersistedCollection<M> = collection ?? {
230
+ path: initialValuesProp?.path ?? "",
231
+ name: initialValuesProp?.name ?? "",
232
+ group: initialValuesProp?.group ?? "",
233
+ properties: {} as Properties<M>,
234
+ propertiesOrder: [],
235
+ icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
236
+ ownerId: authController.user?.uid ?? ""
237
+ };
238
+
239
+ const setNextMode = useCallback(() => {
240
+ if (currentView === "details") {
241
+ if (importConfig.inUse) {
242
+ setCurrentView("import_data_saving");
243
+ } else if (extraView) {
244
+ setCurrentView("extra_view");
245
+ } else {
246
+ setCurrentView("properties");
247
+ }
248
+ } else if (currentView === "welcome") {
249
+ setCurrentView("details");
250
+ } else if (currentView === "import_data_mapping") {
251
+ setCurrentView("import_data_preview");
252
+ } else if (currentView === "import_data_preview") {
253
+ setCurrentView("details");
254
+ } else if (currentView === "extra_view") {
255
+ setCurrentView("properties");
256
+ } else {
257
+ setCurrentView("details");
258
+ }
259
+
260
+ }, [currentView, importConfig.inUse, extraView]);
261
+
262
+ const doCollectionInference = useCallback((collection: PersistedCollection<any>) => {
263
+ if (!collectionInference) return undefined;
264
+ return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentPathSegments ?? []);
265
+ }, [collectionInference, parentPathSegments]);
266
+
267
+ const inferCollectionFromData = useCallback(async (newCollection: PersistedCollection<M>) => {
268
+
269
+ if (!doCollectionInference) {
270
+ setCollection(newCollection);
271
+ return Promise.resolve(newCollection);
272
+ }
273
+
274
+ setCurrentView("loading");
275
+
276
+ const inferredCollection = await doCollectionInference?.(newCollection);
277
+
278
+ if (!inferredCollection) {
279
+ setCollection(newCollection);
280
+ return Promise.resolve(newCollection);
281
+ }
282
+ const values = {
283
+ ...(newCollection ?? {}),
284
+ };
285
+
286
+ if (Object.keys(inferredCollection.properties ?? {}).length > 0) {
287
+ values.properties = inferredCollection.properties as Properties<M>;
288
+ values.propertiesOrder = inferredCollection.propertiesOrder
289
+ }
290
+
291
+ if (!values.propertiesOrder) {
292
+ values.propertiesOrder = Object.keys(values.properties);
293
+ return values;
294
+ }
295
+
296
+ setCollection(values);
297
+ console.log("Inferred collection", {
298
+ newCollection: newCollection ?? {},
299
+ values
300
+ });
301
+ return values;
302
+ }, [parentPathSegments, doCollectionInference]);
303
+
304
+ const onSubmit = (newCollectionState: PersistedCollection<M>, formikHelpers: FormikHelpers<PersistedCollection<M>>) => {
305
+ try {
306
+
307
+ console.log("Submitting collection", newCollectionState)
308
+ if (!isNewCollection) {
309
+ saveCollection(newCollectionState).then(() => {
310
+ formikHelpers.resetForm({ values: initialValues });
311
+ setNextMode();
312
+ handleClose(newCollectionState);
313
+ });
314
+ return;
315
+ }
316
+
317
+ if (currentView === "welcome") {
318
+ setNextMode();
319
+ formikHelpers.resetForm({ values: newCollectionState });
320
+ } else if (currentView === "details") {
321
+ if (extraView || importConfig.inUse) {
322
+ formikHelpers.resetForm({ values: newCollectionState });
323
+ setNextMode();
324
+ } else if (isNewCollection) {
325
+ inferCollectionFromData(newCollectionState)
326
+ .then((values) => {
327
+ formikHelpers.resetForm({
328
+ values: values ?? newCollectionState,
329
+ touched: {
330
+ path: true,
331
+ name: true
332
+ }
333
+ });
334
+ }).finally(() => {
335
+ setNextMode();
336
+ });
337
+ } else {
338
+ formikHelpers.resetForm({ values: newCollectionState });
339
+ setNextMode();
340
+ }
341
+ } else if (currentView === "extra_view") {
342
+ setNextMode();
343
+ formikHelpers.resetForm({ values: newCollectionState });
344
+ } else if (currentView === "import_data_mapping") {
345
+ setNextMode();
346
+ } else if (currentView === "import_data_preview") {
347
+ setNextMode();
348
+ } else if (currentView === "properties") {
349
+ saveCollection(newCollectionState).then(() => {
350
+ formikHelpers.resetForm({ values: initialValues });
351
+ setNextMode();
352
+ handleClose(newCollectionState);
353
+ });
354
+ } else {
355
+ setNextMode();
356
+ formikHelpers.resetForm({ values: newCollectionState });
357
+ }
358
+ } catch (e: any) {
359
+ snackbarController.open({
360
+ type: "error",
361
+ message: "Error persisting collection: " + (e.message ?? "Details in the console")
362
+ });
363
+ console.error(e);
364
+ formikHelpers.resetForm({ values: newCollectionState });
365
+ }
366
+ };
367
+
368
+ if (!isNewCollection && (!navigation.initialised || !initialLoadingCompleted)) {
369
+ return <CircularProgressCenter/>;
370
+ }
371
+
372
+ return <DialogContent fullHeight={true}>
373
+ <Formik
374
+ initialValues={initialValues}
375
+ validationSchema={(currentView === "properties" || currentView === "subcollections" || currentView === "details") && YupSchema}
376
+ validate={() => {
377
+ if (currentView === "properties") {
378
+ // return the errors for the properties form
379
+ return propertyErrorsRef.current;
380
+ }
381
+ return undefined;
382
+ }}
383
+ onSubmit={onSubmit}
384
+ >
385
+ {({
386
+ values,
387
+ setFieldValue,
388
+ isSubmitting,
389
+ dirty,
390
+ submitCount
391
+ }) => {
392
+
393
+ const path = values.path ?? editedCollectionPath;
394
+ const updatedFullPath = fullPath?.includes("/") ? fullPath?.split("/").slice(0, -1).join("/") + "/" + path : path;
395
+ const getDataWithPath = updatedFullPath && getData ? () => getData(updatedFullPath) : undefined;
396
+
397
+ // eslint-disable-next-line react-hooks/rules-of-hooks
398
+ useEffect(() => {
399
+ setFormDirty(dirty);
400
+ }, [dirty]);
401
+
402
+ function onImportDataSet(data: object[]) {
403
+ importConfig.setInUse(true);
404
+ buildEntityPropertiesFromData(data, getInferenceType)
405
+ .then((properties) => {
406
+ const res = cleanPropertiesFromImport(properties);
407
+
408
+ setFieldValue("properties", res.properties);
409
+ setFieldValue("propertiesOrder", Object.keys(res.properties));
410
+
411
+ importConfig.setIdColumn(res.idColumn);
412
+ importConfig.setImportData(data);
413
+ importConfig.setHeadersMapping(res.headersMapping);
414
+ importConfig.setOriginProperties(res.properties);
415
+ });
416
+ }
417
+
418
+ const validValues = Boolean(values.name) && Boolean(values.path);
419
+
420
+ const onImportMappingComplete = () => {
421
+ const updatedProperties = { ...values.properties };
422
+ if (importConfig.idColumn)
423
+ delete updatedProperties[importConfig.idColumn];
424
+ setFieldValue("properties", updatedProperties);
425
+ // setFieldValue("propertiesOrder", Object.values(importConfig.headersMapping));
426
+ setNextMode();
427
+ console.log("onImportMappingComplete", {
428
+ importConfig,
429
+ properties: updatedProperties,
430
+ })
431
+ };
432
+
433
+ return (
434
+ <>
435
+ {!isNewCollection && <Tabs value={currentView}
436
+ className={cn(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
437
+ onValueChange={(v) => setCurrentView(v as EditorView)}>
438
+ <Tab value={"details"}>
439
+ Details
440
+ </Tab>
441
+ <Tab value={"properties"}>
442
+ Properties
443
+ </Tab>
444
+ <Tab value={"subcollections"}>
445
+ Additional views
446
+ </Tab>
447
+ </Tabs>}
448
+
449
+ <Form noValidate
450
+ className={cn(
451
+ isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
452
+ "flex-grow flex flex-col relative")}>
453
+
454
+ {currentView === "loading" &&
455
+ <CircularProgressCenter/>}
456
+
457
+ {currentView === "extra_view" &&
458
+ path &&
459
+ extraView?.View &&
460
+ <extraView.View path={path}/>}
461
+
462
+ {currentView === "welcome" &&
463
+ <CollectionEditorWelcomeView
464
+ path={path}
465
+ onContinue={(data) => {
466
+ if (data) {
467
+ onImportDataSet(data);
468
+ setCurrentView("import_data_mapping");
469
+ } else {
470
+ setCurrentView("details");
471
+ }
472
+ }}
473
+ collections={collections}
474
+ parentCollection={parentCollection}
475
+ pathSuggestions={pathSuggestions}/>}
476
+
477
+ {currentView === "import_data_mapping" && importConfig &&
478
+ <CollectionEditorImportMapping importConfig={importConfig}
479
+ customFields={customFields}/>}
480
+
481
+ {currentView === "import_data_preview" && importConfig &&
482
+ <CollectionEditorImportDataPreview importConfig={importConfig}
483
+ properties={values.properties as Properties}
484
+ propertiesOrder={values.propertiesOrder as string[]}/>}
485
+
486
+ {currentView === "import_data_saving" && importConfig &&
487
+ <ImportSaveInProgress importConfig={importConfig}
488
+ collection={values}
489
+ onImportSuccess={(importedCollection) => {
490
+ handleClose(importedCollection);
491
+ snackbarController.open({
492
+ type: "info",
493
+ message: "Data imported successfully"
494
+ });
495
+ }}
496
+ />}
497
+
498
+ {currentView === "details" &&
499
+ <CollectionDetailsForm
500
+ existingPaths={existingPaths}
501
+ existingAliases={existingAliases}
502
+ groups={groups}
503
+ parentCollection={parentCollection}
504
+ isNewCollection={isNewCollection}/>}
505
+
506
+ {currentView === "subcollections" && collection &&
507
+ <SubcollectionsEditTab
508
+ parentCollection={parentCollection}
509
+ configController={configController}
510
+ getUser={getUser}
511
+ collectionInference={collectionInference}
512
+ parentPathSegments={parentPathSegments}
513
+ collection={collection}/>}
514
+
515
+ {currentView === "properties" &&
516
+ <CollectionPropertiesEditorForm
517
+ showErrors={submitCount > 0}
518
+ isNewCollection={isNewCollection}
519
+ reservedGroups={reservedGroups}
520
+ onPropertyError={(propertyKey, namespace, error) => {
521
+ propertyErrorsRef.current = removeUndefined({
522
+ ...propertyErrorsRef.current,
523
+ [propertyKey]: error
524
+ }, true);
525
+ }}
526
+ getUser={getUser}
527
+ getData={getDataWithPath}
528
+ doCollectionInference={doCollectionInference}
529
+ customFields={customFields}
530
+ extraIcon={extraView?.icon &&
531
+ <IconButton
532
+ color={"primary"}
533
+ onClick={() => setCurrentView("extra_view")}>
534
+ {extraView.icon}
535
+ </IconButton>}/>
536
+ }
537
+
538
+ {currentView !== "welcome" && <DialogActions
539
+ position={"absolute"}>
540
+ {error && <ErrorView error={error}/>}
541
+
542
+ {isNewCollection && includeTemplates && currentView === "import_data_mapping" &&
543
+ <Button variant={"text"}
544
+ type="button"
545
+ onClick={() => {
546
+ importConfig.setInUse(false);
547
+ return setCurrentView("welcome");
548
+ }}>
549
+ <ArrowBackIcon/>
550
+ Back
551
+ </Button>}
552
+
553
+ {isNewCollection && includeTemplates && currentView === "import_data_preview" &&
554
+ <Button variant={"text"}
555
+ type="button"
556
+ onClick={() => {
557
+ saveCollection(values);
558
+ setCurrentView("import_data_mapping");
559
+ }}>
560
+ <ArrowBackIcon/>
561
+ Back
562
+ </Button>}
563
+
564
+ {isNewCollection && includeTemplates && currentView === "details" &&
565
+ <Button variant={"text"}
566
+ type="button"
567
+ onClick={() => setCurrentView("welcome")}>
568
+ <ArrowBackIcon/>
569
+ Back
570
+ </Button>}
571
+
572
+ {isNewCollection && currentView === "properties" && <Button variant={"text"}
573
+ type="button"
574
+ onClick={() => setCurrentView("details")}>
575
+ <ArrowBackIcon/>
576
+ Back
577
+ </Button>}
578
+
579
+ <Button variant={"text"}
580
+ onClick={() => {
581
+ handleCancel();
582
+ }}>
583
+ Cancel
584
+ </Button>
585
+
586
+ {isNewCollection && currentView === "import_data_mapping" &&
587
+ <Button
588
+ variant={"filled"}
589
+ color="primary"
590
+ onClick={onImportMappingComplete}
591
+ >
592
+ Next
593
+ </Button>}
594
+
595
+ {isNewCollection && currentView === "import_data_preview" &&
596
+ <Button
597
+ variant={"filled"}
598
+ color="primary"
599
+ onClick={() => {
600
+ setNextMode();
601
+ }}
602
+ >
603
+ Next
604
+ </Button>}
605
+
606
+ {isNewCollection && (currentView === "details" || currentView === "properties") &&
607
+ <LoadingButton
608
+ variant={"filled"}
609
+ color="primary"
610
+ type="submit"
611
+ loading={isSubmitting}
612
+ disabled={isSubmitting || (currentView === "details" && !validValues)}
613
+ startIcon={currentView === "properties"
614
+ ? <DoneIcon/>
615
+ : undefined}
616
+ >
617
+ {currentView === "details" && "Next"}
618
+ {currentView === "properties" && "Create collection"}
619
+ </LoadingButton>}
620
+
621
+ {!isNewCollection && <LoadingButton
622
+ variant="filled"
623
+ color="primary"
624
+ type="submit"
625
+ loading={isSubmitting}
626
+ // disabled={isSubmitting || !dirty}
627
+ >
628
+ Update collection
629
+ </LoadingButton>}
630
+
631
+ </DialogActions>}
632
+ </Form>
633
+ </>
634
+ );
635
+ }}
636
+
637
+ </Formik>
638
+ </DialogContent>
639
+
640
+ }