@teselagen/ui 0.6.6 → 0.7.1
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/DataTable/ColumnFilterMenu.d.ts +2 -1
- package/DataTable/Columns.d.ts +51 -0
- package/DataTable/DisplayOptions.d.ts +14 -14
- package/DataTable/EditabelCell.d.ts +2 -5
- package/DataTable/EditableCell.d.ts +7 -0
- package/DataTable/FilterAndSortMenu.d.ts +9 -9
- package/DataTable/PagingTool.d.ts +25 -2
- package/DataTable/RenderCell.d.ts +18 -0
- package/DataTable/SearchBar.d.ts +4 -3
- package/DataTable/SortableColumns.d.ts +6 -9
- package/DataTable/ThComponent.d.ts +9 -0
- package/DataTable/index.d.ts +0 -5
- package/DataTable/utils/getIdOrCodeOrIndex.d.ts +1 -2
- package/DataTable/utils/handleCopyTable.d.ts +1 -0
- package/DataTable/utils/index.d.ts +4 -2
- package/DataTable/utils/primarySelectedValue.d.ts +1 -0
- package/DataTable/utils/queryParams.d.ts +13 -8
- package/DataTable/utils/removeCleanRows.d.ts +1 -1
- package/DataTable/utils/rowClick.d.ts +24 -3
- package/DataTable/utils/useDeepEqualMemo.d.ts +1 -0
- package/DataTable/utils/useTableEntities.d.ts +5 -0
- package/DataTable/utils/useTableParams.d.ts +49 -0
- package/DataTable/utils/withTableParams.d.ts +3 -16
- package/DataTable/viewColumn.d.ts +11 -4
- package/FormComponents/AbstractField.d.ts +1 -0
- package/FormComponents/Uploader.d.ts +34 -1
- package/FormComponents/index.d.ts +111 -60
- package/MatchHeaders.d.ts +9 -10
- package/SimpleStepViz.d.ts +2 -1
- package/TgSuggest/index.d.ts +1 -21
- package/UploadCsvWizard.d.ts +1 -1
- package/index.cjs.js +47861 -49125
- package/index.d.ts +6 -3
- package/index.es.js +47959 -49223
- package/package.json +6 -5
- package/src/DataTable/CellDragHandle.js +70 -69
- package/src/DataTable/ColumnFilterMenu.js +23 -21
- package/src/DataTable/Columns.js +948 -0
- package/src/DataTable/Columns.jsx +945 -0
- package/src/DataTable/DisplayOptions.js +173 -192
- package/src/DataTable/EditabelCell.js +7 -18
- package/src/DataTable/EditabelCell.jsx +44 -0
- package/src/DataTable/EditableCell.js +44 -0
- package/src/DataTable/FilterAndSortMenu.js +215 -234
- package/src/DataTable/PagingTool.js +47 -56
- package/src/DataTable/RenderCell.js +191 -0
- package/src/DataTable/RenderCell.jsx +191 -0
- package/src/DataTable/SearchBar.js +12 -5
- package/src/DataTable/SortableColumns.js +44 -39
- package/src/DataTable/ThComponent.js +44 -0
- package/src/DataTable/dataTableEnhancer.js +32 -295
- package/src/DataTable/index.js +2945 -3596
- package/src/DataTable/utils/getIdOrCodeOrIndex.js +1 -1
- package/src/DataTable/utils/handleCopyTable.js +16 -0
- package/src/DataTable/utils/index.js +7 -3
- package/src/DataTable/utils/primarySelectedValue.js +1 -0
- package/src/DataTable/utils/queryParams.js +110 -85
- package/src/DataTable/utils/removeCleanRows.js +3 -3
- package/src/DataTable/utils/rowClick.js +34 -9
- package/src/DataTable/utils/selection.js +1 -1
- package/src/DataTable/utils/useDeepEqualMemo.js +10 -0
- package/src/DataTable/utils/useTableEntities.js +38 -0
- package/src/DataTable/utils/useTableParams.js +362 -0
- package/src/DataTable/utils/withTableParams.js +244 -274
- package/src/DataTable/validateTableWideErrors.js +1 -1
- package/src/DataTable/viewColumn.js +5 -9
- package/src/DialogFooter/index.js +3 -3
- package/src/FillWindow.js +2 -3
- package/src/FormComponents/AbstractField.js +388 -0
- package/src/FormComponents/Uploader.js +674 -649
- package/src/FormComponents/index.js +505 -654
- package/src/FormComponents/tryToMatchSchemas.js +1 -6
- package/src/MatchHeaders.js +27 -22
- package/src/SimpleStepViz.js +19 -23
- package/src/TgSelect/index.js +1 -1
- package/src/TgSuggest/index.js +94 -106
- package/src/UploadCsvWizard.js +571 -577
- package/src/enhancers/withDialog/tg_modalState.js +1 -0
- package/src/index.js +10 -4
- package/src/showDialogOnDocBody.js +5 -9
- package/src/useDialog.js +25 -26
- package/src/utils/commandControls.js +2 -2
- package/src/utils/handlerHelpers.js +19 -25
- package/src/utils/hooks/index.js +1 -0
- package/src/utils/hooks/useDeepEqualMemo.js +10 -0
- package/src/utils/hooks/useStableReference.js +9 -0
- package/src/utils/renderOnDoc.js +8 -5
- package/src/utils/tagUtils.js +3 -3
- package/src/utils/useTraceUpdate.js +19 -0
- package/src/wrapDialog.js +0 -2
- package/style.css +251 -251
- package/useDialog.d.ts +2 -6
- package/utils/hooks/index.d.ts +1 -0
- package/utils/hooks/useDeepEqualMemo.d.ts +1 -0
- package/utils/hooks/useStableReference.d.ts +1 -0
- package/utils/renderOnDoc.d.ts +1 -1
- package/utils/tagUtils.d.ts +5 -1
- package/utils/useTraceUpdate.d.ts +1 -0
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState
|
|
7
|
+
} from "react";
|
|
2
8
|
import {
|
|
3
9
|
Button,
|
|
4
10
|
Callout,
|
|
@@ -16,8 +22,6 @@ import classnames from "classnames";
|
|
|
16
22
|
import { nanoid } from "nanoid";
|
|
17
23
|
import papaparse, { unparse } from "papaparse";
|
|
18
24
|
import downloadjs from "downloadjs";
|
|
19
|
-
import { configure, makeObservable, observable } from "mobx";
|
|
20
|
-
import { observer } from "mobx-react";
|
|
21
25
|
import UploadCsvWizardDialog, {
|
|
22
26
|
SimpleInsertDataDialog
|
|
23
27
|
} from "../UploadCsvWizard";
|
|
@@ -30,7 +34,7 @@ import {
|
|
|
30
34
|
removeExt
|
|
31
35
|
} from "@teselagen/file-utils";
|
|
32
36
|
import tryToMatchSchemas from "./tryToMatchSchemas";
|
|
33
|
-
import {
|
|
37
|
+
import { isArray, isFunction, isPlainObject, noop } from "lodash-es";
|
|
34
38
|
import { flatMap } from "lodash-es";
|
|
35
39
|
import urljoin from "url-join";
|
|
36
40
|
import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
|
|
@@ -38,14 +42,17 @@ import writeXlsxFile from "write-excel-file";
|
|
|
38
42
|
import { startCase } from "lodash-es";
|
|
39
43
|
import { getNewName } from "./getNewName";
|
|
40
44
|
import { isObject } from "lodash-es";
|
|
41
|
-
import {
|
|
42
|
-
import { initialize } from "redux-form";
|
|
45
|
+
import { change, touch, initialize } from "redux-form";
|
|
43
46
|
import classNames from "classnames";
|
|
44
|
-
import { compose } from "recompose";
|
|
45
47
|
import convertSchema from "../DataTable/utils/convertSchema";
|
|
46
48
|
import { LoadingDots } from "./LoadingDots";
|
|
49
|
+
import { useDispatch } from "react-redux";
|
|
50
|
+
import { flushSync } from "react-dom";
|
|
51
|
+
import { useStableReference } from "../utils/hooks/useStableReference";
|
|
52
|
+
|
|
53
|
+
const manualEnterMessage = "Build CSV File";
|
|
54
|
+
const manualEnterSubMessage = "Paste or type data to build a CSV file";
|
|
47
55
|
|
|
48
|
-
configure({ isolateGlobalState: true });
|
|
49
56
|
const helperText = [
|
|
50
57
|
`How to Use This Template to Upload New Data`,
|
|
51
58
|
`1. Go to the first tab and delete the example data.`,
|
|
@@ -64,99 +71,298 @@ const helperSchema = [
|
|
|
64
71
|
}
|
|
65
72
|
];
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
const setValidateAgainstSchema = newValidateAgainstSchema => {
|
|
75
|
+
if (!newValidateAgainstSchema) return;
|
|
76
|
+
const schema = convertSchema(newValidateAgainstSchema);
|
|
77
|
+
if (
|
|
78
|
+
schema.fields.some(f => {
|
|
79
|
+
if (f.path === "id") {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
})
|
|
84
|
+
) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
|
|
87
|
+
);
|
|
74
88
|
}
|
|
89
|
+
return schema;
|
|
90
|
+
};
|
|
75
91
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
const getFileDownloadAttr = exampleFile => {
|
|
93
|
+
const baseUrl = window?.frontEndConfig?.serverBasePath || "";
|
|
94
|
+
return isFunction(exampleFile)
|
|
95
|
+
? { onClick: exampleFile }
|
|
96
|
+
: exampleFile && {
|
|
97
|
+
target: "_blank",
|
|
98
|
+
download: true,
|
|
99
|
+
href:
|
|
100
|
+
exampleFile.startsWith("https") || exampleFile.startsWith("www")
|
|
101
|
+
? exampleFile
|
|
102
|
+
: baseUrl
|
|
103
|
+
? urljoin(baseUrl, "exampleFiles", exampleFile)
|
|
104
|
+
: exampleFile
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const stripId = (ents = []) =>
|
|
109
|
+
ents.map(ent => {
|
|
110
|
+
const { id, ...rest } = ent;
|
|
111
|
+
return rest;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const getNewCsvFile = (ents, fileName) => {
|
|
115
|
+
const strippedEnts = stripId(ents);
|
|
116
|
+
return {
|
|
117
|
+
newFile: new File([papaparse.unparse(strippedEnts)], fileName),
|
|
118
|
+
cleanedEntities: strippedEnts
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const trimFiles = (incomingFiles, fileLimit) => {
|
|
123
|
+
if (fileLimit) {
|
|
124
|
+
if (incomingFiles.length > fileLimit) {
|
|
125
|
+
window.toastr &&
|
|
126
|
+
window.toastr.warning(
|
|
127
|
+
`Detected additional files in your upload that we are ignoring. You can only upload ${fileLimit} file${
|
|
128
|
+
fileLimit > 1 ? "s" : ""
|
|
129
|
+
} at a time.`
|
|
130
|
+
);
|
|
93
131
|
}
|
|
94
|
-
|
|
95
|
-
this[k] = v;
|
|
96
|
-
});
|
|
132
|
+
return incomingFiles.slice(0, fileLimit);
|
|
97
133
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// autorun(() => {
|
|
101
|
-
// console.log(
|
|
102
|
-
// `validateAgainstSchemaStore?.fields:`,
|
|
103
|
-
// JSON.stringify(validateAgainstSchemaStore?.fields, null, 4)
|
|
104
|
-
// );
|
|
105
|
-
// });
|
|
106
|
-
// validateAgainstSchemaStore.fields = ["hahah"];
|
|
107
|
-
// validateAgainstSchemaStore.fields.push("yaa");
|
|
134
|
+
return incomingFiles;
|
|
135
|
+
};
|
|
108
136
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
const InnerDropZone = ({
|
|
138
|
+
getRootProps,
|
|
139
|
+
getInputProps,
|
|
140
|
+
isDragAccept,
|
|
141
|
+
isDragReject,
|
|
142
|
+
isDragActive,
|
|
143
|
+
className,
|
|
144
|
+
minimal,
|
|
145
|
+
dropzoneDisabled,
|
|
146
|
+
contentOverride,
|
|
147
|
+
simpleAccept,
|
|
148
|
+
innerIcon,
|
|
149
|
+
innerText,
|
|
150
|
+
validateAgainstSchema,
|
|
151
|
+
handleManuallyEnterData,
|
|
152
|
+
noBuildCsvOption,
|
|
153
|
+
showFilesCount,
|
|
154
|
+
fileList
|
|
155
|
+
// isDragActive
|
|
156
|
+
// isDragReject
|
|
157
|
+
// isDragAccept
|
|
158
|
+
}) => (
|
|
159
|
+
<section>
|
|
160
|
+
<div
|
|
161
|
+
{...getRootProps()}
|
|
162
|
+
className={classnames("tg-dropzone", className, {
|
|
163
|
+
"tg-dropzone-minimal": minimal,
|
|
164
|
+
"tg-dropzone-active": isDragActive,
|
|
165
|
+
"tg-dropzone-reject": isDragReject, // tnr: the acceptClassName/rejectClassName doesn't work with file extensions (only mimetypes are supported when dragging). Thus we'll just always turn the drop area blue when dragging and let the filtering occur on drop. See https://github.com/react-dropzone/react-dropzone/issues/888#issuecomment-773938074
|
|
166
|
+
"tg-dropzone-accept": isDragAccept,
|
|
167
|
+
"tg-dropzone-disabled": dropzoneDisabled,
|
|
168
|
+
"bp3-disabled": dropzoneDisabled
|
|
169
|
+
})}
|
|
170
|
+
>
|
|
171
|
+
<input {...getInputProps()} />
|
|
172
|
+
{contentOverride || (
|
|
173
|
+
<div
|
|
174
|
+
title={
|
|
175
|
+
simpleAccept
|
|
176
|
+
? "Accepts only the following file types: " + simpleAccept
|
|
177
|
+
: "Accepts any file input"
|
|
178
|
+
}
|
|
179
|
+
className="tg-upload-inner"
|
|
180
|
+
>
|
|
181
|
+
{innerIcon || <Icon icon="upload" iconSize={minimal ? 15 : 30} />}
|
|
182
|
+
{innerText || (minimal ? "Upload" : "Click or drag to upload")}
|
|
183
|
+
{validateAgainstSchema && !noBuildCsvOption && (
|
|
184
|
+
<div
|
|
185
|
+
style={{
|
|
186
|
+
textAlign: "center",
|
|
187
|
+
// fontSize: 18,
|
|
188
|
+
marginTop: 7,
|
|
189
|
+
marginBottom: 5
|
|
190
|
+
}}
|
|
191
|
+
onClick={handleManuallyEnterData}
|
|
192
|
+
className="link-button"
|
|
193
|
+
>
|
|
194
|
+
...or {manualEnterMessage}
|
|
195
|
+
{/* <div
|
|
196
|
+
style={{
|
|
197
|
+
fontSize: 11,
|
|
198
|
+
color: Colors.GRAY3,
|
|
199
|
+
fontStyle: "italic"
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
{manualEnterSubMessage}
|
|
203
|
+
</div> */}
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
112
209
|
|
|
113
|
-
|
|
210
|
+
{showFilesCount ? (
|
|
211
|
+
<div className="tg-upload-file-list-counter">
|
|
212
|
+
Files: {fileList ? fileList.length : 0}
|
|
213
|
+
</div>
|
|
214
|
+
) : null}
|
|
215
|
+
</section>
|
|
216
|
+
);
|
|
114
217
|
|
|
115
|
-
|
|
116
|
-
|
|
218
|
+
const onFileSuccessDefault = async () => {
|
|
219
|
+
return;
|
|
220
|
+
};
|
|
117
221
|
|
|
118
|
-
|
|
222
|
+
const Uploader = ({
|
|
119
223
|
accept: __accept,
|
|
120
|
-
contentOverride: maybeContentOverride,
|
|
121
|
-
innerIcon,
|
|
122
|
-
innerText,
|
|
123
224
|
action,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
validateAgainstSchema: _validateAgainstSchema,
|
|
225
|
+
autoUnzip,
|
|
226
|
+
beforeUpload,
|
|
127
227
|
callout: _callout,
|
|
228
|
+
className = "",
|
|
229
|
+
contentOverride: maybeContentOverride,
|
|
230
|
+
disabled,
|
|
231
|
+
dropzoneProps = {},
|
|
128
232
|
fileLimit,
|
|
129
|
-
readBeforeUpload, //read the file using the browser's FileReader before passing it to onChange and/or uploading it
|
|
130
|
-
showUploadList = true,
|
|
131
|
-
beforeUpload,
|
|
132
233
|
fileList, //list of files with options: {name, loading, error, url, originalName, downloadName}
|
|
133
|
-
|
|
234
|
+
innerIcon,
|
|
235
|
+
innerText,
|
|
236
|
+
meta: { form: formName } = {},
|
|
237
|
+
minimal,
|
|
238
|
+
name,
|
|
239
|
+
noBuildCsvOption,
|
|
240
|
+
noRedux = true,
|
|
241
|
+
onChange: _onChange = noop, //this is almost always getting passed by redux-form, no need to pass this handler manually
|
|
134
242
|
onFieldSubmit = noop, //called when all files have successfully uploaded
|
|
135
|
-
// fileFinished = noop,
|
|
136
|
-
onRemove = noop, //called when a file has been selected to be removed
|
|
137
|
-
onChange = noop, //this is almost always getting passed by redux-form, no need to pass this handler manually
|
|
138
243
|
onFileClick, // called when a file link in the filelist is clicked
|
|
139
|
-
|
|
244
|
+
onFileSuccess = onFileSuccessDefault, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
|
|
245
|
+
onPreviewClick,
|
|
246
|
+
onRemove = noop, //called when a file has been selected to be removed
|
|
140
247
|
overflowList,
|
|
141
|
-
|
|
142
|
-
disabled: _disabled,
|
|
143
|
-
noBuildCsvOption,
|
|
144
|
-
initializeForm,
|
|
248
|
+
readBeforeUpload, //read the file using the browser's FileReader before passing it to onChange and/or uploading it
|
|
145
249
|
showFilesCount,
|
|
250
|
+
showUploadList = true,
|
|
146
251
|
threeDotMenuItems,
|
|
147
|
-
|
|
148
|
-
}) {
|
|
149
|
-
|
|
150
|
-
let _accept = __accept;
|
|
151
|
-
const validateAgainstSchemaStore = useRef(new ValidateAgainstSchema());
|
|
252
|
+
validateAgainstSchema: _validateAgainstSchema
|
|
253
|
+
}) => {
|
|
254
|
+
const dispatch = useDispatch();
|
|
152
255
|
const [acceptLoading, setAcceptLoading] = useState();
|
|
153
256
|
const [resolvedAccept, setResolvedAccept] = useState();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
257
|
+
const [loading, setLoading] = useState(false);
|
|
258
|
+
const filesToClean = useRef([]);
|
|
259
|
+
|
|
260
|
+
// We do this because we don't want functions influencing on the dependencies
|
|
261
|
+
const stableOnChange = useStableReference(_onChange);
|
|
262
|
+
const stableBeforeUpload = useStableReference(beforeUpload);
|
|
263
|
+
// onChange received from redux-form is not working anymore,
|
|
264
|
+
// so we need to overwrite it for redux to works.
|
|
265
|
+
const onChange = useCallback(
|
|
266
|
+
val => {
|
|
267
|
+
flushSync(() => {
|
|
268
|
+
if (noRedux) {
|
|
269
|
+
return stableOnChange.current(val);
|
|
270
|
+
}
|
|
271
|
+
dispatch(touch(formName, name));
|
|
272
|
+
dispatch(change(formName, name, val));
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
[dispatch, formName, name, noRedux, stableOnChange]
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const handleSecondHalfOfUpload = useCallback(
|
|
279
|
+
async ({ acceptedFiles, cleanedFileList }) => {
|
|
280
|
+
// This onChange is not changing things, we need to check whether the error is here or later
|
|
281
|
+
onChange(cleanedFileList); //tnw: this line is necessary, if you want to clear the file list in the beforeUpload, call onChange([])
|
|
282
|
+
// beforeUpload is called, otherwise beforeUpload will not be able to truly cancel the upload
|
|
283
|
+
const keepGoing = stableBeforeUpload.current
|
|
284
|
+
? await stableBeforeUpload.current(cleanedFileList, onChange)
|
|
285
|
+
: true;
|
|
286
|
+
if (!keepGoing) return;
|
|
287
|
+
|
|
288
|
+
if (action) {
|
|
289
|
+
const responses = [];
|
|
290
|
+
await Promise.all(
|
|
291
|
+
acceptedFiles.map(async fileToUpload => {
|
|
292
|
+
const data = new FormData();
|
|
293
|
+
data.append("file", fileToUpload);
|
|
294
|
+
try {
|
|
295
|
+
const res = await (window.serverApi
|
|
296
|
+
? window.serverApi.post(action, data)
|
|
297
|
+
: fetch(action, {
|
|
298
|
+
method: "POST",
|
|
299
|
+
body: data
|
|
300
|
+
}));
|
|
301
|
+
responses.push(res.data && res.data[0]);
|
|
302
|
+
onFileSuccess(res.data[0]).then(() => {
|
|
303
|
+
cleanedFileList = cleanedFileList.map(file => {
|
|
304
|
+
const fileToReturn = {
|
|
305
|
+
...file,
|
|
306
|
+
...res.data[0]
|
|
307
|
+
};
|
|
308
|
+
if (fileToReturn.id === fileToUpload.id) {
|
|
309
|
+
fileToReturn.loading = false;
|
|
310
|
+
}
|
|
311
|
+
return fileToReturn;
|
|
312
|
+
});
|
|
313
|
+
onChange(cleanedFileList);
|
|
314
|
+
});
|
|
315
|
+
} catch (err) {
|
|
316
|
+
console.error("Error uploading file:", err);
|
|
317
|
+
responses.push({
|
|
318
|
+
...fileToUpload,
|
|
319
|
+
error: err && err.msg ? err.msg : err
|
|
320
|
+
});
|
|
321
|
+
cleanedFileList = cleanedFileList.map(file => {
|
|
322
|
+
const fileToReturn = { ...file };
|
|
323
|
+
if (fileToReturn.id === fileToUpload.id) {
|
|
324
|
+
fileToReturn.loading = false;
|
|
325
|
+
fileToReturn.error = true;
|
|
326
|
+
}
|
|
327
|
+
return fileToReturn;
|
|
328
|
+
});
|
|
329
|
+
onChange(cleanedFileList);
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
);
|
|
333
|
+
onFieldSubmit(responses);
|
|
334
|
+
} else {
|
|
335
|
+
onChange(
|
|
336
|
+
cleanedFileList.map(function (file) {
|
|
337
|
+
return {
|
|
338
|
+
...file,
|
|
339
|
+
loading: false
|
|
340
|
+
};
|
|
341
|
+
})
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
setLoading(false);
|
|
345
|
+
},
|
|
346
|
+
[action, stableBeforeUpload, onChange, onFieldSubmit, onFileSuccess]
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const isAcceptPromise = useMemo(
|
|
350
|
+
() =>
|
|
351
|
+
__accept?.then ||
|
|
352
|
+
(Array.isArray(__accept) ? __accept.some(acc => acc?.then) : false),
|
|
353
|
+
[__accept]
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const _accept = useMemo(() => {
|
|
357
|
+
if (resolvedAccept) {
|
|
358
|
+
return resolvedAccept;
|
|
359
|
+
}
|
|
360
|
+
if (isAcceptPromise && !resolvedAccept) {
|
|
361
|
+
return [];
|
|
362
|
+
}
|
|
363
|
+
return __accept;
|
|
364
|
+
}, [__accept, isAcceptPromise, resolvedAccept]);
|
|
365
|
+
|
|
160
366
|
useEffect(() => {
|
|
161
367
|
if (isAcceptPromise) {
|
|
162
368
|
setAcceptLoading(true);
|
|
@@ -169,35 +375,34 @@ function UploaderInner({
|
|
|
169
375
|
);
|
|
170
376
|
}
|
|
171
377
|
}, [__accept, isAcceptPromise]);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
378
|
+
|
|
379
|
+
let dropzoneDisabled = disabled;
|
|
175
380
|
if (acceptLoading) dropzoneDisabled = true;
|
|
176
|
-
const accept = !_accept
|
|
177
|
-
? undefined
|
|
178
|
-
: isAcceptPromise && !resolvedAccept
|
|
179
|
-
? []
|
|
180
|
-
: isPlainObject(_accept)
|
|
181
|
-
? [_accept]
|
|
182
|
-
: isArray(_accept)
|
|
183
|
-
? _accept
|
|
184
|
-
: _accept.split(",").map(a => ({ type: a }));
|
|
185
|
-
const callout = _callout || accept?.find?.(a => a?.callout)?.callout;
|
|
186
381
|
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
382
|
+
const accept = useMemo(
|
|
383
|
+
() =>
|
|
384
|
+
!_accept
|
|
385
|
+
? undefined
|
|
386
|
+
: isAcceptPromise && !resolvedAccept
|
|
387
|
+
? []
|
|
388
|
+
: isPlainObject(_accept)
|
|
389
|
+
? [_accept]
|
|
390
|
+
: isArray(_accept)
|
|
391
|
+
? _accept
|
|
392
|
+
: _accept.split(",").map(acc => ({ type: acc })),
|
|
393
|
+
[_accept, isAcceptPromise, resolvedAccept]
|
|
394
|
+
);
|
|
190
395
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
396
|
+
const callout = _callout || accept?.find?.(a => a?.callout)?.callout;
|
|
397
|
+
|
|
398
|
+
const validateAgainstSchema = useMemo(
|
|
399
|
+
() =>
|
|
400
|
+
setValidateAgainstSchema(
|
|
401
|
+
_validateAgainstSchema ||
|
|
402
|
+
accept?.find?.(a => a?.validateAgainstSchema)?.validateAgainstSchema
|
|
403
|
+
),
|
|
404
|
+
[_validateAgainstSchema, accept]
|
|
405
|
+
);
|
|
201
406
|
|
|
202
407
|
if (
|
|
203
408
|
(validateAgainstSchema || autoUnzip) &&
|
|
@@ -209,13 +414,12 @@ function UploaderInner({
|
|
|
209
414
|
description: "Any of the following types, just compressed"
|
|
210
415
|
});
|
|
211
416
|
}
|
|
212
|
-
const [loading, setLoading] = useState(false);
|
|
213
|
-
const filesToClean = useRef([]);
|
|
214
417
|
|
|
215
|
-
const { showDialogPromise: showUploadCsvWizardDialog,
|
|
418
|
+
const { showDialogPromise: showUploadCsvWizardDialog, Comp } = useDialog({
|
|
216
419
|
ModalComponent: UploadCsvWizardDialog
|
|
217
420
|
});
|
|
218
|
-
|
|
421
|
+
|
|
422
|
+
const { showDialogPromise: showSimpleInsertDataDialog, Comp: Comp2 } =
|
|
219
423
|
useDialog({
|
|
220
424
|
ModalComponent: SimpleInsertDataDialog
|
|
221
425
|
});
|
|
@@ -238,13 +442,13 @@ function UploaderInner({
|
|
|
238
442
|
let advancedAccept;
|
|
239
443
|
|
|
240
444
|
if (Array.isArray(accept)) {
|
|
241
|
-
if (accept.some(
|
|
445
|
+
if (accept.some(acc => isPlainObject(acc))) {
|
|
242
446
|
//advanced accept
|
|
243
447
|
advancedAccept = accept;
|
|
244
|
-
simpleAccept = flatMap(accept,
|
|
245
|
-
if (
|
|
246
|
-
if (!
|
|
247
|
-
|
|
448
|
+
simpleAccept = flatMap(accept, acc => {
|
|
449
|
+
if (acc.validateAgainstSchema) {
|
|
450
|
+
if (!acc.type) {
|
|
451
|
+
acc.type = [".csv", ".xlsx"];
|
|
248
452
|
}
|
|
249
453
|
handleManuallyEnterData = async e => {
|
|
250
454
|
e.stopPropagation();
|
|
@@ -254,41 +458,38 @@ function UploaderInner({
|
|
|
254
458
|
validateAgainstSchema
|
|
255
459
|
}
|
|
256
460
|
);
|
|
257
|
-
if (!newEntities)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
newFileName
|
|
266
|
-
);
|
|
461
|
+
if (!newEntities) return;
|
|
462
|
+
//check existing files to make sure the new file name gets incremented if necessary
|
|
463
|
+
// fileList
|
|
464
|
+
const newFileName = getNewName(fileListToUse, fileName);
|
|
465
|
+
const { newFile, cleanedEntities } = getNewCsvFile(
|
|
466
|
+
newEntities,
|
|
467
|
+
newFileName
|
|
468
|
+
);
|
|
267
469
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
470
|
+
const file = {
|
|
471
|
+
...newFile,
|
|
472
|
+
parsedData: cleanedEntities,
|
|
473
|
+
meta: {
|
|
474
|
+
fields: validateAgainstSchema.fields.map(({ path }) => path)
|
|
475
|
+
},
|
|
476
|
+
name: newFileName,
|
|
477
|
+
originFileObj: newFile,
|
|
478
|
+
originalFileObj: newFile,
|
|
479
|
+
id: nanoid(),
|
|
480
|
+
hasEditClick: true
|
|
481
|
+
};
|
|
280
482
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
483
|
+
const cleanedFileList = [file, ...fileListToUse].slice(
|
|
484
|
+
0,
|
|
485
|
+
fileLimit ? fileLimit : undefined
|
|
486
|
+
);
|
|
487
|
+
handleSecondHalfOfUpload({
|
|
488
|
+
acceptedFiles: cleanedFileList,
|
|
489
|
+
cleanedFileList
|
|
490
|
+
});
|
|
289
491
|
|
|
290
|
-
|
|
291
|
-
}
|
|
492
|
+
window.toastr.success(`File Added`);
|
|
292
493
|
};
|
|
293
494
|
|
|
294
495
|
const nameToUse =
|
|
@@ -338,7 +539,7 @@ function UploaderInner({
|
|
|
338
539
|
}
|
|
339
540
|
};
|
|
340
541
|
});
|
|
341
|
-
const
|
|
542
|
+
const blobFile = await writeXlsxFile(
|
|
342
543
|
[[mainExampleData], fieldsToUse, helperText],
|
|
343
544
|
{
|
|
344
545
|
headerStyle: {
|
|
@@ -349,18 +550,18 @@ function UploaderInner({
|
|
|
349
550
|
filePath: "file.xlsx"
|
|
350
551
|
}
|
|
351
552
|
);
|
|
352
|
-
downloadjs(
|
|
553
|
+
downloadjs(blobFile, `${nameToUse}.xlsx`, "xlsx");
|
|
353
554
|
};
|
|
354
555
|
// handleDownloadXlsxFile()
|
|
355
|
-
|
|
556
|
+
acc.exampleFiles = [
|
|
356
557
|
// ...(a.exampleFile ? [a.exampleFile] : []),
|
|
357
558
|
{
|
|
358
559
|
description: "Download Example CSV File",
|
|
359
560
|
exampleFile: () => {
|
|
360
561
|
const rows = [];
|
|
361
562
|
const schemaToUse = [
|
|
362
|
-
...
|
|
363
|
-
...(
|
|
563
|
+
...acc.validateAgainstSchema.fields,
|
|
564
|
+
...(acc.validateAgainstSchema.exampleDownloadFields ?? [])
|
|
364
565
|
];
|
|
365
566
|
rows.push(
|
|
366
567
|
schemaToUse.map(f => {
|
|
@@ -394,10 +595,10 @@ function UploaderInner({
|
|
|
394
595
|
}
|
|
395
596
|
])
|
|
396
597
|
];
|
|
397
|
-
delete
|
|
598
|
+
delete acc.exampleFile;
|
|
398
599
|
}
|
|
399
|
-
if (
|
|
400
|
-
return
|
|
600
|
+
if (acc.type) return acc.type;
|
|
601
|
+
return acc;
|
|
401
602
|
});
|
|
402
603
|
simpleAccept = simpleAccept.join(", ");
|
|
403
604
|
} else {
|
|
@@ -409,73 +610,6 @@ function UploaderInner({
|
|
|
409
610
|
|
|
410
611
|
const fileListToUse = fileList ? fileList : [];
|
|
411
612
|
|
|
412
|
-
async function handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList }) {
|
|
413
|
-
onChange(cleanedFileList); //tnw: this line is necessary, if you want to clear the file list in the beforeUpload, call onChange([])
|
|
414
|
-
// beforeUpload is called, otherwise beforeUpload will not be able to truly cancel the upload
|
|
415
|
-
const keepGoing = beforeUpload
|
|
416
|
-
? await beforeUpload(cleanedFileList, onChange)
|
|
417
|
-
: true;
|
|
418
|
-
if (!keepGoing) return;
|
|
419
|
-
|
|
420
|
-
if (action) {
|
|
421
|
-
const responses = [];
|
|
422
|
-
await Promise.all(
|
|
423
|
-
acceptedFiles.map(async fileToUpload => {
|
|
424
|
-
const data = new FormData();
|
|
425
|
-
data.append("file", fileToUpload);
|
|
426
|
-
try {
|
|
427
|
-
const res = await (window.serverApi
|
|
428
|
-
? window.serverApi.post(action, data)
|
|
429
|
-
: fetch(action, {
|
|
430
|
-
method: "POST",
|
|
431
|
-
body: data
|
|
432
|
-
}));
|
|
433
|
-
responses.push(res.data && res.data[0]);
|
|
434
|
-
onFileSuccess(res.data[0]).then(() => {
|
|
435
|
-
cleanedFileList = cleanedFileList.map(file => {
|
|
436
|
-
const fileToReturn = {
|
|
437
|
-
...file,
|
|
438
|
-
...res.data[0]
|
|
439
|
-
};
|
|
440
|
-
if (fileToReturn.id === fileToUpload.id) {
|
|
441
|
-
fileToReturn.loading = false;
|
|
442
|
-
}
|
|
443
|
-
return fileToReturn;
|
|
444
|
-
});
|
|
445
|
-
onChange(cleanedFileList);
|
|
446
|
-
});
|
|
447
|
-
} catch (err) {
|
|
448
|
-
console.error("Error uploading file:", err);
|
|
449
|
-
responses.push({
|
|
450
|
-
...fileToUpload,
|
|
451
|
-
error: err && err.msg ? err.msg : err
|
|
452
|
-
});
|
|
453
|
-
cleanedFileList = cleanedFileList.map(file => {
|
|
454
|
-
const fileToReturn = { ...file };
|
|
455
|
-
if (fileToReturn.id === fileToUpload.id) {
|
|
456
|
-
fileToReturn.loading = false;
|
|
457
|
-
fileToReturn.error = true;
|
|
458
|
-
}
|
|
459
|
-
return fileToReturn;
|
|
460
|
-
});
|
|
461
|
-
onChange(cleanedFileList);
|
|
462
|
-
}
|
|
463
|
-
})
|
|
464
|
-
);
|
|
465
|
-
onFieldSubmit(responses);
|
|
466
|
-
} else {
|
|
467
|
-
onChange(
|
|
468
|
-
cleanedFileList.map(function (file) {
|
|
469
|
-
return {
|
|
470
|
-
...file,
|
|
471
|
-
loading: false
|
|
472
|
-
};
|
|
473
|
-
})
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
setLoading(false);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
613
|
return (
|
|
480
614
|
<>
|
|
481
615
|
{callout && (
|
|
@@ -491,8 +625,8 @@ function UploaderInner({
|
|
|
491
625
|
height: "fit-content"
|
|
492
626
|
}}
|
|
493
627
|
>
|
|
494
|
-
|
|
495
|
-
|
|
628
|
+
<Comp />
|
|
629
|
+
<Comp2 />
|
|
496
630
|
<div
|
|
497
631
|
className="tg-uploader-inner"
|
|
498
632
|
style={{ width: "100%", height: "fit-content", minWidth: 0 }}
|
|
@@ -503,17 +637,17 @@ function UploaderInner({
|
|
|
503
637
|
style={{ fontSize: 11, marginBottom: 5 }}
|
|
504
638
|
>
|
|
505
639
|
{advancedAccept && !acceptLoading ? (
|
|
506
|
-
<div
|
|
640
|
+
<div>
|
|
507
641
|
Accepts
|
|
508
|
-
<span
|
|
509
|
-
{advancedAccept.map((
|
|
642
|
+
<span>
|
|
643
|
+
{advancedAccept.map((acc, i) => {
|
|
510
644
|
const disabled = !(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
645
|
+
acc.description ||
|
|
646
|
+
acc.exampleFile ||
|
|
647
|
+
acc.exampleFiles
|
|
514
648
|
);
|
|
515
|
-
const PopOrTooltip =
|
|
516
|
-
const hasDownload =
|
|
649
|
+
const PopOrTooltip = acc.exampleFiles ? Popover : Tooltip;
|
|
650
|
+
const hasDownload = acc.exampleFile || acc.exampleFiles;
|
|
517
651
|
const CustomTag = !hasDownload ? "span" : "a";
|
|
518
652
|
return (
|
|
519
653
|
<PopOrTooltip
|
|
@@ -522,40 +656,38 @@ function UploaderInner({
|
|
|
522
656
|
disabled={disabled}
|
|
523
657
|
modifiers={popoverOverflowModifiers}
|
|
524
658
|
content={
|
|
525
|
-
|
|
659
|
+
acc.exampleFiles ? (
|
|
526
660
|
<Menu>
|
|
527
|
-
{
|
|
661
|
+
{acc.exampleFiles.map(
|
|
528
662
|
(
|
|
529
663
|
{ description, subtext, exampleFile, icon },
|
|
530
664
|
i
|
|
531
|
-
) =>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
<div>
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
);
|
|
558
|
-
}
|
|
665
|
+
) => (
|
|
666
|
+
<MenuItem
|
|
667
|
+
icon={icon || "download"}
|
|
668
|
+
intent="primary"
|
|
669
|
+
text={
|
|
670
|
+
subtext ? (
|
|
671
|
+
<div>
|
|
672
|
+
<div>{description}</div>
|
|
673
|
+
<div
|
|
674
|
+
style={{
|
|
675
|
+
fontSize: 11,
|
|
676
|
+
fontStyle: "italic",
|
|
677
|
+
color: Colors.GRAY3
|
|
678
|
+
}}
|
|
679
|
+
>
|
|
680
|
+
{subtext}
|
|
681
|
+
</div>{" "}
|
|
682
|
+
</div>
|
|
683
|
+
) : (
|
|
684
|
+
description
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
{...getFileDownloadAttr(exampleFile)}
|
|
688
|
+
key={i}
|
|
689
|
+
/>
|
|
690
|
+
)
|
|
559
691
|
)}
|
|
560
692
|
</Menu>
|
|
561
693
|
) : (
|
|
@@ -565,20 +697,20 @@ function UploaderInner({
|
|
|
565
697
|
wordBreak: "break-word"
|
|
566
698
|
}}
|
|
567
699
|
>
|
|
568
|
-
{
|
|
700
|
+
{acc.description ? (
|
|
569
701
|
<div
|
|
570
702
|
style={{
|
|
571
703
|
marginBottom: 4,
|
|
572
704
|
fontStyle: "italic"
|
|
573
705
|
}}
|
|
574
706
|
>
|
|
575
|
-
{
|
|
707
|
+
{acc.description}
|
|
576
708
|
</div>
|
|
577
709
|
) : (
|
|
578
710
|
""
|
|
579
711
|
)}
|
|
580
|
-
{
|
|
581
|
-
(
|
|
712
|
+
{acc.exampleFile &&
|
|
713
|
+
(acc.isTemplate
|
|
582
714
|
? "Download Example Template"
|
|
583
715
|
: "Download Example File")}
|
|
584
716
|
</div>
|
|
@@ -588,20 +720,20 @@ function UploaderInner({
|
|
|
588
720
|
<CustomTag
|
|
589
721
|
className="tgFileTypeDescriptor"
|
|
590
722
|
style={{ marginRight: 10, cursor: "pointer" }}
|
|
591
|
-
{...getFileDownloadAttr(
|
|
723
|
+
{...getFileDownloadAttr(acc.exampleFile)}
|
|
592
724
|
>
|
|
593
|
-
{(
|
|
594
|
-
? isArray(
|
|
595
|
-
?
|
|
596
|
-
: [
|
|
597
|
-
: [
|
|
725
|
+
{(acc.type
|
|
726
|
+
? isArray(acc.type)
|
|
727
|
+
? acc.type
|
|
728
|
+
: [acc.type]
|
|
729
|
+
: [acc]
|
|
598
730
|
)
|
|
599
731
|
.map(t => {
|
|
600
732
|
if (!t.startsWith) {
|
|
601
|
-
console.error(`Missing type here:`,
|
|
733
|
+
console.error(`Missing type here:`, acc);
|
|
602
734
|
throw new Error(
|
|
603
735
|
`Missing "type" here: ${JSON.stringify(
|
|
604
|
-
|
|
736
|
+
acc,
|
|
605
737
|
null,
|
|
606
738
|
4
|
|
607
739
|
)}`
|
|
@@ -619,7 +751,7 @@ function UploaderInner({
|
|
|
619
751
|
}}
|
|
620
752
|
size={10}
|
|
621
753
|
icon="download"
|
|
622
|
-
|
|
754
|
+
/>
|
|
623
755
|
)}
|
|
624
756
|
</CustomTag>
|
|
625
757
|
</PopOrTooltip>
|
|
@@ -631,7 +763,8 @@ function UploaderInner({
|
|
|
631
763
|
// make the dots below "load"
|
|
632
764
|
|
|
633
765
|
<>
|
|
634
|
-
Accept Loading
|
|
766
|
+
Accept Loading
|
|
767
|
+
<LoadingDots />
|
|
635
768
|
</>
|
|
636
769
|
) : (
|
|
637
770
|
<>Accepts {simpleAccept}</>
|
|
@@ -646,139 +779,140 @@ function UploaderInner({
|
|
|
646
779
|
simpleAccept
|
|
647
780
|
? simpleAccept
|
|
648
781
|
.split(", ")
|
|
649
|
-
.map(
|
|
782
|
+
.map(acc => (acc.startsWith(".") ? acc : "." + acc))
|
|
650
783
|
.join(", ")
|
|
651
784
|
: undefined
|
|
652
785
|
}
|
|
653
|
-
{
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
cleanupFiles();
|
|
670
|
-
if (rejectedFiles.length) {
|
|
671
|
-
let msg = "";
|
|
672
|
-
rejectedFiles.forEach(file => {
|
|
673
|
-
if (msg) msg += "\n";
|
|
674
|
-
msg +=
|
|
675
|
-
`${file.file.name}: ` +
|
|
676
|
-
file.errors.map(err => err.message).join(", ");
|
|
677
|
-
});
|
|
678
|
-
window.toastr &&
|
|
679
|
-
window.toastr.warning(
|
|
680
|
-
<div className="preserve-newline">{msg}</div>
|
|
681
|
-
);
|
|
786
|
+
onDrop={async (_acceptedFiles, rejectedFiles) => {
|
|
787
|
+
let acceptedFiles = [];
|
|
788
|
+
for (const file of _acceptedFiles) {
|
|
789
|
+
if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
|
|
790
|
+
const files = await filterFilesInZip(
|
|
791
|
+
file,
|
|
792
|
+
simpleAccept
|
|
793
|
+
?.split(", ")
|
|
794
|
+
?.map(acc => (acc.startsWith(".") ? acc : "." + acc)) ||
|
|
795
|
+
[]
|
|
796
|
+
);
|
|
797
|
+
acceptedFiles.push(...files.map(f => f.originFileObj));
|
|
798
|
+
} else {
|
|
799
|
+
acceptedFiles.push(file);
|
|
682
800
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
file.
|
|
692
|
-
}
|
|
693
|
-
filesToClean.current.push(file);
|
|
801
|
+
}
|
|
802
|
+
cleanupFiles();
|
|
803
|
+
if (rejectedFiles.length) {
|
|
804
|
+
let msg = "";
|
|
805
|
+
rejectedFiles.forEach(file => {
|
|
806
|
+
if (msg) msg += "\n";
|
|
807
|
+
msg +=
|
|
808
|
+
`${file.file.name}: ` +
|
|
809
|
+
file.errors.map(err => err.message).join(", ");
|
|
694
810
|
});
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
acceptedFiles.map(file => {
|
|
699
|
-
return new Promise((resolve, reject) => {
|
|
700
|
-
const reader = new FileReader();
|
|
701
|
-
reader.readAsText(file, "UTF-8");
|
|
702
|
-
reader.onload = evt => {
|
|
703
|
-
file.parsedString = evt.target.result;
|
|
704
|
-
resolve(file);
|
|
705
|
-
};
|
|
706
|
-
reader.onerror = err => {
|
|
707
|
-
console.error("err:", err);
|
|
708
|
-
reject(err);
|
|
709
|
-
};
|
|
710
|
-
});
|
|
711
|
-
})
|
|
811
|
+
window.toastr &&
|
|
812
|
+
window.toastr.warning(
|
|
813
|
+
<div className="preserve-newline">{msg}</div>
|
|
712
814
|
);
|
|
815
|
+
}
|
|
816
|
+
if (!acceptedFiles.length) return;
|
|
817
|
+
setLoading(true);
|
|
818
|
+
acceptedFiles = trimFiles(acceptedFiles, fileLimit);
|
|
819
|
+
|
|
820
|
+
acceptedFiles.forEach(file => {
|
|
821
|
+
file.preview = URL.createObjectURL(file);
|
|
822
|
+
file.loading = true;
|
|
823
|
+
if (!file.id) {
|
|
824
|
+
file.id = nanoid();
|
|
713
825
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
originFileObj: file,
|
|
717
|
-
originalFileObj: file,
|
|
718
|
-
id: file.id,
|
|
719
|
-
lastModified: file.lastModified,
|
|
720
|
-
lastModifiedDate: file.lastModifiedDate,
|
|
721
|
-
loading: file.loading,
|
|
722
|
-
name: file.name,
|
|
723
|
-
preview: file.preview,
|
|
724
|
-
size: file.size,
|
|
725
|
-
type: file.type,
|
|
726
|
-
...(file.parsedString
|
|
727
|
-
? { parsedString: file.parsedString }
|
|
728
|
-
: {})
|
|
729
|
-
};
|
|
730
|
-
});
|
|
826
|
+
filesToClean.current.push(file);
|
|
827
|
+
});
|
|
731
828
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
829
|
+
if (readBeforeUpload) {
|
|
830
|
+
acceptedFiles = await Promise.all(
|
|
831
|
+
acceptedFiles.map(file => {
|
|
832
|
+
return new Promise((resolve, reject) => {
|
|
833
|
+
const reader = new FileReader();
|
|
834
|
+
reader.readAsText(file, "UTF-8");
|
|
835
|
+
reader.onload = evt => {
|
|
836
|
+
file.parsedString = evt.target.result;
|
|
837
|
+
resolve(file);
|
|
838
|
+
};
|
|
839
|
+
reader.onerror = err => {
|
|
840
|
+
console.error("err:", err);
|
|
841
|
+
reject(err);
|
|
842
|
+
};
|
|
843
|
+
});
|
|
844
|
+
})
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
const cleanedAccepted = acceptedFiles.map(file => {
|
|
848
|
+
return {
|
|
849
|
+
originFileObj: file,
|
|
850
|
+
originalFileObj: file,
|
|
851
|
+
id: file.id,
|
|
852
|
+
lastModified: file.lastModified,
|
|
853
|
+
lastModifiedDate: file.lastModifiedDate,
|
|
854
|
+
loading: file.loading,
|
|
855
|
+
name: file.name,
|
|
856
|
+
preview: file.preview,
|
|
857
|
+
size: file.size,
|
|
858
|
+
type: file.type,
|
|
859
|
+
...(file.parsedString
|
|
860
|
+
? { parsedString: file.parsedString }
|
|
861
|
+
: {})
|
|
862
|
+
};
|
|
863
|
+
});
|
|
759
864
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
865
|
+
const toKeep = [];
|
|
866
|
+
if (validateAgainstSchema) {
|
|
867
|
+
const filesWIssues = [];
|
|
868
|
+
const filesWOIssues = [];
|
|
869
|
+
for (const [i, file] of cleanedAccepted.entries()) {
|
|
870
|
+
if (isCsvOrExcelFile(file)) {
|
|
871
|
+
let parsedF;
|
|
872
|
+
try {
|
|
873
|
+
parsedF = await parseCsvOrExcelFile(file, {
|
|
874
|
+
csvParserOptions: isFunction(
|
|
875
|
+
validateAgainstSchema.csvParserOptions
|
|
876
|
+
)
|
|
877
|
+
? validateAgainstSchema.csvParserOptions({
|
|
878
|
+
validateAgainstSchema
|
|
879
|
+
})
|
|
880
|
+
: validateAgainstSchema.csvParserOptions
|
|
769
881
|
});
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
882
|
+
} catch (error) {
|
|
883
|
+
console.error("error:", error);
|
|
884
|
+
window.toastr &&
|
|
885
|
+
window.toastr.error(
|
|
886
|
+
`There was an error parsing your file. Please try again. ${
|
|
887
|
+
error.message || error
|
|
888
|
+
}`
|
|
775
889
|
);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const {
|
|
894
|
+
csvValidationIssue: _csvValidationIssue,
|
|
895
|
+
matchedHeaders,
|
|
896
|
+
userSchema,
|
|
897
|
+
searchResults,
|
|
898
|
+
ignoredHeadersMsg
|
|
899
|
+
} = await tryToMatchSchemas({
|
|
900
|
+
incomingData: parsedF.data,
|
|
901
|
+
validateAgainstSchema
|
|
902
|
+
});
|
|
903
|
+
if (userSchema?.userData?.length === 0) {
|
|
904
|
+
console.error(
|
|
905
|
+
`userSchema, parsedF.data:`,
|
|
906
|
+
userSchema,
|
|
907
|
+
parsedF.data
|
|
908
|
+
);
|
|
909
|
+
} else {
|
|
910
|
+
toKeep.push(file);
|
|
911
|
+
let csvValidationIssue = _csvValidationIssue;
|
|
912
|
+
if (csvValidationIssue) {
|
|
913
|
+
if (isObject(csvValidationIssue)) {
|
|
914
|
+
dispatch(
|
|
915
|
+
initialize(
|
|
782
916
|
`editableCellTable${
|
|
783
917
|
cleanedAccepted.length > 1 ? `-${i}` : ""
|
|
784
918
|
}`,
|
|
@@ -790,149 +924,141 @@ function UploaderInner({
|
|
|
790
924
|
keepValues: true,
|
|
791
925
|
updateUnregisteredFields: true
|
|
792
926
|
}
|
|
927
|
+
)
|
|
928
|
+
);
|
|
929
|
+
const err = Object.values(csvValidationIssue)[0];
|
|
930
|
+
// csvValidationIssue = `It looks like there was an error with your data - \n\n${
|
|
931
|
+
// err && err.message ? err.message : err
|
|
932
|
+
// }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
|
|
933
|
+
const errMsg = err && err.message ? err.message : err;
|
|
934
|
+
if (isPlainObject(errMsg)) {
|
|
935
|
+
throw new Error(
|
|
936
|
+
`errMsg is an object ${JSON.stringify(
|
|
937
|
+
errMsg,
|
|
938
|
+
null,
|
|
939
|
+
4
|
|
940
|
+
)}`
|
|
793
941
|
);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
// }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
|
|
798
|
-
const errMsg =
|
|
799
|
-
err && err.message ? err.message : err;
|
|
800
|
-
if (isPlainObject(errMsg)) {
|
|
801
|
-
throw new Error(
|
|
802
|
-
`errMsg is an object ${JSON.stringify(
|
|
803
|
-
errMsg,
|
|
804
|
-
null,
|
|
805
|
-
4
|
|
806
|
-
)}`
|
|
807
|
-
);
|
|
808
|
-
}
|
|
809
|
-
csvValidationIssue = (
|
|
942
|
+
}
|
|
943
|
+
csvValidationIssue = (
|
|
944
|
+
<div>
|
|
810
945
|
<div>
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
data (Correct on the Review Data page):
|
|
814
|
-
</div>
|
|
815
|
-
<div style={{ color: "red" }}>{errMsg}</div>
|
|
816
|
-
<div>
|
|
817
|
-
Please review your headers and then correct
|
|
818
|
-
any errors on the next page.
|
|
819
|
-
</div>
|
|
946
|
+
It looks like there was an error with your data
|
|
947
|
+
(Correct on the Review Data page):
|
|
820
948
|
</div>
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
matchedHeaders,
|
|
828
|
-
userSchema,
|
|
829
|
-
searchResults
|
|
830
|
-
});
|
|
831
|
-
} else {
|
|
832
|
-
filesWOIssues.push({
|
|
833
|
-
file,
|
|
834
|
-
csvValidationIssue,
|
|
835
|
-
ignoredHeadersMsg,
|
|
836
|
-
matchedHeaders,
|
|
837
|
-
userSchema,
|
|
838
|
-
searchResults
|
|
839
|
-
});
|
|
840
|
-
const newFileName = removeExt(file.name) + `.csv`;
|
|
841
|
-
|
|
842
|
-
const { newFile, cleanedEntities } = getNewCsvFile(
|
|
843
|
-
userSchema.userData,
|
|
844
|
-
newFileName
|
|
949
|
+
<div style={{ color: "red" }}>{errMsg}</div>
|
|
950
|
+
<div>
|
|
951
|
+
Please review your headers and then correct any
|
|
952
|
+
errors on the next page.
|
|
953
|
+
</div>
|
|
954
|
+
</div>
|
|
845
955
|
);
|
|
846
|
-
|
|
847
|
-
file.meta = parsedF.meta;
|
|
848
|
-
file.hasEditClick = true;
|
|
849
|
-
file.parsedData = cleanedEntities;
|
|
850
|
-
file.name = newFileName;
|
|
851
|
-
file.originFileObj = newFile;
|
|
852
|
-
file.originalFileObj = newFile;
|
|
853
956
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const allFiles = [...filesWIssues, ...filesWOIssues];
|
|
862
|
-
const doAllFilesHaveSameHeaders = allFiles.every(f => {
|
|
863
|
-
if (f.userSchema.fields && f.userSchema.fields.length) {
|
|
864
|
-
return f.userSchema.fields.every((h, i) => {
|
|
865
|
-
return (
|
|
866
|
-
h.path === allFiles[0].userSchema.fields[i].path
|
|
867
|
-
);
|
|
957
|
+
filesWIssues.push({
|
|
958
|
+
file,
|
|
959
|
+
csvValidationIssue,
|
|
960
|
+
ignoredHeadersMsg,
|
|
961
|
+
matchedHeaders,
|
|
962
|
+
userSchema,
|
|
963
|
+
searchResults
|
|
868
964
|
});
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
? ""
|
|
880
|
-
: file.name
|
|
881
|
-
? `"${file.name}"`
|
|
882
|
-
: ""
|
|
883
|
-
}`
|
|
884
|
-
},
|
|
885
|
-
doAllFilesHaveSameHeaders,
|
|
886
|
-
filesWIssues: allFiles,
|
|
887
|
-
validateAgainstSchema
|
|
888
|
-
}
|
|
889
|
-
);
|
|
965
|
+
} else {
|
|
966
|
+
filesWOIssues.push({
|
|
967
|
+
file,
|
|
968
|
+
csvValidationIssue,
|
|
969
|
+
ignoredHeadersMsg,
|
|
970
|
+
matchedHeaders,
|
|
971
|
+
userSchema,
|
|
972
|
+
searchResults
|
|
973
|
+
});
|
|
974
|
+
const newFileName = removeExt(file.name) + `.csv`;
|
|
890
975
|
|
|
891
|
-
if (!res) {
|
|
892
|
-
window.toastr.warning(`File Upload Aborted`);
|
|
893
|
-
return;
|
|
894
|
-
} else {
|
|
895
|
-
allFiles.forEach(({ file }, i) => {
|
|
896
|
-
const newEntities = res[i];
|
|
897
|
-
// const newFileName = removeExt(file.name) + `_updated.csv`;
|
|
898
|
-
//swap out file with a new csv file
|
|
899
976
|
const { newFile, cleanedEntities } = getNewCsvFile(
|
|
900
|
-
|
|
901
|
-
|
|
977
|
+
userSchema.userData,
|
|
978
|
+
newFileName
|
|
902
979
|
);
|
|
903
980
|
|
|
981
|
+
file.meta = parsedF.meta;
|
|
904
982
|
file.hasEditClick = true;
|
|
905
983
|
file.parsedData = cleanedEntities;
|
|
906
|
-
|
|
984
|
+
file.name = newFileName;
|
|
907
985
|
file.originFileObj = newFile;
|
|
908
986
|
file.originalFileObj = newFile;
|
|
909
|
-
}
|
|
910
|
-
setTimeout(() => {
|
|
911
|
-
//inside a timeout for cypress purposes
|
|
912
|
-
window.toastr.success(
|
|
913
|
-
`Added Fixed Up File${
|
|
914
|
-
allFiles.length > 1 ? "s" : ""
|
|
915
|
-
} ${allFiles.map(({ file }) => file.name).join(", ")}`
|
|
916
|
-
);
|
|
917
|
-
}, 200);
|
|
987
|
+
}
|
|
918
988
|
}
|
|
989
|
+
} else {
|
|
990
|
+
toKeep.push(file);
|
|
919
991
|
}
|
|
920
|
-
} else {
|
|
921
|
-
toKeep.push(...cleanedAccepted);
|
|
922
992
|
}
|
|
993
|
+
if (filesWIssues.length) {
|
|
994
|
+
const { file } = filesWIssues[0];
|
|
995
|
+
const allFiles = [...filesWIssues, ...filesWOIssues];
|
|
996
|
+
const doAllFilesHaveSameHeaders = allFiles.every(f => {
|
|
997
|
+
if (f.userSchema.fields && f.userSchema.fields.length) {
|
|
998
|
+
return f.userSchema.fields.every((h, i) => {
|
|
999
|
+
return h.path === allFiles[0].userSchema.fields[i].path;
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
return false;
|
|
1003
|
+
});
|
|
1004
|
+
const multipleFiles = allFiles.length > 1;
|
|
1005
|
+
const { res } = await showUploadCsvWizardDialog(
|
|
1006
|
+
"onUploadWizardFinish",
|
|
1007
|
+
{
|
|
1008
|
+
dialogProps: {
|
|
1009
|
+
title: `Fix Up File${multipleFiles ? "s" : ""} ${
|
|
1010
|
+
multipleFiles ? "" : file.name ? `"${file.name}"` : ""
|
|
1011
|
+
}`
|
|
1012
|
+
},
|
|
1013
|
+
doAllFilesHaveSameHeaders,
|
|
1014
|
+
filesWIssues: allFiles,
|
|
1015
|
+
validateAgainstSchema
|
|
1016
|
+
}
|
|
1017
|
+
);
|
|
923
1018
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
)
|
|
1019
|
+
if (!res) {
|
|
1020
|
+
window.toastr.warning(`File Upload Aborted`);
|
|
1021
|
+
return;
|
|
1022
|
+
} else {
|
|
1023
|
+
allFiles.forEach(({ file }, i) => {
|
|
1024
|
+
const newEntities = res[i];
|
|
1025
|
+
// const newFileName = removeExt(file.name) + `_updated.csv`;
|
|
1026
|
+
//swap out file with a new csv file
|
|
1027
|
+
const { newFile, cleanedEntities } = getNewCsvFile(
|
|
1028
|
+
newEntities,
|
|
1029
|
+
file.name
|
|
1030
|
+
);
|
|
1031
|
+
|
|
1032
|
+
file.hasEditClick = true;
|
|
1033
|
+
file.parsedData = cleanedEntities;
|
|
1034
|
+
file.originFileObj = newFile;
|
|
1035
|
+
file.originalFileObj = newFile;
|
|
1036
|
+
});
|
|
1037
|
+
setTimeout(() => {
|
|
1038
|
+
//inside a timeout for cypress purposes
|
|
1039
|
+
window.toastr.success(
|
|
1040
|
+
`Added Fixed Up File${
|
|
1041
|
+
allFiles.length > 1 ? "s" : ""
|
|
1042
|
+
} ${allFiles.map(({ file }) => file.name).join(", ")}`
|
|
1043
|
+
);
|
|
1044
|
+
}, 200);
|
|
1045
|
+
}
|
|
929
1046
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1047
|
+
} else {
|
|
1048
|
+
toKeep.push(...cleanedAccepted);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
if (toKeep.length === 0) {
|
|
1052
|
+
window.toastr &&
|
|
1053
|
+
window.toastr.error(
|
|
1054
|
+
`It looks like there wasn't any data in your file. Please add some data and try again`
|
|
1055
|
+
);
|
|
935
1056
|
}
|
|
1057
|
+
const cleanedFileList = trimFiles(
|
|
1058
|
+
[...toKeep, ...fileListToUse],
|
|
1059
|
+
fileLimit
|
|
1060
|
+
);
|
|
1061
|
+
handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
|
|
936
1062
|
}}
|
|
937
1063
|
{...dropzoneProps}
|
|
938
1064
|
>
|
|
@@ -942,71 +1068,26 @@ function UploaderInner({
|
|
|
942
1068
|
isDragAccept,
|
|
943
1069
|
isDragReject,
|
|
944
1070
|
isDragActive
|
|
945
|
-
// isDragActive
|
|
946
|
-
// isDragReject
|
|
947
|
-
// isDragAccept
|
|
948
1071
|
}) => (
|
|
949
|
-
<
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
: "Accepts any file input"
|
|
969
|
-
}
|
|
970
|
-
className="tg-upload-inner"
|
|
971
|
-
>
|
|
972
|
-
{innerIcon || (
|
|
973
|
-
<Icon icon="upload" iconSize={minimal ? 15 : 30} />
|
|
974
|
-
)}
|
|
975
|
-
{innerText ||
|
|
976
|
-
(minimal ? "Upload" : "Click or drag to upload")}
|
|
977
|
-
{validateAgainstSchema && !noBuildCsvOption && (
|
|
978
|
-
<div
|
|
979
|
-
style={{
|
|
980
|
-
textAlign: "center",
|
|
981
|
-
// fontSize: 18,
|
|
982
|
-
marginTop: 7,
|
|
983
|
-
marginBottom: 5
|
|
984
|
-
}}
|
|
985
|
-
onClick={handleManuallyEnterData}
|
|
986
|
-
className="link-button"
|
|
987
|
-
>
|
|
988
|
-
...or {manualEnterMessage}
|
|
989
|
-
{/* <div
|
|
990
|
-
style={{
|
|
991
|
-
fontSize: 11,
|
|
992
|
-
color: Colors.GRAY3,
|
|
993
|
-
fontStyle: "italic"
|
|
994
|
-
}}
|
|
995
|
-
>
|
|
996
|
-
{manualEnterSubMessage}
|
|
997
|
-
</div> */}
|
|
998
|
-
</div>
|
|
999
|
-
)}
|
|
1000
|
-
</div>
|
|
1001
|
-
)}
|
|
1002
|
-
</div>
|
|
1003
|
-
|
|
1004
|
-
{showFilesCount ? (
|
|
1005
|
-
<div className="tg-upload-file-list-counter">
|
|
1006
|
-
Files: {fileList ? fileList.length : 0}
|
|
1007
|
-
</div>
|
|
1008
|
-
) : null}
|
|
1009
|
-
</section>
|
|
1072
|
+
<InnerDropZone
|
|
1073
|
+
getRootProps={getRootProps}
|
|
1074
|
+
getInputProps={getInputProps}
|
|
1075
|
+
isDragAccept={isDragAccept}
|
|
1076
|
+
isDragReject={isDragReject}
|
|
1077
|
+
isDragActive={isDragActive}
|
|
1078
|
+
className={className}
|
|
1079
|
+
minimal={minimal}
|
|
1080
|
+
dropzoneDisabled={dropzoneDisabled}
|
|
1081
|
+
contentOverride={contentOverride}
|
|
1082
|
+
simpleAccept={simpleAccept}
|
|
1083
|
+
innerIcon={innerIcon}
|
|
1084
|
+
innerText={innerText}
|
|
1085
|
+
validateAgainstSchema={validateAgainstSchema}
|
|
1086
|
+
handleManuallyEnterData={handleManuallyEnterData}
|
|
1087
|
+
noBuildCsvOption={noBuildCsvOption}
|
|
1088
|
+
showFilesCount={showFilesCount}
|
|
1089
|
+
fileList={fileList}
|
|
1090
|
+
/>
|
|
1010
1091
|
)}
|
|
1011
1092
|
</Dropzone>
|
|
1012
1093
|
{/* {validateAgainstSchema && <CsvWizardHelper bindToggle={{}} validateAgainstSchema={validateAgainstSchema}></CsvWizardHelper>} */}
|
|
@@ -1101,24 +1182,23 @@ function UploaderInner({
|
|
|
1101
1182
|
userSchema
|
|
1102
1183
|
}
|
|
1103
1184
|
);
|
|
1104
|
-
|
|
1105
1185
|
if (!newEntities) {
|
|
1106
1186
|
return;
|
|
1107
1187
|
} else {
|
|
1108
1188
|
const { newFile, cleanedEntities } =
|
|
1109
1189
|
getNewCsvFile(newEntities, fileName);
|
|
1110
|
-
const
|
|
1190
|
+
const tmpFile = Object.assign({}, file, {
|
|
1111
1191
|
...newFile,
|
|
1112
1192
|
originFileObj: newFile,
|
|
1113
1193
|
originalFileObj: newFile,
|
|
1114
1194
|
parsedData: cleanedEntities
|
|
1115
1195
|
});
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1196
|
+
tmpFile.name = newFile.name;
|
|
1197
|
+
const tmpFileList = [...fileList];
|
|
1198
|
+
tmpFileList[index] = tmpFile;
|
|
1119
1199
|
handleSecondHalfOfUpload({
|
|
1120
|
-
acceptedFiles:
|
|
1121
|
-
cleanedFileList:
|
|
1200
|
+
acceptedFiles: tmpFileList,
|
|
1201
|
+
cleanedFileList: tmpFileList
|
|
1122
1202
|
});
|
|
1123
1203
|
window.toastr.success(`File Updated`);
|
|
1124
1204
|
}
|
|
@@ -1188,61 +1268,6 @@ function UploaderInner({
|
|
|
1188
1268
|
</div>
|
|
1189
1269
|
</>
|
|
1190
1270
|
);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
const Uploader = compose(
|
|
1194
|
-
connect(undefined, { initializeForm: initialize }),
|
|
1195
|
-
observer
|
|
1196
|
-
)(UploaderInner);
|
|
1271
|
+
};
|
|
1197
1272
|
|
|
1198
1273
|
export default Uploader;
|
|
1199
|
-
|
|
1200
|
-
function getFileDownloadAttr(exampleFile) {
|
|
1201
|
-
const baseUrl = window?.frontEndConfig?.serverBasePath || "";
|
|
1202
|
-
return isFunction(exampleFile)
|
|
1203
|
-
? { onClick: exampleFile }
|
|
1204
|
-
: exampleFile && {
|
|
1205
|
-
target: "_blank",
|
|
1206
|
-
download: true,
|
|
1207
|
-
href:
|
|
1208
|
-
exampleFile.startsWith("https") || exampleFile.startsWith("www")
|
|
1209
|
-
? exampleFile
|
|
1210
|
-
: baseUrl
|
|
1211
|
-
? urljoin(baseUrl, "exampleFiles", exampleFile)
|
|
1212
|
-
: exampleFile
|
|
1213
|
-
};
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
function getNewCsvFile(ents, fileName) {
|
|
1217
|
-
const strippedEnts = stripId(ents);
|
|
1218
|
-
|
|
1219
|
-
return {
|
|
1220
|
-
newFile: new File([papaparse.unparse(strippedEnts)], fileName),
|
|
1221
|
-
cleanedEntities: strippedEnts
|
|
1222
|
-
};
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
function stripId(ents = []) {
|
|
1226
|
-
return ents.map(ent => {
|
|
1227
|
-
const { id, ...rest } = ent;
|
|
1228
|
-
return rest;
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
const manualEnterMessage = "Build CSV File";
|
|
1233
|
-
const manualEnterSubMessage = "Paste or type data to build a CSV file";
|
|
1234
|
-
|
|
1235
|
-
function trimFiles(incomingFiles, fileLimit) {
|
|
1236
|
-
if (fileLimit) {
|
|
1237
|
-
if (fileLimit && incomingFiles.length > fileLimit) {
|
|
1238
|
-
window.toastr &&
|
|
1239
|
-
window.toastr.warning(
|
|
1240
|
-
`Detected additional files in your upload that we are ignoring. You can only upload ${fileLimit} file${
|
|
1241
|
-
fileLimit > 1 ? "s" : ""
|
|
1242
|
-
} at a time.`
|
|
1243
|
-
);
|
|
1244
|
-
}
|
|
1245
|
-
return incomingFiles.slice(0, fileLimit);
|
|
1246
|
-
}
|
|
1247
|
-
return incomingFiles;
|
|
1248
|
-
}
|