@firecms/data_import_export 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.
- 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 +1 -1
- package/dist/export_import/export.d.ts +15 -4
- package/dist/export_import/index.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +940 -570
- 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 +1 -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 -34
- package/src/components/DataNewPropertiesMapping.tsx +153 -40
- package/src/components/ImportFileUpload.tsx +12 -4
- package/src/components/ImportNewPropertyFieldPreview.tsx +7 -2
- package/src/export_import/BasicExportAction.tsx +147 -0
- package/src/export_import/ExportCollectionAction.tsx +45 -9
- package/src/export_import/ImportCollectionAction.tsx +22 -22
- package/src/export_import/export.ts +63 -29
- 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 +2 -2
- package/src/utils/data.ts +36 -126
- 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
@@ -5,6 +5,7 @@ import {
|
|
5
5
|
Entity,
|
6
6
|
EntityCollection,
|
7
7
|
ExportConfig,
|
8
|
+
getDefaultValuesFor,
|
8
9
|
resolveCollection,
|
9
10
|
ResolvedEntityCollection,
|
10
11
|
useCustomizationController,
|
@@ -18,7 +19,7 @@ import {
|
|
18
19
|
BooleanSwitchWithLabel,
|
19
20
|
Button,
|
20
21
|
CircularProgress,
|
21
|
-
|
22
|
+
cls,
|
22
23
|
Dialog,
|
23
24
|
DialogActions,
|
24
25
|
DialogContent,
|
@@ -26,9 +27,9 @@ import {
|
|
26
27
|
GetAppIcon,
|
27
28
|
IconButton,
|
28
29
|
Tooltip,
|
29
|
-
Typography
|
30
|
+
Typography
|
30
31
|
} from "@firecms/ui";
|
31
|
-
import {
|
32
|
+
import { downloadEntitiesExport } from "./export";
|
32
33
|
|
33
34
|
const DOCS_LIMIT = 500;
|
34
35
|
|
@@ -36,6 +37,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
36
37
|
collection: inputCollection,
|
37
38
|
path: inputPath,
|
38
39
|
collectionEntitiesCount,
|
40
|
+
onAnalyticsEvent,
|
39
41
|
exportAllowed,
|
40
42
|
notAllowedView
|
41
43
|
}: CollectionActionsProps<M, UserType, EntityCollection<M, any>> & {
|
@@ -49,6 +51,8 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
49
51
|
const exportConfig = typeof inputCollection.exportable === "object" ? inputCollection.exportable : undefined;
|
50
52
|
|
51
53
|
const dateRef = React.useRef<Date>(new Date());
|
54
|
+
|
55
|
+
const [includeUndefinedValues, setIncludeUndefinedValues] = React.useState<boolean>(false);
|
52
56
|
const [flattenArrays, setFlattenArrays] = React.useState<boolean>(true);
|
53
57
|
const [exportType, setExportType] = React.useState<"csv" | "json">("csv");
|
54
58
|
const [dateExportType, setDateExportType] = React.useState<"timestamp" | "string">("string");
|
@@ -123,6 +127,9 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
123
127
|
const doDownload = useCallback(async (collection: ResolvedEntityCollection<M>,
|
124
128
|
exportConfig: ExportConfig<any> | undefined) => {
|
125
129
|
|
130
|
+
onAnalyticsEvent?.("export_collection", {
|
131
|
+
collection: collection.path
|
132
|
+
});
|
126
133
|
setDataLoading(true);
|
127
134
|
dataSource.fetchCollection<M>({
|
128
135
|
path,
|
@@ -135,7 +142,30 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
135
142
|
...exportConfig?.additionalFields?.map(column => column.key) ?? [],
|
136
143
|
...collection.additionalFields?.map(field => field.key) ?? []
|
137
144
|
];
|
138
|
-
|
145
|
+
|
146
|
+
const dataWithDefaults = includeUndefinedValues
|
147
|
+
? data.map(entity => {
|
148
|
+
const defaultValues = getDefaultValuesFor(collection.properties);
|
149
|
+
return {
|
150
|
+
...entity,
|
151
|
+
values: { ...defaultValues, ...entity.values }
|
152
|
+
};
|
153
|
+
})
|
154
|
+
: data;
|
155
|
+
downloadEntitiesExport({
|
156
|
+
data: dataWithDefaults,
|
157
|
+
additionalData,
|
158
|
+
properties: collection.properties,
|
159
|
+
propertiesOrder: collection.propertiesOrder,
|
160
|
+
name: collection.name,
|
161
|
+
flattenArrays,
|
162
|
+
additionalHeaders,
|
163
|
+
exportType,
|
164
|
+
dateExportType
|
165
|
+
});
|
166
|
+
onAnalyticsEvent?.("export_collection_success", {
|
167
|
+
collection: collection.path
|
168
|
+
});
|
139
169
|
})
|
140
170
|
.catch((e) => {
|
141
171
|
console.error("Error loading export data", e);
|
@@ -143,7 +173,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
143
173
|
})
|
144
174
|
.finally(() => setDataLoading(false));
|
145
175
|
|
146
|
-
}, [dataSource, path, fetchAdditionalFields, flattenArrays, exportType, dateExportType]);
|
176
|
+
}, [onAnalyticsEvent, dataSource, path, fetchAdditionalFields, includeUndefinedValues, flattenArrays, exportType, dateExportType]);
|
147
177
|
|
148
178
|
const onOkClicked = useCallback(() => {
|
149
179
|
doDownload(collection, exportConfig);
|
@@ -182,7 +212,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
182
212
|
<input id="radio-csv" type="radio" value="csv" name="exportType"
|
183
213
|
checked={exportType === "csv"}
|
184
214
|
onChange={() => setExportType("csv")}
|
185
|
-
className={
|
215
|
+
className={cls(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
186
216
|
<label htmlFor="radio-csv"
|
187
217
|
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">CSV</label>
|
188
218
|
</div>
|
@@ -190,7 +220,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
190
220
|
<input id="radio-json" type="radio" value="json" name="exportType"
|
191
221
|
checked={exportType === "json"}
|
192
222
|
onChange={() => setExportType("json")}
|
193
|
-
className={
|
223
|
+
className={cls(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
194
224
|
<label htmlFor="radio-json"
|
195
225
|
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">JSON</label>
|
196
226
|
</div>
|
@@ -201,7 +231,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
201
231
|
<input id="radio-timestamp" type="radio" value="timestamp" name="dateExportType"
|
202
232
|
checked={dateExportType === "timestamp"}
|
203
233
|
onChange={() => setDateExportType("timestamp")}
|
204
|
-
className={
|
234
|
+
className={cls(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
205
235
|
<label htmlFor="radio-timestamp"
|
206
236
|
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">Dates as
|
207
237
|
timestamps ({dateRef.current.getTime()})</label>
|
@@ -210,7 +240,7 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
210
240
|
<input id="radio-string" type="radio" value="string" name="dateExportType"
|
211
241
|
checked={dateExportType === "string"}
|
212
242
|
onChange={() => setDateExportType("string")}
|
213
|
-
className={
|
243
|
+
className={cls(focusedMixin, "w-4 text-primary-dark bg-gray-100 border-gray-300 dark:bg-gray-700 dark:border-gray-600")}/>
|
214
244
|
<label htmlFor="radio-string"
|
215
245
|
className="p-2 text-sm font-medium text-gray-900 dark:text-slate-300">Dates as
|
216
246
|
strings ({dateRef.current.toISOString()})</label>
|
@@ -225,6 +255,12 @@ export function ExportCollectionAction<M extends Record<string, any>, UserType e
|
|
225
255
|
onValueChange={setFlattenArrays}
|
226
256
|
label={"Flatten arrays"}/>
|
227
257
|
|
258
|
+
<BooleanSwitchWithLabel
|
259
|
+
size={"small"}
|
260
|
+
value={includeUndefinedValues}
|
261
|
+
onValueChange={setIncludeUndefinedValues}
|
262
|
+
label={"Include undefined values"}/>
|
263
|
+
|
228
264
|
{!canExport && notAllowedView}
|
229
265
|
|
230
266
|
</DialogContent>
|
@@ -18,7 +18,7 @@ import {
|
|
18
18
|
} from "@firecms/core";
|
19
19
|
import {
|
20
20
|
Button,
|
21
|
-
|
21
|
+
cls,
|
22
22
|
defaultBorderMixin,
|
23
23
|
Dialog,
|
24
24
|
DialogActions,
|
@@ -32,7 +32,7 @@ import {
|
|
32
32
|
} from "@firecms/ui";
|
33
33
|
import { buildEntityPropertiesFromData } from "@firecms/schema_inference";
|
34
34
|
import { useImportConfig } from "../hooks";
|
35
|
-
import { convertDataToEntity, getInferenceType
|
35
|
+
import { convertDataToEntity, getInferenceType } from "../utils";
|
36
36
|
import { DataNewPropertiesMapping, ImportFileUpload, ImportSaveInProgress } from "../components";
|
37
37
|
import { ImportConfig } from "../types";
|
38
38
|
|
@@ -60,20 +60,23 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
60
60
|
|
61
61
|
const handleClickOpen = useCallback(() => {
|
62
62
|
setOpen(true);
|
63
|
+
onAnalyticsEvent?.("import_open");
|
63
64
|
setStep("initial");
|
64
|
-
}, [
|
65
|
+
}, [onAnalyticsEvent]);
|
65
66
|
|
66
67
|
const handleClose = useCallback(() => {
|
67
68
|
setOpen(false);
|
68
69
|
}, [setOpen]);
|
69
70
|
|
70
71
|
const onMappingComplete = useCallback(() => {
|
72
|
+
onAnalyticsEvent?.("import_mapping_complete");
|
71
73
|
setStep("preview");
|
72
|
-
}, []);
|
74
|
+
}, [onAnalyticsEvent]);
|
73
75
|
|
74
76
|
const onPreviewComplete = useCallback(() => {
|
77
|
+
onAnalyticsEvent?.("import_data_save");
|
75
78
|
setStep("import_data_saving");
|
76
|
-
}, []);
|
79
|
+
}, [onAnalyticsEvent]);
|
77
80
|
|
78
81
|
const onDataAdded = async (data: object[]) => {
|
79
82
|
importConfig.setImportData(data);
|
@@ -90,6 +93,7 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
90
93
|
}
|
91
94
|
}
|
92
95
|
setTimeout(() => {
|
96
|
+
onAnalyticsEvent?.("import_data_added");
|
93
97
|
setStep("mapping");
|
94
98
|
}, 100);
|
95
99
|
// setStep("mapping");
|
@@ -109,6 +113,7 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
109
113
|
if (collection.collectionGroup) {
|
110
114
|
return null;
|
111
115
|
}
|
116
|
+
|
112
117
|
return <>
|
113
118
|
|
114
119
|
<Tooltip title={"Import"}>
|
@@ -131,17 +136,14 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
131
136
|
</>}
|
132
137
|
|
133
138
|
{step === "mapping" && <>
|
134
|
-
<Typography variant={"h6"}>Map fields</Typography>
|
135
|
-
<DataNewPropertiesMapping
|
136
|
-
idColumn={importConfig.idColumn}
|
137
|
-
originProperties={importConfig.originProperties}
|
139
|
+
<Typography variant={"h6"} className={"ml-3.5"}>Map fields</Typography>
|
140
|
+
<DataNewPropertiesMapping importConfig={importConfig}
|
138
141
|
destinationProperties={properties}
|
139
|
-
onIdPropertyChanged={(value) => importConfig.setIdColumn(value ?? undefined)}
|
140
142
|
buildPropertyView={({
|
141
143
|
isIdColumn,
|
142
144
|
property,
|
143
145
|
propertyKey,
|
144
|
-
importKey
|
146
|
+
importKey,
|
145
147
|
}) => {
|
146
148
|
return <PropertyTreeSelect
|
147
149
|
selectedPropertyKey={propertyKey ?? ""}
|
@@ -153,6 +155,7 @@ export function ImportCollectionAction<M extends Record<string, any>, UserType e
|
|
153
155
|
}}
|
154
156
|
onPropertySelected={(newPropertyKey) => {
|
155
157
|
|
158
|
+
onAnalyticsEvent?.("import_mapping_field_updated");
|
156
159
|
const newHeadersMapping: Record<string, string | null> = Object.entries(importConfig.headersMapping)
|
157
160
|
.map(([currentImportKey, currentPropertyKey]) => {
|
158
161
|
if (currentPropertyKey === newPropertyKey) {
|
@@ -253,14 +256,15 @@ function PropertyTreeSelect({
|
|
253
256
|
}
|
254
257
|
|
255
258
|
if (!selectedPropertyKey || !selectedProperty) {
|
256
|
-
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>;
|
257
261
|
}
|
258
262
|
|
259
263
|
return <PropertySelectEntry propertyKey={selectedPropertyKey}
|
260
264
|
property={selectedProperty as Property}/>;
|
261
265
|
}, [selectedProperty]);
|
262
266
|
|
263
|
-
const onSelectValueChange =
|
267
|
+
const onSelectValueChange = (value: string) => {
|
264
268
|
if (value === internalIDValue) {
|
265
269
|
onIdSelected();
|
266
270
|
onPropertySelected(null);
|
@@ -269,14 +273,14 @@ function PropertyTreeSelect({
|
|
269
273
|
} else {
|
270
274
|
onPropertySelected(value);
|
271
275
|
}
|
272
|
-
}
|
276
|
+
};
|
273
277
|
|
274
278
|
return <Select value={isIdColumn ? internalIDValue : (selectedPropertyKey ?? undefined)}
|
275
279
|
onValueChange={onSelectValueChange}
|
276
280
|
renderValue={renderValue}>
|
277
281
|
|
278
282
|
<SelectItem value={"__do_not_import"}>
|
279
|
-
<Typography variant={"body2"} className={"p-4"}>Do not import this property</Typography>
|
283
|
+
<Typography variant={"body2"} color={"disabled"} className={"p-4"}>Do not import this property</Typography>
|
280
284
|
</SelectItem>
|
281
285
|
|
282
286
|
<SelectItem value={internalIDValue}>
|
@@ -338,7 +342,7 @@ export function PropertySelectEntry({
|
|
338
342
|
className="flex flex-row w-full text-start items-center h-full">
|
339
343
|
|
340
344
|
{new Array(level).fill(0).map((_, index) =>
|
341
|
-
<div className={
|
345
|
+
<div className={cls(defaultBorderMixin, "ml-8 border-l h-12")} key={index}/>)}
|
342
346
|
|
343
347
|
<div className={"m-4"}>
|
344
348
|
<Tooltip title={widget?.name}>
|
@@ -379,8 +383,7 @@ export function ImportDataPreview<M extends Record<string, any>>({
|
|
379
383
|
}) {
|
380
384
|
|
381
385
|
useEffect(() => {
|
382
|
-
const
|
383
|
-
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));
|
384
387
|
importConfig.setEntities(mappedData);
|
385
388
|
}, []);
|
386
389
|
|
@@ -396,14 +399,11 @@ export function ImportDataPreview<M extends Record<string, any>>({
|
|
396
399
|
dataLoading: false,
|
397
400
|
noMoreToLoad: false
|
398
401
|
}}
|
402
|
+
enablePopupIcon={false}
|
399
403
|
endAdornment={<div className={"h-12"}/>}
|
400
404
|
filterable={false}
|
401
405
|
sortable={false}
|
402
406
|
selectionController={selectionController}
|
403
|
-
displayedColumnIds={propertiesOrder.map(p => ({
|
404
|
-
key: p,
|
405
|
-
disabled: false
|
406
|
-
}))}
|
407
407
|
properties={properties}/>
|
408
408
|
|
409
409
|
}
|
@@ -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,57 @@ interface Header {
|
|
14
13
|
label: string;
|
15
14
|
}
|
16
15
|
|
17
|
-
export
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
export interface DownloadEntitiesExportParams<M extends Record<string, any>> {
|
17
|
+
data: Entity<M>[];
|
18
|
+
additionalData: Record<string, any>[] | undefined;
|
19
|
+
properties: ResolvedProperties<M>;
|
20
|
+
propertiesOrder: string[] | undefined;
|
21
|
+
name: string;
|
22
|
+
flattenArrays: boolean;
|
23
|
+
additionalHeaders: string[] | undefined;
|
24
|
+
exportType: "csv" | "json";
|
25
|
+
dateExportType: "timestamp" | "string";
|
26
|
+
}
|
27
|
+
|
28
|
+
export function downloadEntitiesExport<M extends Record<string, any>>({
|
29
|
+
data,
|
30
|
+
additionalData,
|
31
|
+
properties,
|
32
|
+
propertiesOrder,
|
33
|
+
name,
|
34
|
+
flattenArrays,
|
35
|
+
additionalHeaders,
|
36
|
+
exportType,
|
37
|
+
dateExportType
|
38
|
+
}: DownloadEntitiesExportParams<M>
|
24
39
|
) {
|
25
40
|
|
26
|
-
console.debug("Downloading export", {
|
27
|
-
|
41
|
+
console.debug("Downloading export", {
|
42
|
+
dataLength: data.length,
|
43
|
+
properties,
|
44
|
+
exportType,
|
45
|
+
dateExportType
|
46
|
+
});
|
28
47
|
|
29
48
|
if (exportType === "csv") {
|
30
49
|
const arrayValuesCount = flattenArrays ? getArrayValuesCount(data.map(d => d.values)) : {};
|
31
|
-
const headers = getExportHeaders(properties, additionalHeaders, arrayValuesCount);
|
32
|
-
const exportableData =
|
50
|
+
const headers = getExportHeaders(properties, propertiesOrder, additionalHeaders, arrayValuesCount);
|
51
|
+
const exportableData = getEntityCSVExportableData(data, additionalData, properties, headers, dateExportType);
|
33
52
|
const headersData = entryToCSVRow(headers.map(h => h.label));
|
34
53
|
const csvData = exportableData.map(entry => entryToCSVRow(entry));
|
35
|
-
downloadBlob([headersData, ...csvData], `${
|
54
|
+
downloadBlob([headersData, ...csvData], `${name}.csv`, "text/csv");
|
36
55
|
} else {
|
37
|
-
const exportableData =
|
56
|
+
const exportableData = getEntityJsonExportableData(data, additionalData, properties, dateExportType);
|
38
57
|
const json = JSON.stringify(exportableData, null, 2);
|
39
|
-
downloadBlob([json], `${
|
58
|
+
downloadBlob([json], `${name}.json`, "application/json");
|
40
59
|
}
|
41
60
|
}
|
42
61
|
|
43
|
-
export function
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
62
|
+
export function getEntityCSVExportableData(data: Entity<any>[],
|
63
|
+
additionalData: Record<string, any>[] | undefined,
|
64
|
+
properties: ResolvedProperties,
|
65
|
+
headers: Header[],
|
66
|
+
dateExportType: "timestamp" | "string"
|
48
67
|
) {
|
49
68
|
|
50
69
|
const mergedData: any[] = data.map(e => ({
|
@@ -63,10 +82,10 @@ export function getCSVExportableData(data: Entity<any>[],
|
|
63
82
|
});
|
64
83
|
}
|
65
84
|
|
66
|
-
export function
|
67
|
-
|
68
|
-
|
69
|
-
|
85
|
+
export function getEntityJsonExportableData(data: Entity<any>[],
|
86
|
+
additionalData: Record<string, any>[] | undefined,
|
87
|
+
properties: ResolvedProperties,
|
88
|
+
dateExportType: "timestamp" | "string"
|
70
89
|
) {
|
71
90
|
|
72
91
|
const mergedData: any[] = data.map(e => ({
|
@@ -84,13 +103,22 @@ export function getJsonExportableData(data: Entity<any>[],
|
|
84
103
|
}
|
85
104
|
|
86
105
|
function getExportHeaders<M extends Record<string, any>>(properties: ResolvedProperties<M>,
|
106
|
+
propertiesOrder: string[] | undefined,
|
87
107
|
additionalHeaders: string[] | undefined,
|
88
108
|
arrayValuesCount?: ArrayValuesCount): Header[] {
|
89
109
|
|
90
110
|
const headers: Header[] = [
|
91
|
-
{
|
92
|
-
|
93
|
-
|
111
|
+
{
|
112
|
+
label: "id",
|
113
|
+
key: "id"
|
114
|
+
},
|
115
|
+
...(propertiesOrder ?? Object.keys(properties))
|
116
|
+
.flatMap((childKey) => {
|
117
|
+
const property = properties[childKey];
|
118
|
+
if (!property) {
|
119
|
+
console.warn("Property not found", childKey, properties);
|
120
|
+
return [];
|
121
|
+
}
|
94
122
|
if (arrayValuesCount && arrayValuesCount[childKey] > 1) {
|
95
123
|
return Array.from({ length: arrayValuesCount[childKey] },
|
96
124
|
(_, i) => getHeaders(property as ResolvedProperty, `${childKey}[${i}]`, ""))
|
@@ -102,7 +130,10 @@ function getExportHeaders<M extends Record<string, any>>(properties: ResolvedPro
|
|
102
130
|
];
|
103
131
|
|
104
132
|
if (additionalHeaders) {
|
105
|
-
headers.push(...additionalHeaders.map(h => ({
|
133
|
+
headers.push(...additionalHeaders.map(h => ({
|
134
|
+
label: h,
|
135
|
+
key: h
|
136
|
+
})));
|
106
137
|
}
|
107
138
|
|
108
139
|
return headers;
|
@@ -121,7 +152,10 @@ function getHeaders(property: ResolvedProperty, propertyKey: string, prefix = ""
|
|
121
152
|
.map(([childKey, p]) => getHeaders(p, childKey, currentKey))
|
122
153
|
.flat();
|
123
154
|
} else {
|
124
|
-
return [{
|
155
|
+
return [{
|
156
|
+
label: currentKey,
|
157
|
+
key: currentKey
|
158
|
+
}];
|
125
159
|
}
|
126
160
|
}
|
127
161
|
|
@@ -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,7 +9,7 @@ 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
|
-
|
12
|
+
key: "import_export",
|
13
13
|
collectionView: {
|
14
14
|
CollectionActions: [ImportCollectionAction, ExportCollectionAction],
|
15
15
|
collectionActionsProps: props
|
@@ -19,7 +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
23
|
onAnalyticsEvent?: (event: string, params?: any) => void;
|
24
24
|
}
|
25
25
|
export type ExportAllowedParams = { collectionEntitiesCount: number, path: string, collection: EntityCollection };
|