@teselagen/ui 0.5.21-beta.1 → 0.5.21-beta.2
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/FormComponents/AbstractField.d.ts +1 -0
- package/index.cjs.js +41 -36
- package/index.es.js +41 -36
- package/package.json +5 -1
- package/src/DataTable/utils/useTableParams.js +1 -0
- package/src/FormComponents/AbstractField.js +388 -0
- package/src/FormComponents/index.js +1 -3
- package/src/UploadCsvWizard.js +49 -41
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function withAbstractWrapper(ComponentToWrap: any, opts?: {}): (props: any) => import("react/jsx-runtime").JSX.Element;
|
package/index.cjs.js
CHANGED
|
@@ -32903,9 +32903,7 @@ const withAbstractWrapper = /* @__PURE__ */ __name((ComponentToWrap, opts = {})
|
|
|
32903
32903
|
};
|
|
32904
32904
|
}, "withAbstractWrapper");
|
|
32905
32905
|
const InputField = generateField(RenderBlueprintInput);
|
|
32906
|
-
const FileUploadField = generateField(renderFileUpload
|
|
32907
|
-
showErrorIfUntouched: true
|
|
32908
|
-
});
|
|
32906
|
+
const FileUploadField = generateField(renderFileUpload);
|
|
32909
32907
|
const DateInputField = generateField(renderBlueprintDateInput);
|
|
32910
32908
|
const DateRangeInputField = generateField(renderBlueprintDateRangeInput);
|
|
32911
32909
|
const CheckboxField = generateField(renderBlueprintCheckbox, {
|
|
@@ -53030,35 +53028,37 @@ const PreviewCsvData = /* @__PURE__ */ __name((props) => {
|
|
|
53030
53028
|
userSchema = exampleData,
|
|
53031
53029
|
entities
|
|
53032
53030
|
} = props;
|
|
53033
|
-
const data =
|
|
53034
|
-
|
|
53035
|
-
|
|
53036
|
-
|
|
53037
|
-
|
|
53038
|
-
|
|
53039
|
-
|
|
53040
|
-
|
|
53041
|
-
|
|
53042
|
-
toRet[path2] = row[matchingKey];
|
|
53043
|
-
}
|
|
53044
|
-
if (toRet[path2] === void 0 || toRet[path2] === "") {
|
|
53045
|
-
if (defaultValue2) {
|
|
53046
|
-
if (isFunction$4(defaultValue2)) {
|
|
53047
|
-
toRet[path2] = defaultValue2(i, row);
|
|
53048
|
-
} else
|
|
53049
|
-
toRet[path2] = defaultValue2;
|
|
53031
|
+
const data = React$1.useMemo(() => {
|
|
53032
|
+
return userSchema.userData && userSchema.userData.length && userSchema.userData.map((row, i) => {
|
|
53033
|
+
const toRet = {
|
|
53034
|
+
_isClean: row._isClean
|
|
53035
|
+
};
|
|
53036
|
+
validateAgainstSchema.fields.forEach(({ path: path2, defaultValue: defaultValue2 }) => {
|
|
53037
|
+
const matchingKey = matchedHeaders == null ? void 0 : matchedHeaders[path2];
|
|
53038
|
+
if (!matchingKey) {
|
|
53039
|
+
toRet[path2] = defaultValue2 === void 0 ? defaultValue2 : "";
|
|
53050
53040
|
} else {
|
|
53051
|
-
toRet[path2] =
|
|
53041
|
+
toRet[path2] = row[matchingKey];
|
|
53052
53042
|
}
|
|
53043
|
+
if (toRet[path2] === void 0 || toRet[path2] === "") {
|
|
53044
|
+
if (defaultValue2) {
|
|
53045
|
+
if (isFunction$4(defaultValue2)) {
|
|
53046
|
+
toRet[path2] = defaultValue2(i, row);
|
|
53047
|
+
} else
|
|
53048
|
+
toRet[path2] = defaultValue2;
|
|
53049
|
+
} else {
|
|
53050
|
+
toRet[path2] = "";
|
|
53051
|
+
}
|
|
53052
|
+
}
|
|
53053
|
+
});
|
|
53054
|
+
if (row.id === void 0) {
|
|
53055
|
+
toRet.id = nanoid();
|
|
53056
|
+
} else {
|
|
53057
|
+
toRet.id = row.id;
|
|
53053
53058
|
}
|
|
53059
|
+
return toRet;
|
|
53054
53060
|
});
|
|
53055
|
-
|
|
53056
|
-
toRet.id = nanoid();
|
|
53057
|
-
} else {
|
|
53058
|
-
toRet.id = row.id;
|
|
53059
|
-
}
|
|
53060
|
-
return toRet;
|
|
53061
|
-
});
|
|
53061
|
+
}, [matchedHeaders, userSchema, validateAgainstSchema.fields]);
|
|
53062
53062
|
return /* @__PURE__ */ React$1.createElement("div", { style: { minWidth: 400 } }, /* @__PURE__ */ React$1.createElement(core.Callout, { style: { marginBottom: 5 }, intent: "primary" }, headerMessage || (showDoesDataLookCorrectMsg ? "Does this data look correct? Edit it as needed." : `${isEditingExistingFile ? "Edit" : "Input"} your data here. Hover table headers for additional instructions.`)), validateAgainstSchema.description && /* @__PURE__ */ React$1.createElement(core.Callout, null, validateAgainstSchema.description), /* @__PURE__ */ React$1.createElement(
|
|
53063
53063
|
"div",
|
|
53064
53064
|
{
|
|
@@ -53102,13 +53102,15 @@ const SimpleInsertDataDialog = compose(
|
|
|
53102
53102
|
showDoesDataLookCorrectMsg,
|
|
53103
53103
|
submitting,
|
|
53104
53104
|
userSchema,
|
|
53105
|
-
validateAgainstSchema
|
|
53105
|
+
validateAgainstSchema,
|
|
53106
|
+
initialValues
|
|
53106
53107
|
}) => {
|
|
53108
|
+
var _a;
|
|
53107
53109
|
const dispatch = reactRedux.useDispatch();
|
|
53108
53110
|
const _reduxFormEntities = reactRedux.useSelector(
|
|
53109
53111
|
(state) => {
|
|
53110
|
-
var
|
|
53111
|
-
return (_b = (
|
|
53112
|
+
var _a2, _b;
|
|
53113
|
+
return (_b = (_a2 = state.form) == null ? void 0 : _a2[dataTableForm]) == null ? void 0 : _b.values.reduxFormEntities;
|
|
53112
53114
|
}
|
|
53113
53115
|
);
|
|
53114
53116
|
const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
|
|
@@ -53117,8 +53119,8 @@ const SimpleInsertDataDialog = compose(
|
|
|
53117
53119
|
}, [dataTableForm, dispatch]);
|
|
53118
53120
|
const _reduxFormCellValidation = reactRedux.useSelector(
|
|
53119
53121
|
(state) => {
|
|
53120
|
-
var
|
|
53121
|
-
return (_b = (
|
|
53122
|
+
var _a2, _b;
|
|
53123
|
+
return (_b = (_a2 = state.form) == null ? void 0 : _a2[dataTableForm]) == null ? void 0 : _b.values.reduxFormCellValidation;
|
|
53122
53124
|
}
|
|
53123
53125
|
);
|
|
53124
53126
|
const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
|
|
@@ -53133,7 +53135,7 @@ const SimpleInsertDataDialog = compose(
|
|
|
53133
53135
|
rightElement: /* @__PURE__ */ React$1.createElement("div", { style: { paddingTop: 6, paddingRight: 5 } }, ".csv"),
|
|
53134
53136
|
inlineLabel: true,
|
|
53135
53137
|
label: "File Name:",
|
|
53136
|
-
defaultValue: "manual_data_entry",
|
|
53138
|
+
defaultValue: (_a = initialValues == null ? void 0 : initialValues.fileName) != null ? _a : "manual_data_entry",
|
|
53137
53139
|
name: "fileName"
|
|
53138
53140
|
}
|
|
53139
53141
|
), /* @__PURE__ */ React$1.createElement(
|
|
@@ -53317,6 +53319,7 @@ const MultipleFileDialog = /* @__PURE__ */ __name(({
|
|
|
53317
53319
|
steps,
|
|
53318
53320
|
hasSubmittedOuter
|
|
53319
53321
|
}) => {
|
|
53322
|
+
console.log({ filesWIssues, finishedFiles });
|
|
53320
53323
|
const tabs = /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, /* @__PURE__ */ React$1.createElement(core.Callout, { style: { marginBottom: 10, flexGrow: 0 }, intent: "warning" }, /* @__PURE__ */ React$1.createElement("div", null, "Please look over each of the following files and correct any issues.")), /* @__PURE__ */ React$1.createElement(
|
|
53321
53324
|
core.Tabs,
|
|
53322
53325
|
{
|
|
@@ -53492,6 +53495,7 @@ const UploadCsvWizardDialog = compose(
|
|
|
53492
53495
|
if (_filesWIssues.length > 0) {
|
|
53493
53496
|
const reduxFormEntitiesArray2 = [];
|
|
53494
53497
|
const finishedFiles2 = _filesWIssues.map((f2, i) => {
|
|
53498
|
+
console.log("aa");
|
|
53495
53499
|
const { reduxFormEntities, reduxFormCellValidation } = reduxForm.formValueSelector(`editableCellTable-${i}`)(
|
|
53496
53500
|
state,
|
|
53497
53501
|
"reduxFormEntities",
|
|
@@ -53505,12 +53509,13 @@ const UploadCsvWizardDialog = compose(
|
|
|
53505
53509
|
return entsToUse && entsToUse.length && !some(validationToUse, (v2) => v2) && entsToUse;
|
|
53506
53510
|
});
|
|
53507
53511
|
return {
|
|
53508
|
-
|
|
53509
|
-
|
|
53512
|
+
_reduxFormEntitiesArray: reduxFormEntitiesArray2,
|
|
53513
|
+
_finishedFiles: finishedFiles2
|
|
53510
53514
|
};
|
|
53511
53515
|
}
|
|
53512
53516
|
});
|
|
53513
53517
|
const reduxFormEntitiesArray = useDeepEqualMemo(_reduxFormEntitiesArray);
|
|
53518
|
+
console.log({ _finishedFiles });
|
|
53514
53519
|
const finishedFiles = useDeepEqualMemo(_finishedFiles);
|
|
53515
53520
|
const [hasSubmittedOuter, setSubmittedOuter] = React$1.useState();
|
|
53516
53521
|
const [steps, setSteps] = React$1.useState(getInitialSteps(true));
|
package/index.es.js
CHANGED
|
@@ -32885,9 +32885,7 @@ const withAbstractWrapper = /* @__PURE__ */ __name((ComponentToWrap, opts = {})
|
|
|
32885
32885
|
};
|
|
32886
32886
|
}, "withAbstractWrapper");
|
|
32887
32887
|
const InputField = generateField(RenderBlueprintInput);
|
|
32888
|
-
const FileUploadField = generateField(renderFileUpload
|
|
32889
|
-
showErrorIfUntouched: true
|
|
32890
|
-
});
|
|
32888
|
+
const FileUploadField = generateField(renderFileUpload);
|
|
32891
32889
|
const DateInputField = generateField(renderBlueprintDateInput);
|
|
32892
32890
|
const DateRangeInputField = generateField(renderBlueprintDateRangeInput);
|
|
32893
32891
|
const CheckboxField = generateField(renderBlueprintCheckbox, {
|
|
@@ -53012,35 +53010,37 @@ const PreviewCsvData = /* @__PURE__ */ __name((props) => {
|
|
|
53012
53010
|
userSchema = exampleData,
|
|
53013
53011
|
entities
|
|
53014
53012
|
} = props;
|
|
53015
|
-
const data =
|
|
53016
|
-
|
|
53017
|
-
|
|
53018
|
-
|
|
53019
|
-
|
|
53020
|
-
|
|
53021
|
-
|
|
53022
|
-
|
|
53023
|
-
|
|
53024
|
-
toRet[path2] = row[matchingKey];
|
|
53025
|
-
}
|
|
53026
|
-
if (toRet[path2] === void 0 || toRet[path2] === "") {
|
|
53027
|
-
if (defaultValue2) {
|
|
53028
|
-
if (isFunction$4(defaultValue2)) {
|
|
53029
|
-
toRet[path2] = defaultValue2(i, row);
|
|
53030
|
-
} else
|
|
53031
|
-
toRet[path2] = defaultValue2;
|
|
53013
|
+
const data = useMemo(() => {
|
|
53014
|
+
return userSchema.userData && userSchema.userData.length && userSchema.userData.map((row, i) => {
|
|
53015
|
+
const toRet = {
|
|
53016
|
+
_isClean: row._isClean
|
|
53017
|
+
};
|
|
53018
|
+
validateAgainstSchema.fields.forEach(({ path: path2, defaultValue: defaultValue2 }) => {
|
|
53019
|
+
const matchingKey = matchedHeaders == null ? void 0 : matchedHeaders[path2];
|
|
53020
|
+
if (!matchingKey) {
|
|
53021
|
+
toRet[path2] = defaultValue2 === void 0 ? defaultValue2 : "";
|
|
53032
53022
|
} else {
|
|
53033
|
-
toRet[path2] =
|
|
53023
|
+
toRet[path2] = row[matchingKey];
|
|
53034
53024
|
}
|
|
53025
|
+
if (toRet[path2] === void 0 || toRet[path2] === "") {
|
|
53026
|
+
if (defaultValue2) {
|
|
53027
|
+
if (isFunction$4(defaultValue2)) {
|
|
53028
|
+
toRet[path2] = defaultValue2(i, row);
|
|
53029
|
+
} else
|
|
53030
|
+
toRet[path2] = defaultValue2;
|
|
53031
|
+
} else {
|
|
53032
|
+
toRet[path2] = "";
|
|
53033
|
+
}
|
|
53034
|
+
}
|
|
53035
|
+
});
|
|
53036
|
+
if (row.id === void 0) {
|
|
53037
|
+
toRet.id = nanoid();
|
|
53038
|
+
} else {
|
|
53039
|
+
toRet.id = row.id;
|
|
53035
53040
|
}
|
|
53041
|
+
return toRet;
|
|
53036
53042
|
});
|
|
53037
|
-
|
|
53038
|
-
toRet.id = nanoid();
|
|
53039
|
-
} else {
|
|
53040
|
-
toRet.id = row.id;
|
|
53041
|
-
}
|
|
53042
|
-
return toRet;
|
|
53043
|
-
});
|
|
53043
|
+
}, [matchedHeaders, userSchema, validateAgainstSchema.fields]);
|
|
53044
53044
|
return /* @__PURE__ */ React__default.createElement("div", { style: { minWidth: 400 } }, /* @__PURE__ */ React__default.createElement(Callout, { style: { marginBottom: 5 }, intent: "primary" }, headerMessage || (showDoesDataLookCorrectMsg ? "Does this data look correct? Edit it as needed." : `${isEditingExistingFile ? "Edit" : "Input"} your data here. Hover table headers for additional instructions.`)), validateAgainstSchema.description && /* @__PURE__ */ React__default.createElement(Callout, null, validateAgainstSchema.description), /* @__PURE__ */ React__default.createElement(
|
|
53045
53045
|
"div",
|
|
53046
53046
|
{
|
|
@@ -53084,13 +53084,15 @@ const SimpleInsertDataDialog = compose(
|
|
|
53084
53084
|
showDoesDataLookCorrectMsg,
|
|
53085
53085
|
submitting,
|
|
53086
53086
|
userSchema,
|
|
53087
|
-
validateAgainstSchema
|
|
53087
|
+
validateAgainstSchema,
|
|
53088
|
+
initialValues
|
|
53088
53089
|
}) => {
|
|
53090
|
+
var _a;
|
|
53089
53091
|
const dispatch = useDispatch();
|
|
53090
53092
|
const _reduxFormEntities = useSelector(
|
|
53091
53093
|
(state) => {
|
|
53092
|
-
var
|
|
53093
|
-
return (_b = (
|
|
53094
|
+
var _a2, _b;
|
|
53095
|
+
return (_b = (_a2 = state.form) == null ? void 0 : _a2[dataTableForm]) == null ? void 0 : _b.values.reduxFormEntities;
|
|
53094
53096
|
}
|
|
53095
53097
|
);
|
|
53096
53098
|
const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
|
|
@@ -53099,8 +53101,8 @@ const SimpleInsertDataDialog = compose(
|
|
|
53099
53101
|
}, [dataTableForm, dispatch]);
|
|
53100
53102
|
const _reduxFormCellValidation = useSelector(
|
|
53101
53103
|
(state) => {
|
|
53102
|
-
var
|
|
53103
|
-
return (_b = (
|
|
53104
|
+
var _a2, _b;
|
|
53105
|
+
return (_b = (_a2 = state.form) == null ? void 0 : _a2[dataTableForm]) == null ? void 0 : _b.values.reduxFormCellValidation;
|
|
53104
53106
|
}
|
|
53105
53107
|
);
|
|
53106
53108
|
const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
|
|
@@ -53115,7 +53117,7 @@ const SimpleInsertDataDialog = compose(
|
|
|
53115
53117
|
rightElement: /* @__PURE__ */ React__default.createElement("div", { style: { paddingTop: 6, paddingRight: 5 } }, ".csv"),
|
|
53116
53118
|
inlineLabel: true,
|
|
53117
53119
|
label: "File Name:",
|
|
53118
|
-
defaultValue: "manual_data_entry",
|
|
53120
|
+
defaultValue: (_a = initialValues == null ? void 0 : initialValues.fileName) != null ? _a : "manual_data_entry",
|
|
53119
53121
|
name: "fileName"
|
|
53120
53122
|
}
|
|
53121
53123
|
), /* @__PURE__ */ React__default.createElement(
|
|
@@ -53299,6 +53301,7 @@ const MultipleFileDialog = /* @__PURE__ */ __name(({
|
|
|
53299
53301
|
steps,
|
|
53300
53302
|
hasSubmittedOuter
|
|
53301
53303
|
}) => {
|
|
53304
|
+
console.log({ filesWIssues, finishedFiles });
|
|
53302
53305
|
const tabs = /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(Callout, { style: { marginBottom: 10, flexGrow: 0 }, intent: "warning" }, /* @__PURE__ */ React__default.createElement("div", null, "Please look over each of the following files and correct any issues.")), /* @__PURE__ */ React__default.createElement(
|
|
53303
53306
|
Tabs,
|
|
53304
53307
|
{
|
|
@@ -53474,6 +53477,7 @@ const UploadCsvWizardDialog = compose(
|
|
|
53474
53477
|
if (_filesWIssues.length > 0) {
|
|
53475
53478
|
const reduxFormEntitiesArray2 = [];
|
|
53476
53479
|
const finishedFiles2 = _filesWIssues.map((f2, i) => {
|
|
53480
|
+
console.log("aa");
|
|
53477
53481
|
const { reduxFormEntities, reduxFormCellValidation } = formValueSelector(`editableCellTable-${i}`)(
|
|
53478
53482
|
state,
|
|
53479
53483
|
"reduxFormEntities",
|
|
@@ -53487,12 +53491,13 @@ const UploadCsvWizardDialog = compose(
|
|
|
53487
53491
|
return entsToUse && entsToUse.length && !some(validationToUse, (v2) => v2) && entsToUse;
|
|
53488
53492
|
});
|
|
53489
53493
|
return {
|
|
53490
|
-
|
|
53491
|
-
|
|
53494
|
+
_reduxFormEntitiesArray: reduxFormEntitiesArray2,
|
|
53495
|
+
_finishedFiles: finishedFiles2
|
|
53492
53496
|
};
|
|
53493
53497
|
}
|
|
53494
53498
|
});
|
|
53495
53499
|
const reduxFormEntitiesArray = useDeepEqualMemo(_reduxFormEntitiesArray);
|
|
53500
|
+
console.log({ _finishedFiles });
|
|
53496
53501
|
const finishedFiles = useDeepEqualMemo(_finishedFiles);
|
|
53497
53502
|
const [hasSubmittedOuter, setSubmittedOuter] = useState();
|
|
53498
53503
|
const [steps, setSteps] = useState(getInitialSteps(true));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teselagen/ui",
|
|
3
|
-
"version": "0.5.21-beta.
|
|
3
|
+
"version": "0.5.21-beta.2",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@teselagen/file-utils": "0.3.16",
|
|
14
14
|
"@teselagen/bounce-loader": "0.3.11",
|
|
15
|
+
"@blueprintjs/core": "3.52.0",
|
|
16
|
+
"@blueprintjs/datetime": "3.23.19",
|
|
17
|
+
"@blueprintjs/icons": "3.33.0",
|
|
18
|
+
"@blueprintjs/select": "3.18.11",
|
|
15
19
|
"@dnd-kit/core": "^6.1.0",
|
|
16
20
|
"@dnd-kit/modifiers": "^7.0.0",
|
|
17
21
|
"@dnd-kit/sortable": "^8.0.0",
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { Button, FormGroup, Position, Tooltip } from "@blueprintjs/core";
|
|
2
|
+
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
AssignDefaultsModeContext,
|
|
5
|
+
WorkflowDefaultParamsContext,
|
|
6
|
+
workflowDefaultParamsObj
|
|
7
|
+
} from "../AssignDefaultsModeContext";
|
|
8
|
+
import { difference, isEqual, kebabCase } from "lodash-es";
|
|
9
|
+
import {
|
|
10
|
+
fakeWait,
|
|
11
|
+
getIntent,
|
|
12
|
+
getIntentClass,
|
|
13
|
+
LabelWithTooltipInfo
|
|
14
|
+
} from "./utils";
|
|
15
|
+
import useDeepCompareEffect from "use-deep-compare-effect";
|
|
16
|
+
import { change } from "redux-form";
|
|
17
|
+
import popoverOverflowModifiers from "../utils/popoverOverflowModifiers";
|
|
18
|
+
import classNames from "classnames";
|
|
19
|
+
|
|
20
|
+
const AbstractInput = props => {
|
|
21
|
+
const {
|
|
22
|
+
defaultValue,
|
|
23
|
+
meta: { dispatch, form, touched, error, warning },
|
|
24
|
+
onDefaultValChanged,
|
|
25
|
+
onFieldSubmit,
|
|
26
|
+
children,
|
|
27
|
+
tooltipProps,
|
|
28
|
+
tooltipError,
|
|
29
|
+
disabled,
|
|
30
|
+
intent,
|
|
31
|
+
tooltipInfo,
|
|
32
|
+
label,
|
|
33
|
+
inlineLabel,
|
|
34
|
+
isLabelTooltip,
|
|
35
|
+
secondaryLabel,
|
|
36
|
+
className,
|
|
37
|
+
showErrorIfUntouched,
|
|
38
|
+
asyncValidating,
|
|
39
|
+
containerStyle,
|
|
40
|
+
leftEl,
|
|
41
|
+
rightEl,
|
|
42
|
+
labelStyle,
|
|
43
|
+
noOuterLabel,
|
|
44
|
+
fileLimit,
|
|
45
|
+
noMarginBottom,
|
|
46
|
+
assignDefaultButton,
|
|
47
|
+
showGenerateDefaultDot,
|
|
48
|
+
setAssignDefaultsMode,
|
|
49
|
+
startAssigningDefault,
|
|
50
|
+
input,
|
|
51
|
+
noFillField,
|
|
52
|
+
isRequired,
|
|
53
|
+
isLoadingDefaultValue,
|
|
54
|
+
enableReinitialize,
|
|
55
|
+
defaultValCount
|
|
56
|
+
} = props;
|
|
57
|
+
|
|
58
|
+
const prevProps = useRef({ defaultValue, defaultValCount });
|
|
59
|
+
|
|
60
|
+
const updateDefaultValue = useCallback(() => {
|
|
61
|
+
dispatch(change(form, input.name, defaultValue));
|
|
62
|
+
onDefaultValChanged &&
|
|
63
|
+
onDefaultValChanged(defaultValue, input.name, form, props);
|
|
64
|
+
onFieldSubmit && onFieldSubmit(defaultValue);
|
|
65
|
+
}, [
|
|
66
|
+
defaultValue,
|
|
67
|
+
dispatch,
|
|
68
|
+
form,
|
|
69
|
+
input.name,
|
|
70
|
+
onDefaultValChanged,
|
|
71
|
+
onFieldSubmit,
|
|
72
|
+
props
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (
|
|
77
|
+
((input.value !== false && !input.value) || enableReinitialize) &&
|
|
78
|
+
defaultValue !== undefined
|
|
79
|
+
) {
|
|
80
|
+
updateDefaultValue();
|
|
81
|
+
}
|
|
82
|
+
}, [defaultValue, enableReinitialize, input.value, updateDefaultValue]);
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const {
|
|
86
|
+
defaultValue: oldDefaultValue,
|
|
87
|
+
defaultValCount: oldDefaultValCount
|
|
88
|
+
} = prevProps.current;
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
((input.value !== false && !input.value) ||
|
|
92
|
+
enableReinitialize ||
|
|
93
|
+
defaultValCount !== oldDefaultValCount) &&
|
|
94
|
+
!isEqual(defaultValue, oldDefaultValue)
|
|
95
|
+
) {
|
|
96
|
+
updateDefaultValue();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
prevProps.current = { defaultValue, defaultValCount };
|
|
100
|
+
}, [
|
|
101
|
+
defaultValue,
|
|
102
|
+
defaultValCount,
|
|
103
|
+
enableReinitialize,
|
|
104
|
+
input.value,
|
|
105
|
+
updateDefaultValue
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
// if our custom field level validation is happening then we don't want to show the error visually
|
|
109
|
+
const showError =
|
|
110
|
+
(touched || showErrorIfUntouched) && error && !asyncValidating;
|
|
111
|
+
const showWarning = (touched || showErrorIfUntouched) && warning;
|
|
112
|
+
let componentToWrap =
|
|
113
|
+
isLabelTooltip || tooltipError ? (
|
|
114
|
+
<Tooltip
|
|
115
|
+
disabled={isLabelTooltip ? false : !showError}
|
|
116
|
+
intent={isLabelTooltip ? "none" : error ? "danger" : "warning"}
|
|
117
|
+
content={isLabelTooltip ? label : error || warning}
|
|
118
|
+
position={Position.TOP}
|
|
119
|
+
modifiers={popoverOverflowModifiers}
|
|
120
|
+
{...tooltipProps}
|
|
121
|
+
>
|
|
122
|
+
{children}
|
|
123
|
+
</Tooltip>
|
|
124
|
+
) : (
|
|
125
|
+
children
|
|
126
|
+
);
|
|
127
|
+
const testClassName = "tg-test-" + kebabCase(input.name);
|
|
128
|
+
if (noFillField) {
|
|
129
|
+
componentToWrap = <div className="tg-no-fill-field">{componentToWrap}</div>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let helperText;
|
|
133
|
+
if (!tooltipError) {
|
|
134
|
+
if (showError) {
|
|
135
|
+
helperText = error;
|
|
136
|
+
} else if (showWarning) {
|
|
137
|
+
helperText = warning;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// if in a cypress test show message so that inputs will not be interactable
|
|
142
|
+
if (window.Cypress && isLoadingDefaultValue) {
|
|
143
|
+
return "Loading default value...";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let labelInfo = secondaryLabel;
|
|
147
|
+
|
|
148
|
+
const hasOuterLabel = !noOuterLabel && !isLabelTooltip;
|
|
149
|
+
function getFileLimitInfo() {
|
|
150
|
+
if (!fileLimit) return "";
|
|
151
|
+
return `max ${fileLimit} file${fileLimit === 1 ? "" : "s"}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (isRequired && hasOuterLabel && label && !labelInfo) {
|
|
155
|
+
labelInfo = `(required${fileLimit ? `, ${getFileLimitInfo()}` : ""})`;
|
|
156
|
+
} else if (!labelInfo && fileLimit) {
|
|
157
|
+
labelInfo = `(${getFileLimitInfo()})`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<FormGroup
|
|
162
|
+
className={classNames(className, testClassName, {
|
|
163
|
+
"tg-flex-form-content": leftEl || rightEl,
|
|
164
|
+
"tg-tooltipError": tooltipError,
|
|
165
|
+
"tg-has-error": showError && error
|
|
166
|
+
})}
|
|
167
|
+
disabled={disabled}
|
|
168
|
+
helperText={helperText}
|
|
169
|
+
intent={intent}
|
|
170
|
+
label={
|
|
171
|
+
hasOuterLabel && (
|
|
172
|
+
<LabelWithTooltipInfo
|
|
173
|
+
labelStyle={labelStyle}
|
|
174
|
+
label={label}
|
|
175
|
+
tooltipInfo={tooltipInfo}
|
|
176
|
+
/>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
inline={inlineLabel}
|
|
180
|
+
labelInfo={labelInfo}
|
|
181
|
+
style={{
|
|
182
|
+
...(noMarginBottom && { marginBottom: 0 }),
|
|
183
|
+
...containerStyle
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
{showGenerateDefaultDot && (
|
|
187
|
+
<div style={{ zIndex: 10, position: "relative", height: 0, width: 0 }}>
|
|
188
|
+
<div style={{ position: "absolute", left: "0px", top: "0px" }}>
|
|
189
|
+
<Tooltip
|
|
190
|
+
modifiers={popoverOverflowModifiers}
|
|
191
|
+
content="Allows a Default to be Set. Click to Enter Set Default Mode (or press Shift+D when outside the input field)"
|
|
192
|
+
>
|
|
193
|
+
<div
|
|
194
|
+
onClick={() => {
|
|
195
|
+
setAssignDefaultsMode(true);
|
|
196
|
+
startAssigningDefault();
|
|
197
|
+
}}
|
|
198
|
+
className="generateDefaultDot"
|
|
199
|
+
></div>
|
|
200
|
+
</Tooltip>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
{assignDefaultButton}
|
|
205
|
+
{leftEl} {componentToWrap} {rightEl}
|
|
206
|
+
</FormGroup>
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export const withAbstractWrapper = (ComponentToWrap, opts = {}) => {
|
|
211
|
+
return props => {
|
|
212
|
+
const {
|
|
213
|
+
massageDefaultIdValue,
|
|
214
|
+
generateDefaultValue,
|
|
215
|
+
defaultValueByIdOverride,
|
|
216
|
+
defaultValue: defaultValueFromProps,
|
|
217
|
+
isRequired,
|
|
218
|
+
...rest
|
|
219
|
+
} = props;
|
|
220
|
+
|
|
221
|
+
//get is assign defaults mode
|
|
222
|
+
//if assign default value mode then add on to the component
|
|
223
|
+
const [defaultValCount, setDefaultValCount] = useState(0);
|
|
224
|
+
const [defaultValueFromBackend, setDefault] = useState();
|
|
225
|
+
const [allowUserOverride, setUserOverride] = useState(true);
|
|
226
|
+
const [isLoadingDefaultValue, setLoadingDefaultValue] = useState(false);
|
|
227
|
+
const { inAssignDefaultsMode, setAssignDefaultsMode } = useContext(
|
|
228
|
+
AssignDefaultsModeContext
|
|
229
|
+
);
|
|
230
|
+
// tnr: we might want to grab this context object off the window in the future and have it live in lims by default
|
|
231
|
+
// there is no reason for those vals to live in TRC. Example code below:
|
|
232
|
+
// const workflowParams = useContext(window.__tgDefaultValParamsContext || defaultNullContext);
|
|
233
|
+
const workflowParams = useContext(WorkflowDefaultParamsContext);
|
|
234
|
+
|
|
235
|
+
const caresAboutToolContext = generateDefaultValue?.params?.toolName;
|
|
236
|
+
|
|
237
|
+
const customParamsToUse = {
|
|
238
|
+
...(caresAboutToolContext
|
|
239
|
+
? { ...workflowDefaultParamsObj, ...workflowParams }
|
|
240
|
+
: {}),
|
|
241
|
+
...(generateDefaultValue ? generateDefaultValue.customParams : {})
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
async function triggerGetDefault() {
|
|
245
|
+
if (!defaultValueByIdOverride) {
|
|
246
|
+
//if defaultValueByIdOverride is passed, we can skip over getting the value from the backend straight to massaging the default value
|
|
247
|
+
if (!window.__triggerGetDefaultValueRequest) return;
|
|
248
|
+
if (!generateDefaultValue) return;
|
|
249
|
+
setLoadingDefaultValue(true);
|
|
250
|
+
//custom params should match params keys. if not throw an error
|
|
251
|
+
const doParamsMatch = isEqual(
|
|
252
|
+
Object.keys({
|
|
253
|
+
...(caresAboutToolContext ? workflowDefaultParamsObj : {}), //we don't want to compare these keys so we just spread them here
|
|
254
|
+
...(generateDefaultValue.params || {})
|
|
255
|
+
}).sort(),
|
|
256
|
+
Object.keys(customParamsToUse).sort()
|
|
257
|
+
);
|
|
258
|
+
if (!doParamsMatch) {
|
|
259
|
+
console.warn(
|
|
260
|
+
`Issue with generateDefaultValue. customParams don't match params`
|
|
261
|
+
);
|
|
262
|
+
console.warn(
|
|
263
|
+
`generateDefaultValue.params:`,
|
|
264
|
+
generateDefaultValue.params
|
|
265
|
+
);
|
|
266
|
+
console.warn(`generateDefaultValue.customParams:`, customParamsToUse);
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Issue with generateDefaultValue code=${
|
|
269
|
+
generateDefaultValue.code
|
|
270
|
+
}: Difference detected with: ${difference(
|
|
271
|
+
Object.keys(generateDefaultValue.params || {}),
|
|
272
|
+
Object.keys(customParamsToUse || {})
|
|
273
|
+
).join(
|
|
274
|
+
", "
|
|
275
|
+
)}. customParams passed into the field should match params (as defined in defaultValueConstants.js). See console for more details.`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
let { defaultValue, allowUserOverride } = defaultValueByIdOverride
|
|
282
|
+
? { defaultValue: defaultValueByIdOverride }
|
|
283
|
+
: await window.__triggerGetDefaultValueRequest(
|
|
284
|
+
generateDefaultValue.code,
|
|
285
|
+
customParamsToUse
|
|
286
|
+
);
|
|
287
|
+
if (massageDefaultIdValue) {
|
|
288
|
+
const massagedRes = await massageDefaultIdValue({
|
|
289
|
+
defaultValueById: defaultValue
|
|
290
|
+
});
|
|
291
|
+
if (massagedRes.defaultValue) {
|
|
292
|
+
defaultValue = massagedRes.defaultValue;
|
|
293
|
+
}
|
|
294
|
+
if (massagedRes.preventUserOverrideFromBeingDisabled) {
|
|
295
|
+
allowUserOverride = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// TODO:Add ths back in when we have a better way to determine if a field is a checkbox or switch
|
|
300
|
+
// if (
|
|
301
|
+
// "false" === false
|
|
302
|
+
// // ComponentToWrap === renderBlueprintCheckbox ||
|
|
303
|
+
// // ComponentToWrap === renderBlueprintSwitch
|
|
304
|
+
// ) {
|
|
305
|
+
// setDefault(defaultValue === "true");
|
|
306
|
+
// } else {
|
|
307
|
+
if (typeof defaultValue === "string") {
|
|
308
|
+
// remove double spaces and leading/trailing
|
|
309
|
+
defaultValue = defaultValue.replace(/\s+/g, " ").trim();
|
|
310
|
+
}
|
|
311
|
+
setDefault(defaultValue);
|
|
312
|
+
// }
|
|
313
|
+
setUserOverride(allowUserOverride);
|
|
314
|
+
setDefaultValCount(defaultValCount + 1);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error(`error aswf298f:`, error);
|
|
317
|
+
}
|
|
318
|
+
if (window.Cypress && window.Cypress.addFakeDefaultValueWait) {
|
|
319
|
+
await fakeWait();
|
|
320
|
+
}
|
|
321
|
+
setLoadingDefaultValue(false);
|
|
322
|
+
}
|
|
323
|
+
// if generateDefaultValue, hit the backend for that value
|
|
324
|
+
useDeepCompareEffect(() => {
|
|
325
|
+
// if the input already has a value we don't want to override with the default value request
|
|
326
|
+
if (rest.input.value) return;
|
|
327
|
+
triggerGetDefault();
|
|
328
|
+
}, [generateDefaultValue || {}]);
|
|
329
|
+
// const asyncValidating = props.asyncValidating;
|
|
330
|
+
const defaultProps = {
|
|
331
|
+
...rest,
|
|
332
|
+
defaultValue: defaultValueFromBackend || defaultValueFromProps,
|
|
333
|
+
disabled: props.disabled || allowUserOverride === false,
|
|
334
|
+
readOnly: props.readOnly || isLoadingDefaultValue,
|
|
335
|
+
intent: getIntent(props),
|
|
336
|
+
intentClass: getIntentClass(props)
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// don't show intent while async validating
|
|
340
|
+
// if (asyncValidating) {
|
|
341
|
+
// delete defaultProps.intent;
|
|
342
|
+
// delete defaultProps.intentClass;
|
|
343
|
+
// }
|
|
344
|
+
|
|
345
|
+
const startAssigningDefault = () =>
|
|
346
|
+
window.__showAssignDefaultValueModal &&
|
|
347
|
+
window.__showAssignDefaultValueModal({
|
|
348
|
+
...props,
|
|
349
|
+
generateDefaultValue: {
|
|
350
|
+
...props.generateDefaultValue,
|
|
351
|
+
customParams: customParamsToUse
|
|
352
|
+
},
|
|
353
|
+
onFinish: () => {
|
|
354
|
+
triggerGetDefault();
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return (
|
|
359
|
+
<AbstractInput
|
|
360
|
+
{...{
|
|
361
|
+
...opts,
|
|
362
|
+
defaultValCount,
|
|
363
|
+
isRequired,
|
|
364
|
+
...defaultProps,
|
|
365
|
+
isLoadingDefaultValue,
|
|
366
|
+
showGenerateDefaultDot:
|
|
367
|
+
!inAssignDefaultsMode &&
|
|
368
|
+
window.__showGenerateDefaultDot &&
|
|
369
|
+
window.__showGenerateDefaultDot() &&
|
|
370
|
+
!!generateDefaultValue,
|
|
371
|
+
setAssignDefaultsMode,
|
|
372
|
+
startAssigningDefault,
|
|
373
|
+
assignDefaultButton: inAssignDefaultsMode && generateDefaultValue && (
|
|
374
|
+
<Button
|
|
375
|
+
onClick={startAssigningDefault}
|
|
376
|
+
small
|
|
377
|
+
style={{ background: "yellow", color: "black" }}
|
|
378
|
+
>
|
|
379
|
+
Assign Default
|
|
380
|
+
</Button>
|
|
381
|
+
)
|
|
382
|
+
}}
|
|
383
|
+
>
|
|
384
|
+
<ComponentToWrap {...defaultProps} />
|
|
385
|
+
</AbstractInput>
|
|
386
|
+
);
|
|
387
|
+
};
|
|
388
|
+
};
|
|
@@ -1199,9 +1199,7 @@ export const withAbstractWrapper = (ComponentToWrap, opts = {}) => {
|
|
|
1199
1199
|
};
|
|
1200
1200
|
|
|
1201
1201
|
export const InputField = generateField(RenderBlueprintInput);
|
|
1202
|
-
export const FileUploadField = generateField(renderFileUpload
|
|
1203
|
-
showErrorIfUntouched: true
|
|
1204
|
-
});
|
|
1202
|
+
export const FileUploadField = generateField(renderFileUpload);
|
|
1205
1203
|
export const DateInputField = generateField(renderBlueprintDateInput);
|
|
1206
1204
|
export const DateRangeInputField = generateField(renderBlueprintDateRangeInput);
|
|
1207
1205
|
export const CheckboxField = generateField(renderBlueprintCheckbox, {
|
package/src/UploadCsvWizard.js
CHANGED
|
@@ -69,46 +69,50 @@ export const PreviewCsvData = props => {
|
|
|
69
69
|
userSchema = exampleData,
|
|
70
70
|
entities
|
|
71
71
|
} = props;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
if (toRet[path] === undefined || toRet[path] === "") {
|
|
87
|
-
if (defaultValue) {
|
|
88
|
-
if (isFunction(defaultValue)) {
|
|
89
|
-
toRet[path] = defaultValue(i, row);
|
|
90
|
-
} else toRet[path] = defaultValue;
|
|
72
|
+
|
|
73
|
+
const data = useMemo(() => {
|
|
74
|
+
return (
|
|
75
|
+
userSchema.userData &&
|
|
76
|
+
userSchema.userData.length &&
|
|
77
|
+
userSchema.userData.map((row, i) => {
|
|
78
|
+
const toRet = {
|
|
79
|
+
_isClean: row._isClean
|
|
80
|
+
};
|
|
81
|
+
validateAgainstSchema.fields.forEach(({ path, defaultValue }) => {
|
|
82
|
+
const matchingKey = matchedHeaders?.[path];
|
|
83
|
+
if (!matchingKey) {
|
|
84
|
+
toRet[path] = defaultValue === undefined ? defaultValue : "";
|
|
91
85
|
} else {
|
|
92
|
-
|
|
93
|
-
// ? example[i1]
|
|
94
|
-
// : i1 === 0 && example;
|
|
95
|
-
toRet[path] = "";
|
|
96
|
-
// if (useExampleData && exampleToUse) {
|
|
97
|
-
// toRet[path] = exampleToUse;
|
|
98
|
-
// delete toRet._isClean;
|
|
99
|
-
// } else {
|
|
100
|
-
// }
|
|
86
|
+
toRet[path] = row[matchingKey];
|
|
101
87
|
}
|
|
102
|
-
|
|
103
|
-
|
|
88
|
+
if (toRet[path] === undefined || toRet[path] === "") {
|
|
89
|
+
if (defaultValue) {
|
|
90
|
+
if (isFunction(defaultValue)) {
|
|
91
|
+
toRet[path] = defaultValue(i, row);
|
|
92
|
+
} else toRet[path] = defaultValue;
|
|
93
|
+
} else {
|
|
94
|
+
// const exampleToUse = isArray(example) //this means that the row was not added by a user
|
|
95
|
+
// ? example[i1]
|
|
96
|
+
// : i1 === 0 && example;
|
|
97
|
+
toRet[path] = "";
|
|
98
|
+
// if (useExampleData && exampleToUse) {
|
|
99
|
+
// toRet[path] = exampleToUse;
|
|
100
|
+
// delete toRet._isClean;
|
|
101
|
+
// } else {
|
|
102
|
+
// }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
if (row.id === undefined) {
|
|
108
|
+
toRet.id = nanoid();
|
|
109
|
+
} else {
|
|
110
|
+
toRet.id = row.id;
|
|
111
|
+
}
|
|
112
|
+
return toRet;
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
}, [matchedHeaders, userSchema, validateAgainstSchema.fields]);
|
|
112
116
|
|
|
113
117
|
return (
|
|
114
118
|
<div style={{ minWidth: 400 }}>
|
|
@@ -167,7 +171,8 @@ export const SimpleInsertDataDialog = compose(
|
|
|
167
171
|
showDoesDataLookCorrectMsg,
|
|
168
172
|
submitting,
|
|
169
173
|
userSchema,
|
|
170
|
-
validateAgainstSchema
|
|
174
|
+
validateAgainstSchema,
|
|
175
|
+
initialValues
|
|
171
176
|
}) => {
|
|
172
177
|
const dispatch = useDispatch();
|
|
173
178
|
const _reduxFormEntities = useSelector(
|
|
@@ -197,7 +202,7 @@ export const SimpleInsertDataDialog = compose(
|
|
|
197
202
|
}
|
|
198
203
|
inlineLabel
|
|
199
204
|
label="File Name:"
|
|
200
|
-
defaultValue={"manual_data_entry"}
|
|
205
|
+
defaultValue={initialValues?.fileName ?? "manual_data_entry"}
|
|
201
206
|
name="fileName"
|
|
202
207
|
/>
|
|
203
208
|
<PreviewCsvData
|
|
@@ -413,6 +418,7 @@ const MultipleFileDialog = ({
|
|
|
413
418
|
steps,
|
|
414
419
|
hasSubmittedOuter
|
|
415
420
|
}) => {
|
|
421
|
+
console.log({ filesWIssues, finishedFiles });
|
|
416
422
|
const tabs = (
|
|
417
423
|
<>
|
|
418
424
|
<Callout style={{ marginBottom: 10, flexGrow: 0 }} intent="warning">
|
|
@@ -628,6 +634,7 @@ const UploadCsvWizardDialog = compose(
|
|
|
628
634
|
if (_filesWIssues.length > 0) {
|
|
629
635
|
const reduxFormEntitiesArray = [];
|
|
630
636
|
const finishedFiles = _filesWIssues.map((f, i) => {
|
|
637
|
+
console.log("aa");
|
|
631
638
|
const { reduxFormEntities, reduxFormCellValidation } =
|
|
632
639
|
formValueSelector(`editableCellTable-${i}`)(
|
|
633
640
|
state,
|
|
@@ -647,12 +654,13 @@ const UploadCsvWizardDialog = compose(
|
|
|
647
654
|
);
|
|
648
655
|
});
|
|
649
656
|
return {
|
|
650
|
-
reduxFormEntitiesArray,
|
|
651
|
-
finishedFiles
|
|
657
|
+
_reduxFormEntitiesArray: reduxFormEntitiesArray,
|
|
658
|
+
_finishedFiles: finishedFiles
|
|
652
659
|
};
|
|
653
660
|
}
|
|
654
661
|
});
|
|
655
662
|
const reduxFormEntitiesArray = useDeepEqualMemo(_reduxFormEntitiesArray);
|
|
663
|
+
console.log({ _finishedFiles });
|
|
656
664
|
const finishedFiles = useDeepEqualMemo(_finishedFiles);
|
|
657
665
|
|
|
658
666
|
const [hasSubmittedOuter, setSubmittedOuter] = useState();
|