@firecms/data_import_export 3.0.0-canary.5 → 3.0.0-canary.51
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.
- package/LICENSE +113 -21
- package/README.md +1 -1
- package/dist/components/DataNewPropertiesMapping.d.ts +3 -5
- package/dist/components/ImportFileUpload.d.ts +1 -1
- package/dist/export_import/BasicExportAction.d.ts +7 -0
- package/dist/export_import/ExportCollectionAction.d.ts +2 -1
- package/dist/export_import/ImportCollectionAction.d.ts +3 -1
- package/dist/export_import/export.d.ts +4 -4
- package/dist/export_import/index.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +901 -546
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/types/column_mapping.d.ts +5 -7
- package/dist/useImportExportPlugin.d.ts +2 -1
- package/dist/utils/data.d.ts +3 -10
- package/dist/utils/file_headers.d.ts +1 -0
- package/dist/utils/file_to_json.d.ts +6 -1
- package/dist/utils/get_properties_mapping.d.ts +0 -3
- package/dist/utils/index.d.ts +0 -1
- package/package.json +20 -18
- package/src/components/DataNewPropertiesMapping.tsx +155 -41
- package/src/components/ImportFileUpload.tsx +12 -4
- package/src/components/ImportNewPropertyFieldPreview.tsx +7 -2
- package/src/components/ImportSaveInProgress.tsx +24 -2
- package/src/export_import/BasicExportAction.tsx +137 -0
- package/src/export_import/ExportCollectionAction.tsx +15 -7
- package/src/export_import/ImportCollectionAction.tsx +52 -31
- package/src/export_import/export.ts +50 -30
- package/src/export_import/index.ts +4 -0
- package/src/hooks/useImportConfig.tsx +6 -0
- package/src/index.ts +1 -0
- package/src/types/column_mapping.ts +6 -6
- package/src/useImportExportPlugin.tsx +4 -3
- package/src/utils/data.ts +50 -127
- package/src/utils/file_headers.ts +90 -0
- package/src/utils/file_to_json.ts +33 -15
- package/src/utils/get_properties_mapping.ts +63 -59
- package/src/utils/index.ts +0 -1
@@ -28,7 +28,7 @@ import {
|
|
28
28
|
Tooltip,
|
29
29
|
Typography,
|
30
30
|
} from "@firecms/ui";
|
31
|
-
import {
|
31
|
+
import { downloadEntitiesExport } from "./export";
|
32
32
|
|
33
33
|
const DOCS_LIMIT = 500;
|
34
34
|
|
@@ -36,11 +36,13 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
36
36
|
collection: inputCollection,
|
37
37
|
path: inputPath,
|
38
38
|
collectionEntitiesCount,
|
39
|
+
onAnalyticsEvent,
|
39
40
|
exportAllowed,
|
40
41
|
notAllowedView
|
41
42
|
}: CollectionActionsProps<M, UserType, EntityCollection<M, any>> & {
|
42
43
|
exportAllowed?: (props: { collectionEntitiesCount: number, path: string, collection: EntityCollection }) => boolean;
|
43
44
|
notAllowedView?: React.ReactNode;
|
45
|
+
onAnalyticsEvent?: (event: string, params?: any) => void;
|
44
46
|
}) {
|
45
47
|
|
46
48
|
const customizationController = useCustomizationController();
|
@@ -122,6 +124,9 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
122
124
|
const doDownload = useCallback(async (collection: ResolvedEntityCollection<M>,
|
123
125
|
exportConfig: ExportConfig<any> | undefined) => {
|
124
126
|
|
127
|
+
onAnalyticsEvent?.("export_collection", {
|
128
|
+
collection: collection.path
|
129
|
+
});
|
125
130
|
setDataLoading(true);
|
126
131
|
dataSource.fetchCollection<M>({
|
127
132
|
path,
|
@@ -134,7 +139,10 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
134
139
|
...exportConfig?.additionalFields?.map(column => column.key) ?? [],
|
135
140
|
...collection.additionalFields?.map(field => field.key) ?? []
|
136
141
|
];
|
137
|
-
|
142
|
+
downloadEntitiesExport(data, additionalData, collection.properties, collection.propertiesOrder, collection.name, flattenArrays, additionalHeaders, exportType, dateExportType);
|
143
|
+
onAnalyticsEvent?.("export_collection_success", {
|
144
|
+
collection: collection.path
|
145
|
+
});
|
138
146
|
})
|
139
147
|
.catch((e) => {
|
140
148
|
console.error("Error loading export data", e);
|
@@ -142,7 +150,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
142
150
|
})
|
143
151
|
.finally(() => setDataLoading(false));
|
144
152
|
|
145
|
-
}, [dataSource, path, fetchAdditionalFields, flattenArrays, exportType, dateExportType]);
|
153
|
+
}, [onAnalyticsEvent, dataSource, path, fetchAdditionalFields, flattenArrays, exportType, dateExportType]);
|
146
154
|
|
147
155
|
const onOkClicked = useCallback(() => {
|
148
156
|
doDownload(collection, exportConfig);
|
@@ -183,7 +191,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
183
191
|
onChange={() => setExportType("csv")}
|
184
192
|
className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
185
193
|
<label htmlFor="radio-csv"
|
186
|
-
className="p-2 text-sm font-medium text-gray-900 dark:text-
|
194
|
+
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">CSV</label>
|
187
195
|
</div>
|
188
196
|
<div className="flex items-center">
|
189
197
|
<input id="radio-json" type="radio" value="json" name="exportType"
|
@@ -191,7 +199,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
191
199
|
onChange={() => setExportType("json")}
|
192
200
|
className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
193
201
|
<label htmlFor="radio-json"
|
194
|
-
className="p-2 text-sm font-medium text-gray-900 dark:text-
|
202
|
+
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">JSON</label>
|
195
203
|
</div>
|
196
204
|
</div>
|
197
205
|
|
@@ -202,7 +210,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
202
210
|
onChange={() => setDateExportType("timestamp")}
|
203
211
|
className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
204
212
|
<label htmlFor="radio-timestamp"
|
205
|
-
className="p-2 text-sm font-medium text-gray-900 dark:text-
|
213
|
+
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">Dates as
|
206
214
|
timestamps ({dateRef.current.getTime()})</label>
|
207
215
|
</div>
|
208
216
|
<div className="flex items-center">
|
@@ -211,7 +219,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
211
219
|
onChange={() => setDateExportType("string")}
|
212
220
|
className={cn(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
213
221
|
<label htmlFor="radio-string"
|
214
|
-
className="p-2 text-sm font-medium text-gray-900 dark:text-
|
222
|
+
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">Dates as
|
215
223
|
strings ({dateRef.current.toISOString()})</label>
|
216
224
|
</div>
|
217
225
|
</div>
|
@@ -2,13 +2,15 @@ import React, { useCallback, useEffect } from "react";
|
|
2
2
|
import {
|
3
3
|
CollectionActionsProps,
|
4
4
|
EntityCollectionTable,
|
5
|
-
PropertyConfigBadge,
|
6
5
|
getFieldConfig,
|
7
6
|
getPropertiesWithPropertiesOrder,
|
8
7
|
getPropertyInPath,
|
8
|
+
PropertiesOrBuilders,
|
9
9
|
Property,
|
10
|
+
PropertyConfigBadge,
|
10
11
|
resolveCollection,
|
11
12
|
ResolvedProperties,
|
13
|
+
slugify,
|
12
14
|
useCustomizationController,
|
13
15
|
User,
|
14
16
|
useSelectionController,
|
@@ -30,7 +32,7 @@ import {
|
|
30
32
|
} from "@firecms/ui";
|
31
33
|
import { buildEntityPropertiesFromData } from "@firecms/schema_inference";
|
32
34
|
import { useImportConfig } from "../hooks";
|
33
|
-
import { convertDataToEntity, getInferenceType
|
35
|
+
import { convertDataToEntity, getInferenceType } from "../utils";
|
34
36
|
import { DataNewPropertiesMapping, ImportFileUpload, ImportSaveInProgress } from "../components";
|
35
37
|
import { ImportConfig } from "../types";
|
36
38
|
|
@@ -40,8 +42,12 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
40
42
|
collection,
|
41
43
|
path,
|
42
44
|
collectionEntitiesCount,
|
43
|
-
|
45
|
+
onAnalyticsEvent
|
46
|
+
}: CollectionActionsProps<M, UserType> & {
|
47
|
+
onAnalyticsEvent?: (event: string, params?: any) => void;
|
48
|
+
}
|
44
49
|
) {
|
50
|
+
|
45
51
|
const customizationController = useCustomizationController();
|
46
52
|
|
47
53
|
const snackbarController = useSnackbarController();
|
@@ -54,20 +60,23 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
54
60
|
|
55
61
|
const handleClickOpen = useCallback(() => {
|
56
62
|
setOpen(true);
|
63
|
+
onAnalyticsEvent?.("import_open");
|
57
64
|
setStep("initial");
|
58
|
-
}, [
|
65
|
+
}, [onAnalyticsEvent]);
|
59
66
|
|
60
67
|
const handleClose = useCallback(() => {
|
61
68
|
setOpen(false);
|
62
69
|
}, [setOpen]);
|
63
70
|
|
64
71
|
const onMappingComplete = useCallback(() => {
|
72
|
+
onAnalyticsEvent?.("import_mapping_complete");
|
65
73
|
setStep("preview");
|
66
|
-
}, []);
|
74
|
+
}, [onAnalyticsEvent]);
|
67
75
|
|
68
76
|
const onPreviewComplete = useCallback(() => {
|
77
|
+
onAnalyticsEvent?.("import_data_save");
|
69
78
|
setStep("import_data_saving");
|
70
|
-
}, []);
|
79
|
+
}, [onAnalyticsEvent]);
|
71
80
|
|
72
81
|
const onDataAdded = async (data: object[]) => {
|
73
82
|
importConfig.setImportData(data);
|
@@ -76,15 +85,15 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
76
85
|
const originProperties = await buildEntityPropertiesFromData(data, getInferenceType);
|
77
86
|
importConfig.setOriginProperties(originProperties);
|
78
87
|
|
79
|
-
const headersMapping = buildHeadersMappingFromData(data);
|
88
|
+
const headersMapping = buildHeadersMappingFromData(data, collection?.properties);
|
80
89
|
importConfig.setHeadersMapping(headersMapping);
|
81
90
|
const firstKey = Object.keys(headersMapping)?.[0];
|
82
91
|
if (firstKey?.includes("id") || firstKey?.includes("key")) {
|
83
|
-
|
84
|
-
importConfig.setIdColumn(idColumn);
|
92
|
+
importConfig.setIdColumn(firstKey);
|
85
93
|
}
|
86
94
|
}
|
87
95
|
setTimeout(() => {
|
96
|
+
onAnalyticsEvent?.("import_data_added");
|
88
97
|
setStep("mapping");
|
89
98
|
}, 100);
|
90
99
|
// setStep("mapping");
|
@@ -104,6 +113,7 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
104
113
|
if (collection.collectionGroup) {
|
105
114
|
return null;
|
106
115
|
}
|
116
|
+
|
107
117
|
return <>
|
108
118
|
|
109
119
|
<Tooltip title={"Import"}>
|
@@ -126,17 +136,14 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
126
136
|
</>}
|
127
137
|
|
128
138
|
{step === "mapping" && <>
|
129
|
-
<Typography variant={"h6"}>Map fields</Typography>
|
130
|
-
<DataNewPropertiesMapping
|
131
|
-
idColumn={importConfig.idColumn}
|
132
|
-
originProperties={importConfig.originProperties}
|
139
|
+
<Typography variant={"h6"} className={"ml-3.5"}>Map fields</Typography>
|
140
|
+
<DataNewPropertiesMapping importConfig={importConfig}
|
133
141
|
destinationProperties={properties}
|
134
|
-
onIdPropertyChanged={(value) => importConfig.setIdColumn(value)}
|
135
142
|
buildPropertyView={({
|
136
143
|
isIdColumn,
|
137
144
|
property,
|
138
145
|
propertyKey,
|
139
|
-
importKey
|
146
|
+
importKey,
|
140
147
|
}) => {
|
141
148
|
return <PropertyTreeSelect
|
142
149
|
selectedPropertyKey={propertyKey ?? ""}
|
@@ -148,6 +155,7 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
148
155
|
}}
|
149
156
|
onPropertySelected={(newPropertyKey) => {
|
150
157
|
|
158
|
+
onAnalyticsEvent?.("import_mapping_field_updated");
|
151
159
|
const newHeadersMapping: Record<string, string | null> = Object.entries(importConfig.headersMapping)
|
152
160
|
.map(([currentImportKey, currentPropertyKey]) => {
|
153
161
|
if (currentPropertyKey === newPropertyKey) {
|
@@ -248,30 +256,31 @@ function PropertyTreeSelect({
|
|
248
256
|
}
|
249
257
|
|
250
258
|
if (!selectedPropertyKey || !selectedProperty) {
|
251
|
-
return <Typography variant={"body2"} className={"p-4"}>Do not import this
|
259
|
+
return <Typography variant={"body2"} color="disabled" className={"p-4"}>Do not import this
|
260
|
+
property</Typography>;
|
252
261
|
}
|
253
262
|
|
254
263
|
return <PropertySelectEntry propertyKey={selectedPropertyKey}
|
255
264
|
property={selectedProperty as Property}/>;
|
256
265
|
}, [selectedProperty]);
|
257
266
|
|
258
|
-
const onSelectValueChange =
|
267
|
+
const onSelectValueChange = (value: string) => {
|
259
268
|
if (value === internalIDValue) {
|
260
269
|
onIdSelected();
|
261
270
|
onPropertySelected(null);
|
262
|
-
} else if (value === "") {
|
271
|
+
} else if (value === "__do_not_import") {
|
263
272
|
onPropertySelected(null);
|
264
273
|
} else {
|
265
274
|
onPropertySelected(value);
|
266
275
|
}
|
267
|
-
}
|
276
|
+
};
|
268
277
|
|
269
278
|
return <Select value={isIdColumn ? internalIDValue : (selectedPropertyKey ?? undefined)}
|
270
279
|
onValueChange={onSelectValueChange}
|
271
280
|
renderValue={renderValue}>
|
272
281
|
|
273
|
-
<SelectItem value={""}>
|
274
|
-
<Typography variant={"body2"} className={"p-4"}>Do not import this property</Typography>
|
282
|
+
<SelectItem value={"__do_not_import"}>
|
283
|
+
<Typography variant={"body2"} color={"disabled"} className={"p-4"}>Do not import this property</Typography>
|
275
284
|
</SelectItem>
|
276
285
|
|
277
286
|
<SelectItem value={internalIDValue}>
|
@@ -370,12 +379,11 @@ export function ImportDataPreview<M extends Record<string, any>>({
|
|
370
379
|
}: {
|
371
380
|
importConfig: ImportConfig,
|
372
381
|
properties: ResolvedProperties<M>,
|
373
|
-
propertiesOrder: Extract<keyof M, string>[]
|
382
|
+
propertiesOrder: Extract<keyof M, string>[],
|
374
383
|
}) {
|
375
384
|
|
376
385
|
useEffect(() => {
|
377
|
-
const
|
378
|
-
const mappedData = importConfig.importData.map(d => convertDataToEntity(d, importConfig.idColumn, importConfig.headersMapping, properties, propertiesMapping, "TEMP_PATH"));
|
386
|
+
const mappedData = importConfig.importData.map(d => convertDataToEntity(d, importConfig.idColumn, importConfig.headersMapping, properties, "TEMP_PATH", importConfig.defaultValues));
|
379
387
|
importConfig.setEntities(mappedData);
|
380
388
|
}, []);
|
381
389
|
|
@@ -391,30 +399,43 @@ export function ImportDataPreview<M extends Record<string, any>>({
|
|
391
399
|
dataLoading: false,
|
392
400
|
noMoreToLoad: false
|
393
401
|
}}
|
402
|
+
enablePopupIcon={false}
|
394
403
|
endAdornment={<div className={"h-12"}/>}
|
395
404
|
filterable={false}
|
396
405
|
sortable={false}
|
397
406
|
selectionController={selectionController}
|
398
|
-
displayedColumnIds={propertiesOrder.map(p => ({
|
399
|
-
key: p,
|
400
|
-
disabled: false
|
401
|
-
}))}
|
402
407
|
properties={properties}/>
|
403
408
|
|
404
409
|
}
|
405
410
|
|
406
|
-
function buildHeadersMappingFromData(objArr: object[]) {
|
411
|
+
function buildHeadersMappingFromData(objArr: object[], properties?: PropertiesOrBuilders<any>) {
|
407
412
|
const headersMapping: Record<string, string> = {};
|
408
413
|
objArr.filter(Boolean).forEach((obj) => {
|
409
414
|
Object.keys(obj).forEach((key) => {
|
410
415
|
// @ts-ignore
|
411
416
|
const child = obj[key];
|
412
417
|
if (typeof child === "object" && !Array.isArray(child)) {
|
413
|
-
|
418
|
+
const childProperty = properties?.[key];
|
419
|
+
const childProperties = childProperty && "properties" in childProperty ? childProperty.properties : undefined;
|
420
|
+
const childHeadersMapping = buildHeadersMappingFromData([child], childProperties);
|
421
|
+
Object.entries(childHeadersMapping).forEach(([subKey, mapping]) => {
|
414
422
|
headersMapping[`${key}.${subKey}`] = `${key}.${mapping}`;
|
415
423
|
});
|
416
424
|
}
|
417
|
-
|
425
|
+
|
426
|
+
if (!properties) {
|
427
|
+
headersMapping[key] = key;
|
428
|
+
} else if (key in properties) {
|
429
|
+
headersMapping[key] = key;
|
430
|
+
} else {
|
431
|
+
const slug = slugify(key);
|
432
|
+
if (slug in properties) {
|
433
|
+
headersMapping[key] = slug;
|
434
|
+
} else {
|
435
|
+
headersMapping[key] = key;
|
436
|
+
}
|
437
|
+
}
|
438
|
+
|
418
439
|
});
|
419
440
|
});
|
420
441
|
return headersMapping;
|
@@ -4,7 +4,6 @@ import {
|
|
4
4
|
EntityReference,
|
5
5
|
getArrayValuesCount,
|
6
6
|
getValueInPath,
|
7
|
-
ResolvedEntityCollection,
|
8
7
|
ResolvedProperties,
|
9
8
|
ResolvedProperty
|
10
9
|
} from "@firecms/core";
|
@@ -14,37 +13,43 @@ interface Header {
|
|
14
13
|
label: string;
|
15
14
|
}
|
16
15
|
|
17
|
-
export function
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
export function downloadEntitiesExport<M extends Record<string, any>>(data: Entity<M>[],
|
17
|
+
additionalData: Record<string, any>[] | undefined,
|
18
|
+
properties: ResolvedProperties<M>,
|
19
|
+
propertiesOrder: string[] | undefined,
|
20
|
+
name: string,
|
21
|
+
flattenArrays: boolean,
|
22
|
+
additionalHeaders: string[] | undefined,
|
23
|
+
exportType: "csv" | "json",
|
24
|
+
dateExportType: "timestamp" | "string"
|
24
25
|
) {
|
25
26
|
|
26
|
-
console.debug("Downloading export", {
|
27
|
-
|
27
|
+
console.debug("Downloading export", {
|
28
|
+
dataLength: data.length,
|
29
|
+
properties,
|
30
|
+
exportType,
|
31
|
+
dateExportType
|
32
|
+
});
|
28
33
|
|
29
34
|
if (exportType === "csv") {
|
30
35
|
const arrayValuesCount = flattenArrays ? getArrayValuesCount(data.map(d => d.values)) : {};
|
31
|
-
const headers = getExportHeaders(properties, additionalHeaders, arrayValuesCount);
|
32
|
-
const exportableData =
|
36
|
+
const headers = getExportHeaders(properties, propertiesOrder, additionalHeaders, arrayValuesCount);
|
37
|
+
const exportableData = getEntityCSVExportableData(data, additionalData, properties, headers, dateExportType);
|
33
38
|
const headersData = entryToCSVRow(headers.map(h => h.label));
|
34
39
|
const csvData = exportableData.map(entry => entryToCSVRow(entry));
|
35
|
-
downloadBlob([headersData, ...csvData], `${
|
40
|
+
downloadBlob([headersData, ...csvData], `${name}.csv`, "text/csv");
|
36
41
|
} else {
|
37
|
-
const exportableData =
|
42
|
+
const exportableData = getEntityJsonExportableData(data, additionalData, properties, dateExportType);
|
38
43
|
const json = JSON.stringify(exportableData, null, 2);
|
39
|
-
downloadBlob([json], `${
|
44
|
+
downloadBlob([json], `${name}.json`, "application/json");
|
40
45
|
}
|
41
46
|
}
|
42
47
|
|
43
|
-
export function
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
+
export function getEntityCSVExportableData(data: Entity<any>[],
|
49
|
+
additionalData: Record<string, any>[] | undefined,
|
50
|
+
properties: ResolvedProperties,
|
51
|
+
headers: Header[],
|
52
|
+
dateExportType: "timestamp" | "string"
|
48
53
|
) {
|
49
54
|
|
50
55
|
const mergedData: any[] = data.map(e => ({
|
@@ -63,10 +68,10 @@ export function getCSVExportableData(data: Entity<any>[],
|
|
63
68
|
});
|
64
69
|
}
|
65
70
|
|
66
|
-
export function
|
67
|
-
|
68
|
-
|
69
|
-
|
71
|
+
export function getEntityJsonExportableData(data: Entity<any>[],
|
72
|
+
additionalData: Record<string, any>[] | undefined,
|
73
|
+
properties: ResolvedProperties,
|
74
|
+
dateExportType: "timestamp" | "string"
|
70
75
|
) {
|
71
76
|
|
72
77
|
const mergedData: any[] = data.map(e => ({
|
@@ -84,13 +89,22 @@ export function getJsonExportableData(data: Entity<any>[],
|
|
84
89
|
}
|
85
90
|
|
86
91
|
function getExportHeaders<M extends Record<string, any>>(properties: ResolvedProperties<M>,
|
92
|
+
propertiesOrder: string[] | undefined,
|
87
93
|
additionalHeaders: string[] | undefined,
|
88
94
|
arrayValuesCount?: ArrayValuesCount): Header[] {
|
89
95
|
|
90
96
|
const headers: Header[] = [
|
91
|
-
{
|
92
|
-
|
93
|
-
|
97
|
+
{
|
98
|
+
label: "id",
|
99
|
+
key: "id"
|
100
|
+
},
|
101
|
+
...(propertiesOrder ?? Object.keys(properties))
|
102
|
+
.flatMap((childKey) => {
|
103
|
+
const property = properties[childKey];
|
104
|
+
if(!property) {
|
105
|
+
console.warn("Property not found", childKey, properties);
|
106
|
+
return [];
|
107
|
+
}
|
94
108
|
if (arrayValuesCount && arrayValuesCount[childKey] > 1) {
|
95
109
|
return Array.from({ length: arrayValuesCount[childKey] },
|
96
110
|
(_, i) => getHeaders(property as ResolvedProperty, `${childKey}[${i}]`, ""))
|
@@ -102,7 +116,10 @@ function getExportHeaders<M extends Record<string, any>>(properties: ResolvedPro
|
|
102
116
|
];
|
103
117
|
|
104
118
|
if (additionalHeaders) {
|
105
|
-
headers.push(...additionalHeaders.map(h => ({
|
119
|
+
headers.push(...additionalHeaders.map(h => ({
|
120
|
+
label: h,
|
121
|
+
key: h
|
122
|
+
})));
|
106
123
|
}
|
107
124
|
|
108
125
|
return headers;
|
@@ -121,7 +138,10 @@ function getHeaders(property: ResolvedProperty, propertyKey: string, prefix = ""
|
|
121
138
|
.map(([childKey, p]) => getHeaders(p, childKey, currentKey))
|
122
139
|
.flat();
|
123
140
|
} else {
|
124
|
-
return [{
|
141
|
+
return [{
|
142
|
+
label: currentKey,
|
143
|
+
key: currentKey
|
144
|
+
}];
|
125
145
|
}
|
126
146
|
}
|
127
147
|
|
@@ -149,7 +169,7 @@ function processValueForExport(inputValue: any,
|
|
149
169
|
} else {
|
150
170
|
value = inputValue;
|
151
171
|
}
|
152
|
-
} else if (property.dataType === "reference" && inputValue.isEntityReference && inputValue.isEntityReference()) {
|
172
|
+
} else if (property.dataType === "reference" && inputValue && inputValue.isEntityReference && inputValue.isEntityReference()) {
|
153
173
|
const ref = inputValue ? inputValue as EntityReference : undefined;
|
154
174
|
value = ref ? ref.pathWithId : null;
|
155
175
|
} else if (property.dataType === "date" && inputValue instanceof Date) {
|
@@ -5,10 +5,12 @@ import { ImportConfig } from "../types";
|
|
5
5
|
export const useImportConfig = (): ImportConfig => {
|
6
6
|
|
7
7
|
const [inUse, setInUse] = useState<boolean>(false);
|
8
|
+
const [defaultValues, setDefaultValues] = useState<Record<string, any>>({});
|
8
9
|
const [idColumn, setIdColumn] = useState<string | undefined>();
|
9
10
|
const [importData, setImportData] = useState<object[]>([]);
|
10
11
|
const [entities, setEntities] = useState<Entity<any>[]>([]);
|
11
12
|
const [headersMapping, setHeadersMapping] = useState<Record<string, string | null>>({});
|
13
|
+
const [headingsOrder, setHeadingsOrder] = useState<string[]>([]);
|
12
14
|
const [originProperties, setOriginProperties] = useState<Record<string, Property>>({});
|
13
15
|
|
14
16
|
return {
|
@@ -20,9 +22,13 @@ export const useImportConfig = (): ImportConfig => {
|
|
20
22
|
setEntities,
|
21
23
|
importData,
|
22
24
|
setImportData,
|
25
|
+
headingsOrder: (headingsOrder ?? []).length > 0 ? headingsOrder : Object.keys(headersMapping),
|
26
|
+
setHeadingsOrder,
|
23
27
|
headersMapping,
|
24
28
|
setHeadersMapping,
|
25
29
|
originProperties,
|
26
30
|
setOriginProperties,
|
31
|
+
defaultValues,
|
32
|
+
setDefaultValues
|
27
33
|
};
|
28
34
|
};
|
package/src/index.ts
CHANGED
@@ -22,11 +22,11 @@ export type ImportConfig = {
|
|
22
22
|
originProperties: Record<string, Property>;
|
23
23
|
setOriginProperties: React.Dispatch<React.SetStateAction<Record<string, Property>>>;
|
24
24
|
|
25
|
-
|
25
|
+
// unmapped headings order
|
26
|
+
headingsOrder: string[];
|
27
|
+
setHeadingsOrder: React.Dispatch<React.SetStateAction<string[]>>;
|
28
|
+
|
29
|
+
defaultValues: Record<string, any>;
|
30
|
+
setDefaultValues: React.Dispatch<React.SetStateAction<Record<string, any>>>;
|
26
31
|
|
27
|
-
export type DataTypeMapping = {
|
28
|
-
from: DataType;
|
29
|
-
fromSubtype?: DataType;
|
30
|
-
to: DataType;
|
31
|
-
toSubtype?: DataType;
|
32
32
|
}
|
@@ -9,8 +9,8 @@ import { ExportCollectionAction } from "./export_import/ExportCollectionAction";
|
|
9
9
|
export function useImportExportPlugin(props?: ImportExportPluginProps): FireCMSPlugin<any, any, any, ImportExportPluginProps> {
|
10
10
|
|
11
11
|
return useMemo(() => ({
|
12
|
-
|
13
|
-
|
12
|
+
key: "import_export",
|
13
|
+
collectionView: {
|
14
14
|
CollectionActions: [ImportCollectionAction, ExportCollectionAction],
|
15
15
|
collectionActionsProps: props
|
16
16
|
}
|
@@ -19,6 +19,7 @@ export function useImportExportPlugin(props?: ImportExportPluginProps): FireCMSP
|
|
19
19
|
|
20
20
|
export type ImportExportPluginProps = {
|
21
21
|
exportAllowed?: (props: ExportAllowedParams) => boolean;
|
22
|
-
notAllowedView
|
22
|
+
notAllowedView?: React.ReactNode;
|
23
|
+
onAnalyticsEvent?: (event: string, params?: any) => void;
|
23
24
|
}
|
24
25
|
export type ExportAllowedParams = { collectionEntitiesCount: number, path: string, collection: EntityCollection };
|