@firecms/data_import_export 3.0.0-alpha.38
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 +21 -0
- package/README.md +87 -0
- package/dist/components/DataNewPropertiesMapping.d.ts +15 -0
- package/dist/components/ImportFileUpload.d.ts +3 -0
- package/dist/components/ImportNewPropertyFieldPreview.d.ts +10 -0
- package/dist/components/ImportSaveInProgress.d.ts +9 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/export_import/ExportCollectionAction.d.ts +10 -0
- package/dist/export_import/ImportCollectionAction.d.ts +13 -0
- package/dist/export_import/export.d.ts +10 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useImportConfig.d.ts +2 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.es.js +966 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +3 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/column_mapping.d.ts +22 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/useImportExportPlugin.d.ts +14 -0
- package/dist/utils/data.d.ts +11 -0
- package/dist/utils/file_to_json.d.ts +11 -0
- package/dist/utils/get_import_inference_type.d.ts +2 -0
- package/dist/utils/get_properties_mapping.d.ts +3 -0
- package/dist/utils/index.d.ts +4 -0
- package/package.json +103 -0
- package/src/components/DataNewPropertiesMapping.tsx +128 -0
- package/src/components/ImportFileUpload.tsx +28 -0
- package/src/components/ImportNewPropertyFieldPreview.tsx +67 -0
- package/src/components/ImportSaveInProgress.tsx +103 -0
- package/src/components/index.ts +4 -0
- package/src/export_import/ExportCollectionAction.tsx +243 -0
- package/src/export_import/ImportCollectionAction.tsx +419 -0
- package/src/export_import/export.ts +198 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useImportConfig.tsx +28 -0
- package/src/index.ts +5 -0
- package/src/types/column_mapping.ts +32 -0
- package/src/types/index.ts +1 -0
- package/src/useImportExportPlugin.tsx +23 -0
- package/src/utils/data.ts +210 -0
- package/src/utils/file_to_json.ts +83 -0
- package/src/utils/get_import_inference_type.ts +27 -0
- package/src/utils/get_properties_mapping.ts +60 -0
- package/src/utils/index.ts +4 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { EntityCollection, FireCMSPlugin } from "@firecms/core";
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
export declare function useImportExportPlugin(props?: ImportExportPluginProps): FireCMSPlugin<any, any, any, ImportExportPluginProps>;
|
|
7
|
+
export type ImportExportPluginProps = {
|
|
8
|
+
exportAllowed?: (props: {
|
|
9
|
+
collectionEntitiesCount: number;
|
|
10
|
+
path: string;
|
|
11
|
+
collection: EntityCollection;
|
|
12
|
+
}) => boolean;
|
|
13
|
+
notAllowedView: React.ReactNode;
|
|
14
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DataType, Entity, Properties } from "@firecms/core";
|
|
2
|
+
type DataTypeMapping = {
|
|
3
|
+
from: DataType;
|
|
4
|
+
fromSubtype?: DataType;
|
|
5
|
+
to: DataType;
|
|
6
|
+
toSubtype?: DataType;
|
|
7
|
+
};
|
|
8
|
+
export declare function convertDataToEntity(data: Record<any, any>, idColumn: string | undefined, headersMapping: Record<string, string | null>, properties: Properties, propertiesMapping: Record<string, DataTypeMapping>, path: string): Entity<any>;
|
|
9
|
+
export declare function flattenEntry(obj: any, parent?: string): any;
|
|
10
|
+
export declare function processValueMapping(value: any, valueMapping?: DataTypeMapping): any;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function convertFileToJson(file: File): Promise<object[]>;
|
|
2
|
+
/**
|
|
3
|
+
* Take an object with keys of type `address.street`, `address.city` and
|
|
4
|
+
* convert it to an object with nested objects like `{ address: { street: ..., city: ... } }`
|
|
5
|
+
* @param flatObj
|
|
6
|
+
*/
|
|
7
|
+
export declare function unflattenObject(flatObj: {
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}): {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firecms/data_import_export",
|
|
3
|
+
"version": "3.0.0-alpha.38",
|
|
4
|
+
"access": "public",
|
|
5
|
+
"main": "./dist/index.umd.js",
|
|
6
|
+
"module": "./dist/index.es.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"source": "src/index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.es.js",
|
|
12
|
+
"require": "./dist/index.umd.js",
|
|
13
|
+
"types": "./dist/src/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@firecms/core": "^3.0.0-alpha.38",
|
|
19
|
+
"@firecms/schema_inference": "^3.0.0-alpha.38",
|
|
20
|
+
"xlsx": "^0.18.5"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"algoliasearch": "^4.17.1",
|
|
24
|
+
"firebase": "^10.5.2",
|
|
25
|
+
"react": "^18.2.0",
|
|
26
|
+
"react-dom": "^18.2.0",
|
|
27
|
+
"react-router": "^6.12.0",
|
|
28
|
+
"react-router-dom": "^6.12.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "vite",
|
|
32
|
+
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
|
|
33
|
+
"prepublishOnly": "run-s build",
|
|
34
|
+
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f",
|
|
35
|
+
"test": "jest"
|
|
36
|
+
},
|
|
37
|
+
"eslintConfig": {
|
|
38
|
+
"extends": [
|
|
39
|
+
"react-app",
|
|
40
|
+
"react-app/jest"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"browserslist": {
|
|
44
|
+
"production": [
|
|
45
|
+
">0.2%",
|
|
46
|
+
"not dead",
|
|
47
|
+
"not op_mini all"
|
|
48
|
+
],
|
|
49
|
+
"development": [
|
|
50
|
+
"last 1 chrome version",
|
|
51
|
+
"last 1 firefox version",
|
|
52
|
+
"last 1 safari version"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@jest/globals": "^29.7.0",
|
|
57
|
+
"@testing-library/jest-dom": "^5.17.0",
|
|
58
|
+
"@types/jest": "^29.5.6",
|
|
59
|
+
"@types/react": "^18.2.45",
|
|
60
|
+
"@types/react-dom": "^18.2.17",
|
|
61
|
+
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
62
|
+
"@typescript-eslint/parser": "^5.62.0",
|
|
63
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
64
|
+
"babel-jest": "^29.7.0",
|
|
65
|
+
"eslint": "^8.55.0",
|
|
66
|
+
"eslint-config-standard": "^17.1.0",
|
|
67
|
+
"eslint-plugin-import": "^2.29.1",
|
|
68
|
+
"eslint-plugin-n": "^15.7.0",
|
|
69
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
70
|
+
"eslint-plugin-react": "^7.33.2",
|
|
71
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
72
|
+
"jest": "^29.7.0",
|
|
73
|
+
"ts-jest": "^29.1.1",
|
|
74
|
+
"typescript": "^5.3.3",
|
|
75
|
+
"vite": "^4.5.1",
|
|
76
|
+
"vite-plugin-fonts": "^0.7.0"
|
|
77
|
+
},
|
|
78
|
+
"jest": {
|
|
79
|
+
"transform": {
|
|
80
|
+
"^.+\\.tsx?$": "ts-jest"
|
|
81
|
+
},
|
|
82
|
+
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
|
|
83
|
+
"moduleFileExtensions": [
|
|
84
|
+
"ts",
|
|
85
|
+
"tsx",
|
|
86
|
+
"js",
|
|
87
|
+
"jsx",
|
|
88
|
+
"json",
|
|
89
|
+
"node"
|
|
90
|
+
],
|
|
91
|
+
"moduleNameMapper": {
|
|
92
|
+
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"files": [
|
|
96
|
+
"dist",
|
|
97
|
+
"src"
|
|
98
|
+
],
|
|
99
|
+
"publishConfig": {
|
|
100
|
+
"access": "public"
|
|
101
|
+
},
|
|
102
|
+
"gitHead": "bb42f6acf34a0e5225549b0e3ebf9a07b7aedf77"
|
|
103
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChevronRightIcon,
|
|
3
|
+
getPropertyInPath,
|
|
4
|
+
Property,
|
|
5
|
+
Select,
|
|
6
|
+
SelectItem,
|
|
7
|
+
Table,
|
|
8
|
+
TableBody,
|
|
9
|
+
TableCell,
|
|
10
|
+
TableHeader,
|
|
11
|
+
TableRow,
|
|
12
|
+
Typography
|
|
13
|
+
} from "@firecms/core";
|
|
14
|
+
|
|
15
|
+
export interface DataPropertyMappingProps {
|
|
16
|
+
idColumn?: string;
|
|
17
|
+
headersMapping: Record<string, string | null>;
|
|
18
|
+
originProperties: Record<string, Property>;
|
|
19
|
+
destinationProperties: Record<string, Property>;
|
|
20
|
+
onIdPropertyChanged: (value: string) => void;
|
|
21
|
+
buildPropertyView?: (props: {
|
|
22
|
+
isIdColumn: boolean,
|
|
23
|
+
property: Property | null,
|
|
24
|
+
propertyKey: string | null,
|
|
25
|
+
importKey: string
|
|
26
|
+
}) => React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function DataNewPropertiesMapping({
|
|
30
|
+
idColumn,
|
|
31
|
+
headersMapping,
|
|
32
|
+
originProperties,
|
|
33
|
+
destinationProperties,
|
|
34
|
+
onIdPropertyChanged,
|
|
35
|
+
buildPropertyView,
|
|
36
|
+
}: DataPropertyMappingProps) {
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
|
|
41
|
+
<IdSelectField idColumn={idColumn}
|
|
42
|
+
headersMapping={headersMapping}
|
|
43
|
+
onChange={onIdPropertyChanged}/>
|
|
44
|
+
|
|
45
|
+
<Table style={{
|
|
46
|
+
tableLayout: "fixed"
|
|
47
|
+
}}>
|
|
48
|
+
<TableHeader>
|
|
49
|
+
<TableCell header={true} style={{ width: "20%" }}>
|
|
50
|
+
Column in file
|
|
51
|
+
</TableCell>
|
|
52
|
+
<TableCell header={true}>
|
|
53
|
+
</TableCell>
|
|
54
|
+
<TableCell header={true} style={{ width: "75%" }}>
|
|
55
|
+
Property
|
|
56
|
+
</TableCell>
|
|
57
|
+
</TableHeader>
|
|
58
|
+
<TableBody>
|
|
59
|
+
{destinationProperties &&
|
|
60
|
+
Object.entries(headersMapping)
|
|
61
|
+
.map(([importKey, mappedKey]) => {
|
|
62
|
+
const propertyKey = headersMapping[importKey];
|
|
63
|
+
const property = mappedKey ? getPropertyInPath(destinationProperties, mappedKey) as Property : null;
|
|
64
|
+
|
|
65
|
+
const originProperty = getPropertyInPath(originProperties, importKey) as Property | undefined;
|
|
66
|
+
const originDataType = originProperty ? (originProperty.dataType === "array" && typeof originProperty.of === "object"
|
|
67
|
+
? `${originProperty.dataType} - ${(originProperty.of as Property).dataType}`
|
|
68
|
+
: originProperty.dataType)
|
|
69
|
+
: undefined;
|
|
70
|
+
return <TableRow key={importKey} style={{ height: "90px" }}>
|
|
71
|
+
<TableCell style={{ width: "20%" }}>
|
|
72
|
+
<Typography variant={"body2"}>{importKey}</Typography>
|
|
73
|
+
{originProperty && <Typography
|
|
74
|
+
variant={"caption"}
|
|
75
|
+
color={"secondary"}
|
|
76
|
+
>{originDataType}</Typography>}
|
|
77
|
+
</TableCell>
|
|
78
|
+
<TableCell>
|
|
79
|
+
<ChevronRightIcon/>
|
|
80
|
+
</TableCell>
|
|
81
|
+
<TableCell className={importKey === idColumn ? "text-center" : undefined}
|
|
82
|
+
style={{ width: "75%" }}>
|
|
83
|
+
{buildPropertyView?.({
|
|
84
|
+
isIdColumn: importKey === idColumn,
|
|
85
|
+
property,
|
|
86
|
+
propertyKey,
|
|
87
|
+
importKey
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
</TableCell>
|
|
91
|
+
</TableRow>;
|
|
92
|
+
}
|
|
93
|
+
)}
|
|
94
|
+
</TableBody>
|
|
95
|
+
</Table>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function IdSelectField({
|
|
101
|
+
idColumn,
|
|
102
|
+
headersMapping,
|
|
103
|
+
onChange
|
|
104
|
+
}: {
|
|
105
|
+
idColumn?: string,
|
|
106
|
+
headersMapping: Record<string, string | null>;
|
|
107
|
+
onChange: (value: string) => void
|
|
108
|
+
}) {
|
|
109
|
+
return <div>
|
|
110
|
+
<Select
|
|
111
|
+
size={"small"}
|
|
112
|
+
value={idColumn ?? ""}
|
|
113
|
+
onChange={(event) => {
|
|
114
|
+
onChange(event.target.value as string);
|
|
115
|
+
}}
|
|
116
|
+
renderValue={(value) => {
|
|
117
|
+
return <Typography variant={"body2"}>
|
|
118
|
+
{value !== "" ? value : "Autogenerate ID"}
|
|
119
|
+
</Typography>;
|
|
120
|
+
}}
|
|
121
|
+
label={"Column that will be used as ID for each document"}>
|
|
122
|
+
<SelectItem value={""}>Autogenerate ID</SelectItem>
|
|
123
|
+
{Object.entries(headersMapping).map(([key, value]) => {
|
|
124
|
+
return <SelectItem key={key} value={key}>{key}</SelectItem>;
|
|
125
|
+
})}
|
|
126
|
+
</Select>
|
|
127
|
+
</div>;
|
|
128
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FileUpload, UploadIcon } from "@firecms/core";
|
|
2
|
+
import { convertFileToJson } from "../utils/file_to_json";
|
|
3
|
+
|
|
4
|
+
export function ImportFileUpload({ onDataAdded }: { onDataAdded: (data: object[]) => void }) {
|
|
5
|
+
return <FileUpload
|
|
6
|
+
accept={{
|
|
7
|
+
"text/*": [".csv", ".xls", ".xlsx"],
|
|
8
|
+
"application/vnd.ms-excel": [".xls", ".xlsx"],
|
|
9
|
+
"application/msexcel": [".xls", ".xlsx"],
|
|
10
|
+
"application/vnd.ms-office": [".xls", ".xlsx"],
|
|
11
|
+
"application/xls": [".xls", ".xlsx"],
|
|
12
|
+
"application/x-xls": [".xls", ".xlsx"],
|
|
13
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [".xls", ".xlsx"],
|
|
14
|
+
"application/json": [".json"],
|
|
15
|
+
}}
|
|
16
|
+
preventDropOnDocument={true}
|
|
17
|
+
size={"small"}
|
|
18
|
+
maxFiles={1}
|
|
19
|
+
uploadDescription={<><UploadIcon/>Drag and drop a file here or click to upload</>}
|
|
20
|
+
onFilesAdded={(files: File[]) => {
|
|
21
|
+
if (files.length > 0) {
|
|
22
|
+
convertFileToJson(files[0])
|
|
23
|
+
.then((jsonData) => {
|
|
24
|
+
onDataAdded(jsonData);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}}/>
|
|
28
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
cn,
|
|
4
|
+
EditIcon,
|
|
5
|
+
ErrorBoundary,
|
|
6
|
+
FieldConfigBadge,
|
|
7
|
+
FunctionsIcon,
|
|
8
|
+
getFieldConfig,
|
|
9
|
+
IconButton,
|
|
10
|
+
Paper,
|
|
11
|
+
Property,
|
|
12
|
+
RemoveCircleIcon,
|
|
13
|
+
TextField,
|
|
14
|
+
Typography, useFireCMSContext
|
|
15
|
+
} from "@firecms/core";
|
|
16
|
+
|
|
17
|
+
export function ImportNewPropertyFieldPreview({
|
|
18
|
+
propertyKey,
|
|
19
|
+
property,
|
|
20
|
+
onEditClick,
|
|
21
|
+
includeName = true,
|
|
22
|
+
onPropertyNameChanged,
|
|
23
|
+
propertyTypeView
|
|
24
|
+
}: {
|
|
25
|
+
propertyKey: string | null,
|
|
26
|
+
property: Property | null
|
|
27
|
+
includeName?: boolean,
|
|
28
|
+
onEditClick?: () => void,
|
|
29
|
+
onPropertyNameChanged?: (propertyKey: string, value: string) => void,
|
|
30
|
+
propertyTypeView?: React.ReactNode
|
|
31
|
+
}) {
|
|
32
|
+
|
|
33
|
+
const { propertyConfigs } = useFireCMSContext();
|
|
34
|
+
const widget = property ? getFieldConfig(property, propertyConfigs) : null;
|
|
35
|
+
|
|
36
|
+
return <ErrorBoundary>
|
|
37
|
+
<div
|
|
38
|
+
className="flex flex-row w-full items-center">
|
|
39
|
+
|
|
40
|
+
<div className={"mx-4"}>
|
|
41
|
+
{propertyTypeView ?? <FieldConfigBadge propertyConfig={widget ?? undefined}/>}
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div className="w-full flex flex-col grow">
|
|
45
|
+
|
|
46
|
+
<div className={"flex flex-row items-center gap-2"}>
|
|
47
|
+
{includeName &&
|
|
48
|
+
<TextField
|
|
49
|
+
size={"small"}
|
|
50
|
+
className={"text-base grow"}
|
|
51
|
+
value={property?.name ?? ""}
|
|
52
|
+
onChange={(e) => {
|
|
53
|
+
if (onPropertyNameChanged && propertyKey)
|
|
54
|
+
onPropertyNameChanged(propertyKey, e.target.value);
|
|
55
|
+
}}/>}
|
|
56
|
+
|
|
57
|
+
<IconButton onClick={onEditClick} size={"small"}>
|
|
58
|
+
<EditIcon size={"small"}/>
|
|
59
|
+
</IconButton>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
</div>
|
|
66
|
+
</ErrorBoundary>
|
|
67
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CenteredView,
|
|
3
|
+
CircularProgress,
|
|
4
|
+
CMSType,
|
|
5
|
+
DataSource,
|
|
6
|
+
Entity,
|
|
7
|
+
EntityCollection,
|
|
8
|
+
Typography,
|
|
9
|
+
useDataSource
|
|
10
|
+
} from "@firecms/core";
|
|
11
|
+
import { useEffect, useRef, useState } from "react";
|
|
12
|
+
import { ImportConfig } from "../types";
|
|
13
|
+
|
|
14
|
+
export function ImportSaveInProgress<M extends { [Key: string]: CMSType }>
|
|
15
|
+
({
|
|
16
|
+
importConfig,
|
|
17
|
+
collection,
|
|
18
|
+
onImportSuccess
|
|
19
|
+
}:
|
|
20
|
+
{
|
|
21
|
+
importConfig: ImportConfig,
|
|
22
|
+
collection: EntityCollection<any>,
|
|
23
|
+
onImportSuccess: (collection: EntityCollection<any>) => void
|
|
24
|
+
}) {
|
|
25
|
+
|
|
26
|
+
const dataSource = useDataSource();
|
|
27
|
+
|
|
28
|
+
const savingRef = useRef<boolean>(false);
|
|
29
|
+
|
|
30
|
+
const [processedEntities, setProcessedEntities] = useState<number>(0);
|
|
31
|
+
|
|
32
|
+
function save() {
|
|
33
|
+
|
|
34
|
+
if (savingRef.current)
|
|
35
|
+
return;
|
|
36
|
+
|
|
37
|
+
savingRef.current = true;
|
|
38
|
+
|
|
39
|
+
saveDataBatch(
|
|
40
|
+
dataSource,
|
|
41
|
+
collection,
|
|
42
|
+
importConfig.entities,
|
|
43
|
+
0,
|
|
44
|
+
25,
|
|
45
|
+
setProcessedEntities
|
|
46
|
+
).then(() => {
|
|
47
|
+
onImportSuccess(collection);
|
|
48
|
+
savingRef.current = false;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
save();
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<CenteredView className={"flex flex-col gap-4 items-center"}>
|
|
58
|
+
<CircularProgress/>
|
|
59
|
+
|
|
60
|
+
<Typography variant={"h6"}>
|
|
61
|
+
Saving data
|
|
62
|
+
</Typography>
|
|
63
|
+
|
|
64
|
+
<Typography variant={"body2"}>
|
|
65
|
+
{processedEntities}/{importConfig.entities.length} entities saved
|
|
66
|
+
</Typography>
|
|
67
|
+
|
|
68
|
+
<Typography variant={"caption"}>
|
|
69
|
+
Do not close this tab or the import will be interrupted.
|
|
70
|
+
</Typography>
|
|
71
|
+
|
|
72
|
+
</CenteredView>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function saveDataBatch(dataSource: DataSource,
|
|
78
|
+
collection: EntityCollection,
|
|
79
|
+
data: Partial<Entity<any>>[],
|
|
80
|
+
offset = 0,
|
|
81
|
+
batchSize = 25,
|
|
82
|
+
onProgressUpdate: (progress: number) => void): Promise<void> {
|
|
83
|
+
|
|
84
|
+
console.debug("Saving imported data", offset, batchSize);
|
|
85
|
+
|
|
86
|
+
const batch = data.slice(offset, offset + batchSize);
|
|
87
|
+
return Promise.all(batch.map(d =>
|
|
88
|
+
dataSource.saveEntity({
|
|
89
|
+
path: collection.path, // TODO: should check if this is correct, specially for subcollections
|
|
90
|
+
values: d.values,
|
|
91
|
+
entityId: d.id,
|
|
92
|
+
collection,
|
|
93
|
+
status: "new"
|
|
94
|
+
})))
|
|
95
|
+
.then(() => {
|
|
96
|
+
if (offset + batchSize < data.length) {
|
|
97
|
+
onProgressUpdate(offset + batchSize);
|
|
98
|
+
return saveDataBatch(dataSource, collection, data, offset + batchSize, batchSize, onProgressUpdate);
|
|
99
|
+
}
|
|
100
|
+
onProgressUpdate(data.length);
|
|
101
|
+
return Promise.resolve();
|
|
102
|
+
});
|
|
103
|
+
}
|