@teselagen/ui 0.5.19 → 0.5.20
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/index.d.ts +5 -0
- package/DataTable/utils/getIdOrCodeOrIndex.d.ts +2 -1
- package/DataTable/utils/rowClick.d.ts +2 -10
- package/FormComponents/Uploader.d.ts +1 -29
- package/index.cjs.js +6639 -6640
- package/index.d.ts +2 -2
- package/index.es.js +6636 -6637
- package/package.json +3 -1
- package/src/DataTable/CellDragHandle.js +7 -6
- package/src/DataTable/PagingTool.js +1 -1
- package/src/DataTable/index.js +779 -389
- package/src/DataTable/utils/getIdOrCodeOrIndex.js +1 -1
- package/src/DataTable/utils/rowClick.js +4 -7
- package/src/DataTable/utils/selection.js +1 -1
- package/src/DataTable/validateTableWideErrors.js +1 -1
- package/src/FillWindow.js +3 -2
- package/src/FormComponents/Uploader.js +400 -400
- package/src/FormComponents/tryToMatchSchemas.js +6 -0
- package/src/UploadCsvWizard.js +371 -312
- package/src/index.js +3 -3
- package/src/showDialogOnDocBody.js +9 -5
- package/src/useDialog.js +4 -7
- package/src/utils/renderOnDoc.js +5 -8
- package/style.css +7 -7
- package/utils/renderOnDoc.d.ts +1 -1
- package/src/ExcelCell.js +0 -38
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
Callout,
|
|
@@ -16,6 +16,7 @@ import classnames from "classnames";
|
|
|
16
16
|
import { nanoid } from "nanoid";
|
|
17
17
|
import papaparse, { unparse } from "papaparse";
|
|
18
18
|
import downloadjs from "downloadjs";
|
|
19
|
+
import { configure, makeObservable, observable } from "mobx";
|
|
19
20
|
import { observer } from "mobx-react";
|
|
20
21
|
import UploadCsvWizardDialog, {
|
|
21
22
|
SimpleInsertDataDialog
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
removeExt
|
|
30
31
|
} from "@teselagen/file-utils";
|
|
31
32
|
import tryToMatchSchemas from "./tryToMatchSchemas";
|
|
32
|
-
import { isArray, isFunction, isPlainObject, noop } from "lodash-es";
|
|
33
|
+
import { forEach, isArray, isFunction, isPlainObject, noop } from "lodash-es";
|
|
33
34
|
import { flatMap } from "lodash-es";
|
|
34
35
|
import urljoin from "url-join";
|
|
35
36
|
import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
|
|
@@ -37,12 +38,14 @@ import writeXlsxFile from "write-excel-file";
|
|
|
37
38
|
import { startCase } from "lodash-es";
|
|
38
39
|
import { getNewName } from "./getNewName";
|
|
39
40
|
import { isObject } from "lodash-es";
|
|
40
|
-
import {
|
|
41
|
+
import { connect } from "react-redux";
|
|
41
42
|
import { initialize } from "redux-form";
|
|
42
43
|
import classNames from "classnames";
|
|
44
|
+
import { compose } from "recompose";
|
|
43
45
|
import convertSchema from "../DataTable/utils/convertSchema";
|
|
44
46
|
import { LoadingDots } from "./LoadingDots";
|
|
45
47
|
|
|
48
|
+
configure({ isolateGlobalState: true });
|
|
46
49
|
const helperText = [
|
|
47
50
|
`How to Use This Template to Upload New Data`,
|
|
48
51
|
`1. Go to the first tab and delete the example data.`,
|
|
@@ -61,106 +64,58 @@ const helperSchema = [
|
|
|
61
64
|
}
|
|
62
65
|
];
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
})
|
|
74
|
-
) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
`Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
|
|
77
|
-
);
|
|
67
|
+
class ValidateAgainstSchema {
|
|
68
|
+
fields = [];
|
|
69
|
+
|
|
70
|
+
constructor() {
|
|
71
|
+
makeObservable(this, {
|
|
72
|
+
fields: observable.shallow
|
|
73
|
+
});
|
|
78
74
|
}
|
|
79
|
-
return schema;
|
|
80
|
-
};
|
|
81
75
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
<div
|
|
106
|
-
{...getRootProps()}
|
|
107
|
-
className={classnames("tg-dropzone", className, {
|
|
108
|
-
"tg-dropzone-minimal": minimal,
|
|
109
|
-
"tg-dropzone-active": isDragActive,
|
|
110
|
-
"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
|
|
111
|
-
"tg-dropzone-accept": isDragAccept,
|
|
112
|
-
"tg-dropzone-disabled": dropzoneDisabled,
|
|
113
|
-
"bp3-disabled": dropzoneDisabled
|
|
114
|
-
})}
|
|
115
|
-
>
|
|
116
|
-
<input {...getInputProps()} />
|
|
117
|
-
{contentOverride || (
|
|
118
|
-
<div
|
|
119
|
-
title={
|
|
120
|
-
simpleAccept
|
|
121
|
-
? "Accepts only the following file types: " + simpleAccept
|
|
122
|
-
: "Accepts any file input"
|
|
123
|
-
}
|
|
124
|
-
className="tg-upload-inner"
|
|
125
|
-
>
|
|
126
|
-
{innerIcon || <Icon icon="upload" iconSize={minimal ? 15 : 30} />}
|
|
127
|
-
{innerText || (minimal ? "Upload" : "Click or drag to upload")}
|
|
128
|
-
{validateAgainstSchema && !noBuildCsvOption && (
|
|
129
|
-
<div
|
|
130
|
-
style={{
|
|
131
|
-
textAlign: "center",
|
|
132
|
-
// fontSize: 18,
|
|
133
|
-
marginTop: 7,
|
|
134
|
-
marginBottom: 5
|
|
135
|
-
}}
|
|
136
|
-
onClick={handleManuallyEnterData}
|
|
137
|
-
className="link-button"
|
|
138
|
-
>
|
|
139
|
-
...or {manualEnterMessage}
|
|
140
|
-
{/* <div
|
|
141
|
-
style={{
|
|
142
|
-
fontSize: 11,
|
|
143
|
-
color: Colors.GRAY3,
|
|
144
|
-
fontStyle: "italic"
|
|
145
|
-
}}
|
|
146
|
-
>
|
|
147
|
-
{manualEnterSubMessage}
|
|
148
|
-
</div> */}
|
|
149
|
-
</div>
|
|
150
|
-
)}
|
|
151
|
-
</div>
|
|
152
|
-
)}
|
|
153
|
-
</div>
|
|
76
|
+
setValidateAgainstSchema(newValidateAgainstSchema) {
|
|
77
|
+
if (!newValidateAgainstSchema) {
|
|
78
|
+
this.fields = [];
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const schema = convertSchema(newValidateAgainstSchema);
|
|
82
|
+
if (
|
|
83
|
+
schema.fields.some(f => {
|
|
84
|
+
if (f.path === "id") {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
})
|
|
89
|
+
) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Uploader was passed a validateAgainstSchema with a fields array that contains a field with a path of "id". This is not allowed.`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
forEach(schema, (v, k) => {
|
|
95
|
+
this[k] = v;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
154
99
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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");
|
|
162
108
|
|
|
163
|
-
const
|
|
109
|
+
// const validateAgainstSchema = observable.shallow({
|
|
110
|
+
// fields: []
|
|
111
|
+
// })
|
|
112
|
+
|
|
113
|
+
// validateAgainstSchema.fields = ["hahah"];
|
|
114
|
+
|
|
115
|
+
// wink wink
|
|
116
|
+
const emptyPromise = Promise.resolve.bind(Promise);
|
|
117
|
+
|
|
118
|
+
function UploaderInner({
|
|
164
119
|
accept: __accept,
|
|
165
120
|
contentOverride: maybeContentOverride,
|
|
166
121
|
innerIcon,
|
|
@@ -175,9 +130,7 @@ const UploaderInner = ({
|
|
|
175
130
|
showUploadList = true,
|
|
176
131
|
beforeUpload,
|
|
177
132
|
fileList, //list of files with options: {name, loading, error, url, originalName, downloadName}
|
|
178
|
-
onFileSuccess =
|
|
179
|
-
return;
|
|
180
|
-
}, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
|
|
133
|
+
onFileSuccess = emptyPromise, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
|
|
181
134
|
onFieldSubmit = noop, //called when all files have successfully uploaded
|
|
182
135
|
// fileFinished = noop,
|
|
183
136
|
onRemove = noop, //called when a file has been selected to be removed
|
|
@@ -188,27 +141,22 @@ const UploaderInner = ({
|
|
|
188
141
|
autoUnzip,
|
|
189
142
|
disabled: _disabled,
|
|
190
143
|
noBuildCsvOption,
|
|
144
|
+
initializeForm,
|
|
191
145
|
showFilesCount,
|
|
192
146
|
threeDotMenuItems,
|
|
193
147
|
onPreviewClick
|
|
194
|
-
})
|
|
195
|
-
const dispatch = useDispatch();
|
|
148
|
+
}) {
|
|
196
149
|
let dropzoneDisabled = _disabled;
|
|
197
150
|
let _accept = __accept;
|
|
151
|
+
const validateAgainstSchemaStore = useRef(new ValidateAgainstSchema());
|
|
198
152
|
const [acceptLoading, setAcceptLoading] = useState();
|
|
199
153
|
const [resolvedAccept, setResolvedAccept] = useState();
|
|
200
|
-
|
|
201
154
|
if (resolvedAccept) {
|
|
202
155
|
_accept = resolvedAccept;
|
|
203
156
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
() =>
|
|
207
|
-
__accept?.then ||
|
|
208
|
-
(Array.isArray(__accept) ? __accept.some(a => a?.then) : false),
|
|
209
|
-
[__accept]
|
|
210
|
-
);
|
|
211
|
-
|
|
157
|
+
const isAcceptPromise =
|
|
158
|
+
__accept?.then ||
|
|
159
|
+
(Array.isArray(__accept) ? __accept.some(a => a?.then) : false);
|
|
212
160
|
useEffect(() => {
|
|
213
161
|
if (isAcceptPromise) {
|
|
214
162
|
setAcceptLoading(true);
|
|
@@ -221,36 +169,35 @@ const UploaderInner = ({
|
|
|
221
169
|
);
|
|
222
170
|
}
|
|
223
171
|
}, [__accept, isAcceptPromise]);
|
|
224
|
-
|
|
225
172
|
if (isAcceptPromise && !resolvedAccept) {
|
|
226
173
|
_accept = [];
|
|
227
174
|
}
|
|
228
|
-
|
|
229
175
|
if (acceptLoading) dropzoneDisabled = true;
|
|
230
|
-
const accept =
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
? _accept
|
|
240
|
-
: _accept.split(",").map(a => ({ type: a })),
|
|
241
|
-
[_accept, isAcceptPromise, resolvedAccept]
|
|
242
|
-
);
|
|
243
|
-
|
|
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 }));
|
|
244
185
|
const callout = _callout || accept?.find?.(a => a?.callout)?.callout;
|
|
245
186
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
187
|
+
const validateAgainstSchemaToUse =
|
|
188
|
+
_validateAgainstSchema ||
|
|
189
|
+
accept?.find?.(a => a?.validateAgainstSchema)?.validateAgainstSchema;
|
|
190
|
+
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
// validateAgainstSchema
|
|
193
|
+
validateAgainstSchemaStore.current.setValidateAgainstSchema(
|
|
194
|
+
validateAgainstSchemaToUse
|
|
195
|
+
);
|
|
196
|
+
}, [validateAgainstSchemaToUse]);
|
|
197
|
+
let validateAgainstSchema;
|
|
198
|
+
if (validateAgainstSchemaToUse) {
|
|
199
|
+
validateAgainstSchema = validateAgainstSchemaStore.current;
|
|
200
|
+
}
|
|
254
201
|
|
|
255
202
|
if (
|
|
256
203
|
(validateAgainstSchema || autoUnzip) &&
|
|
@@ -268,7 +215,6 @@ const UploaderInner = ({
|
|
|
268
215
|
const { showDialogPromise: showUploadCsvWizardDialog, comp } = useDialog({
|
|
269
216
|
ModalComponent: UploadCsvWizardDialog
|
|
270
217
|
});
|
|
271
|
-
|
|
272
218
|
const { showDialogPromise: showSimpleInsertDataDialog, comp: comp2 } =
|
|
273
219
|
useDialog({
|
|
274
220
|
ModalComponent: SimpleInsertDataDialog
|
|
@@ -607,7 +553,7 @@ const UploaderInner = ({
|
|
|
607
553
|
}
|
|
608
554
|
{...getFileDownloadAttr(exampleFile)}
|
|
609
555
|
key={i}
|
|
610
|
-
|
|
556
|
+
></MenuItem>
|
|
611
557
|
);
|
|
612
558
|
}
|
|
613
559
|
)}
|
|
@@ -673,7 +619,7 @@ const UploaderInner = ({
|
|
|
673
619
|
}}
|
|
674
620
|
size={10}
|
|
675
621
|
icon="download"
|
|
676
|
-
|
|
622
|
+
></Icon>
|
|
677
623
|
)}
|
|
678
624
|
</CustomTag>
|
|
679
625
|
</PopOrTooltip>
|
|
@@ -685,8 +631,7 @@ const UploaderInner = ({
|
|
|
685
631
|
// make the dots below "load"
|
|
686
632
|
|
|
687
633
|
<>
|
|
688
|
-
Accept Loading
|
|
689
|
-
<LoadingDots />
|
|
634
|
+
Accept Loading<LoadingDots></LoadingDots>
|
|
690
635
|
</>
|
|
691
636
|
) : (
|
|
692
637
|
<>Accepts {simpleAccept}</>
|
|
@@ -705,135 +650,135 @@ const UploaderInner = ({
|
|
|
705
650
|
.join(", ")
|
|
706
651
|
: undefined
|
|
707
652
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
653
|
+
{...{
|
|
654
|
+
onDrop: async (_acceptedFiles, rejectedFiles) => {
|
|
655
|
+
let acceptedFiles = [];
|
|
656
|
+
for (const file of _acceptedFiles) {
|
|
657
|
+
if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
|
|
658
|
+
const files = await filterFilesInZip(
|
|
659
|
+
file,
|
|
660
|
+
simpleAccept
|
|
661
|
+
?.split(", ")
|
|
662
|
+
?.map(a => (a.startsWith(".") ? a : "." + a)) || []
|
|
663
|
+
);
|
|
664
|
+
acceptedFiles.push(...files.map(f => f.originFileObj));
|
|
665
|
+
} else {
|
|
666
|
+
acceptedFiles.push(file);
|
|
667
|
+
}
|
|
721
668
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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
|
+
);
|
|
682
|
+
}
|
|
683
|
+
if (!acceptedFiles.length) return;
|
|
684
|
+
setLoading(true);
|
|
685
|
+
acceptedFiles = trimFiles(acceptedFiles, fileLimit);
|
|
686
|
+
|
|
687
|
+
acceptedFiles.forEach(file => {
|
|
688
|
+
file.preview = URL.createObjectURL(file);
|
|
689
|
+
file.loading = true;
|
|
690
|
+
if (!file.id) {
|
|
691
|
+
file.id = nanoid();
|
|
692
|
+
}
|
|
693
|
+
filesToClean.current.push(file);
|
|
731
694
|
});
|
|
732
|
-
window.toastr &&
|
|
733
|
-
window.toastr.warning(
|
|
734
|
-
<div className="preserve-newline">{msg}</div>
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
if (!acceptedFiles.length) return;
|
|
738
|
-
setLoading(true);
|
|
739
|
-
acceptedFiles = trimFiles(acceptedFiles, fileLimit);
|
|
740
695
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
696
|
+
if (readBeforeUpload) {
|
|
697
|
+
acceptedFiles = await Promise.all(
|
|
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
|
+
})
|
|
712
|
+
);
|
|
746
713
|
}
|
|
747
|
-
|
|
748
|
-
|
|
714
|
+
const cleanedAccepted = acceptedFiles.map(file => {
|
|
715
|
+
return {
|
|
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
|
+
});
|
|
749
731
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
preview: file.preview,
|
|
778
|
-
size: file.size,
|
|
779
|
-
type: file.type,
|
|
780
|
-
...(file.parsedString
|
|
781
|
-
? { parsedString: file.parsedString }
|
|
782
|
-
: {})
|
|
783
|
-
};
|
|
784
|
-
});
|
|
732
|
+
const toKeep = [];
|
|
733
|
+
if (validateAgainstSchema) {
|
|
734
|
+
const filesWIssues = [];
|
|
735
|
+
const filesWOIssues = [];
|
|
736
|
+
for (const [i, file] of cleanedAccepted.entries()) {
|
|
737
|
+
if (isCsvOrExcelFile(file)) {
|
|
738
|
+
let parsedF;
|
|
739
|
+
try {
|
|
740
|
+
parsedF = await parseCsvOrExcelFile(file, {
|
|
741
|
+
csvParserOptions: isFunction(
|
|
742
|
+
validateAgainstSchema.csvParserOptions
|
|
743
|
+
)
|
|
744
|
+
? validateAgainstSchema.csvParserOptions({
|
|
745
|
+
validateAgainstSchema
|
|
746
|
+
})
|
|
747
|
+
: validateAgainstSchema.csvParserOptions
|
|
748
|
+
});
|
|
749
|
+
} catch (error) {
|
|
750
|
+
console.error("error:", error);
|
|
751
|
+
window.toastr &&
|
|
752
|
+
window.toastr.error(
|
|
753
|
+
`There was an error parsing your file. Please try again. ${
|
|
754
|
+
error.message || error
|
|
755
|
+
}`
|
|
756
|
+
);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
785
759
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
csvParserOptions: isFunction(
|
|
796
|
-
validateAgainstSchema.csvParserOptions
|
|
797
|
-
)
|
|
798
|
-
? validateAgainstSchema.csvParserOptions({
|
|
799
|
-
validateAgainstSchema
|
|
800
|
-
})
|
|
801
|
-
: validateAgainstSchema.csvParserOptions
|
|
760
|
+
const {
|
|
761
|
+
csvValidationIssue: _csvValidationIssue,
|
|
762
|
+
matchedHeaders,
|
|
763
|
+
userSchema,
|
|
764
|
+
searchResults,
|
|
765
|
+
ignoredHeadersMsg
|
|
766
|
+
} = await tryToMatchSchemas({
|
|
767
|
+
incomingData: parsedF.data,
|
|
768
|
+
validateAgainstSchema
|
|
802
769
|
});
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
error.message || error
|
|
809
|
-
}`
|
|
770
|
+
if (userSchema?.userData?.length === 0) {
|
|
771
|
+
console.error(
|
|
772
|
+
`userSchema, parsedF.data:`,
|
|
773
|
+
userSchema,
|
|
774
|
+
parsedF.data
|
|
810
775
|
);
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
userSchema,
|
|
818
|
-
searchResults,
|
|
819
|
-
ignoredHeadersMsg
|
|
820
|
-
} = await tryToMatchSchemas({
|
|
821
|
-
incomingData: parsedF.data,
|
|
822
|
-
validateAgainstSchema
|
|
823
|
-
});
|
|
824
|
-
if (userSchema?.userData?.length === 0) {
|
|
825
|
-
console.error(
|
|
826
|
-
`userSchema, parsedF.data:`,
|
|
827
|
-
userSchema,
|
|
828
|
-
parsedF.data
|
|
829
|
-
);
|
|
830
|
-
} else {
|
|
831
|
-
toKeep.push(file);
|
|
832
|
-
let csvValidationIssue = _csvValidationIssue;
|
|
833
|
-
if (csvValidationIssue) {
|
|
834
|
-
if (isObject(csvValidationIssue)) {
|
|
835
|
-
dispatch(
|
|
836
|
-
initialize(
|
|
776
|
+
} else {
|
|
777
|
+
toKeep.push(file);
|
|
778
|
+
let csvValidationIssue = _csvValidationIssue;
|
|
779
|
+
if (csvValidationIssue) {
|
|
780
|
+
if (isObject(csvValidationIssue)) {
|
|
781
|
+
initializeForm(
|
|
837
782
|
`editableCellTable${
|
|
838
783
|
cleanedAccepted.length > 1 ? `-${i}` : ""
|
|
839
784
|
}`,
|
|
@@ -845,142 +790,149 @@ const UploaderInner = ({
|
|
|
845
790
|
keepValues: true,
|
|
846
791
|
updateUnregisteredFields: true
|
|
847
792
|
}
|
|
848
|
-
)
|
|
849
|
-
);
|
|
850
|
-
const err = Object.values(csvValidationIssue)[0];
|
|
851
|
-
// csvValidationIssue = `It looks like there was an error with your data - \n\n${
|
|
852
|
-
// err && err.message ? err.message : err
|
|
853
|
-
// }.\n\nPlease review your headers and then correct any errors on the next page.`; //pass just the first error as a string
|
|
854
|
-
const errMsg = err && err.message ? err.message : err;
|
|
855
|
-
if (isPlainObject(errMsg)) {
|
|
856
|
-
throw new Error(
|
|
857
|
-
`errMsg is an object ${JSON.stringify(
|
|
858
|
-
errMsg,
|
|
859
|
-
null,
|
|
860
|
-
4
|
|
861
|
-
)}`
|
|
862
793
|
);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
794
|
+
const err = Object.values(csvValidationIssue)[0];
|
|
795
|
+
// csvValidationIssue = `It looks like there was an error with your data - \n\n${
|
|
796
|
+
// err && err.message ? err.message : err
|
|
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 = (
|
|
871
810
|
<div>
|
|
872
|
-
|
|
873
|
-
|
|
811
|
+
<div>
|
|
812
|
+
It looks like there was an error with your
|
|
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>
|
|
874
820
|
</div>
|
|
875
|
-
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
filesWIssues.push({
|
|
824
|
+
file,
|
|
825
|
+
csvValidationIssue,
|
|
826
|
+
ignoredHeadersMsg,
|
|
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
|
|
876
845
|
);
|
|
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;
|
|
877
853
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
userSchema,
|
|
893
|
-
searchResults
|
|
854
|
+
}
|
|
855
|
+
} else {
|
|
856
|
+
toKeep.push(file);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (filesWIssues.length) {
|
|
860
|
+
const { file } = filesWIssues[0];
|
|
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
|
+
);
|
|
894
868
|
});
|
|
895
|
-
|
|
869
|
+
}
|
|
870
|
+
return false;
|
|
871
|
+
});
|
|
872
|
+
const multipleFiles = allFiles.length > 1;
|
|
873
|
+
const { res } = await showUploadCsvWizardDialog(
|
|
874
|
+
"onUploadWizardFinish",
|
|
875
|
+
{
|
|
876
|
+
dialogProps: {
|
|
877
|
+
title: `Fix Up File${multipleFiles ? "s" : ""} ${
|
|
878
|
+
multipleFiles
|
|
879
|
+
? ""
|
|
880
|
+
: file.name
|
|
881
|
+
? `"${file.name}"`
|
|
882
|
+
: ""
|
|
883
|
+
}`
|
|
884
|
+
},
|
|
885
|
+
doAllFilesHaveSameHeaders,
|
|
886
|
+
filesWIssues: allFiles,
|
|
887
|
+
validateAgainstSchema
|
|
888
|
+
}
|
|
889
|
+
);
|
|
896
890
|
|
|
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
|
|
897
899
|
const { newFile, cleanedEntities } = getNewCsvFile(
|
|
898
|
-
|
|
899
|
-
|
|
900
|
+
newEntities,
|
|
901
|
+
file.name
|
|
900
902
|
);
|
|
901
903
|
|
|
902
|
-
file.meta = parsedF.meta;
|
|
903
904
|
file.hasEditClick = true;
|
|
904
905
|
file.parsedData = cleanedEntities;
|
|
905
|
-
file.name = newFileName;
|
|
906
|
+
// file.name = newFileName;
|
|
906
907
|
file.originFileObj = newFile;
|
|
907
908
|
file.originalFileObj = newFile;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
} else {
|
|
911
|
-
toKeep.push(file);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
if (filesWIssues.length) {
|
|
915
|
-
const { file } = filesWIssues[0];
|
|
916
|
-
const allFiles = [...filesWIssues, ...filesWOIssues];
|
|
917
|
-
const doAllFilesHaveSameHeaders = allFiles.every(f => {
|
|
918
|
-
if (f.userSchema.fields && f.userSchema.fields.length) {
|
|
919
|
-
return f.userSchema.fields.every((h, i) => {
|
|
920
|
-
return h.path === allFiles[0].userSchema.fields[i].path;
|
|
921
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);
|
|
922
918
|
}
|
|
923
|
-
return false;
|
|
924
|
-
});
|
|
925
|
-
const multipleFiles = allFiles.length > 1;
|
|
926
|
-
const { res } = await showUploadCsvWizardDialog(
|
|
927
|
-
"onUploadWizardFinish",
|
|
928
|
-
{
|
|
929
|
-
dialogProps: {
|
|
930
|
-
title: `Fix Up File${multipleFiles ? "s" : ""} ${
|
|
931
|
-
multipleFiles ? "" : file.name ? `"${file.name}"` : ""
|
|
932
|
-
}`
|
|
933
|
-
},
|
|
934
|
-
doAllFilesHaveSameHeaders,
|
|
935
|
-
filesWIssues: allFiles,
|
|
936
|
-
validateAgainstSchema
|
|
937
|
-
}
|
|
938
|
-
);
|
|
939
|
-
|
|
940
|
-
if (!res) {
|
|
941
|
-
window.toastr.warning(`File Upload Aborted`);
|
|
942
|
-
return;
|
|
943
|
-
} else {
|
|
944
|
-
allFiles.forEach(({ file }, i) => {
|
|
945
|
-
const newEntities = res[i];
|
|
946
|
-
// const newFileName = removeExt(file.name) + `_updated.csv`;
|
|
947
|
-
//swap out file with a new csv file
|
|
948
|
-
const { newFile, cleanedEntities } = getNewCsvFile(
|
|
949
|
-
newEntities,
|
|
950
|
-
file.name
|
|
951
|
-
);
|
|
952
|
-
|
|
953
|
-
file.hasEditClick = true;
|
|
954
|
-
file.parsedData = cleanedEntities;
|
|
955
|
-
// file.name = newFileName;
|
|
956
|
-
file.originFileObj = newFile;
|
|
957
|
-
file.originalFileObj = newFile;
|
|
958
|
-
});
|
|
959
|
-
setTimeout(() => {
|
|
960
|
-
//inside a timeout for cypress purposes
|
|
961
|
-
window.toastr.success(
|
|
962
|
-
`Added Fixed Up File${
|
|
963
|
-
allFiles.length > 1 ? "s" : ""
|
|
964
|
-
} ${allFiles.map(({ file }) => file.name).join(", ")}`
|
|
965
|
-
);
|
|
966
|
-
}, 200);
|
|
967
919
|
}
|
|
920
|
+
} else {
|
|
921
|
+
toKeep.push(...cleanedAccepted);
|
|
968
922
|
}
|
|
969
|
-
} else {
|
|
970
|
-
toKeep.push(...cleanedAccepted);
|
|
971
|
-
}
|
|
972
923
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
924
|
+
if (toKeep.length === 0) {
|
|
925
|
+
window.toastr &&
|
|
926
|
+
window.toastr.error(
|
|
927
|
+
`It looks like there wasn't any data in your file. Please add some data and try again`
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
const cleanedFileList = trimFiles(
|
|
931
|
+
[...toKeep, ...fileListToUse],
|
|
932
|
+
fileLimit
|
|
933
|
+
);
|
|
934
|
+
handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
|
|
978
935
|
}
|
|
979
|
-
const cleanedFileList = trimFiles(
|
|
980
|
-
[...toKeep, ...fileListToUse],
|
|
981
|
-
fileLimit
|
|
982
|
-
);
|
|
983
|
-
handleSecondHalfOfUpload({ acceptedFiles, cleanedFileList });
|
|
984
936
|
}}
|
|
985
937
|
{...dropzoneProps}
|
|
986
938
|
>
|
|
@@ -990,26 +942,71 @@ const UploaderInner = ({
|
|
|
990
942
|
isDragAccept,
|
|
991
943
|
isDragReject,
|
|
992
944
|
isDragActive
|
|
945
|
+
// isDragActive
|
|
946
|
+
// isDragReject
|
|
947
|
+
// isDragAccept
|
|
993
948
|
}) => (
|
|
994
|
-
<
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
949
|
+
<section>
|
|
950
|
+
<div
|
|
951
|
+
{...getRootProps()}
|
|
952
|
+
className={classnames("tg-dropzone", className, {
|
|
953
|
+
"tg-dropzone-minimal": minimal,
|
|
954
|
+
"tg-dropzone-active": isDragActive,
|
|
955
|
+
"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
|
|
956
|
+
"tg-dropzone-accept": isDragAccept,
|
|
957
|
+
"tg-dropzone-disabled": dropzoneDisabled,
|
|
958
|
+
"bp3-disabled": dropzoneDisabled
|
|
959
|
+
})}
|
|
960
|
+
>
|
|
961
|
+
<input {...getInputProps()} />
|
|
962
|
+
{contentOverride || (
|
|
963
|
+
<div
|
|
964
|
+
title={
|
|
965
|
+
simpleAccept
|
|
966
|
+
? "Accepts only the following file types: " +
|
|
967
|
+
simpleAccept
|
|
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>
|
|
1013
1010
|
)}
|
|
1014
1011
|
</Dropzone>
|
|
1015
1012
|
{/* {validateAgainstSchema && <CsvWizardHelper bindToggle={{}} validateAgainstSchema={validateAgainstSchema}></CsvWizardHelper>} */}
|
|
@@ -1191,9 +1188,12 @@ const UploaderInner = ({
|
|
|
1191
1188
|
</div>
|
|
1192
1189
|
</>
|
|
1193
1190
|
);
|
|
1194
|
-
}
|
|
1191
|
+
}
|
|
1195
1192
|
|
|
1196
|
-
const Uploader =
|
|
1193
|
+
const Uploader = compose(
|
|
1194
|
+
connect(undefined, { initializeForm: initialize }),
|
|
1195
|
+
observer
|
|
1196
|
+
)(UploaderInner);
|
|
1197
1197
|
|
|
1198
1198
|
export default Uploader;
|
|
1199
1199
|
|