@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.
Files changed (98) hide show
  1. package/DataTable/ColumnFilterMenu.d.ts +2 -1
  2. package/DataTable/Columns.d.ts +51 -0
  3. package/DataTable/DisplayOptions.d.ts +14 -14
  4. package/DataTable/EditabelCell.d.ts +2 -5
  5. package/DataTable/EditableCell.d.ts +7 -0
  6. package/DataTable/FilterAndSortMenu.d.ts +9 -9
  7. package/DataTable/PagingTool.d.ts +25 -2
  8. package/DataTable/RenderCell.d.ts +18 -0
  9. package/DataTable/SearchBar.d.ts +4 -3
  10. package/DataTable/SortableColumns.d.ts +6 -9
  11. package/DataTable/ThComponent.d.ts +9 -0
  12. package/DataTable/index.d.ts +0 -5
  13. package/DataTable/utils/getIdOrCodeOrIndex.d.ts +1 -2
  14. package/DataTable/utils/handleCopyTable.d.ts +1 -0
  15. package/DataTable/utils/index.d.ts +4 -2
  16. package/DataTable/utils/primarySelectedValue.d.ts +1 -0
  17. package/DataTable/utils/queryParams.d.ts +13 -8
  18. package/DataTable/utils/removeCleanRows.d.ts +1 -1
  19. package/DataTable/utils/rowClick.d.ts +24 -3
  20. package/DataTable/utils/useDeepEqualMemo.d.ts +1 -0
  21. package/DataTable/utils/useTableEntities.d.ts +5 -0
  22. package/DataTable/utils/useTableParams.d.ts +49 -0
  23. package/DataTable/utils/withTableParams.d.ts +3 -16
  24. package/DataTable/viewColumn.d.ts +11 -4
  25. package/FormComponents/AbstractField.d.ts +1 -0
  26. package/FormComponents/Uploader.d.ts +34 -1
  27. package/FormComponents/index.d.ts +111 -60
  28. package/MatchHeaders.d.ts +9 -10
  29. package/SimpleStepViz.d.ts +2 -1
  30. package/TgSuggest/index.d.ts +1 -21
  31. package/UploadCsvWizard.d.ts +1 -1
  32. package/index.cjs.js +47861 -49125
  33. package/index.d.ts +6 -3
  34. package/index.es.js +47959 -49223
  35. package/package.json +6 -5
  36. package/src/DataTable/CellDragHandle.js +70 -69
  37. package/src/DataTable/ColumnFilterMenu.js +23 -21
  38. package/src/DataTable/Columns.js +948 -0
  39. package/src/DataTable/Columns.jsx +945 -0
  40. package/src/DataTable/DisplayOptions.js +173 -192
  41. package/src/DataTable/EditabelCell.js +7 -18
  42. package/src/DataTable/EditabelCell.jsx +44 -0
  43. package/src/DataTable/EditableCell.js +44 -0
  44. package/src/DataTable/FilterAndSortMenu.js +215 -234
  45. package/src/DataTable/PagingTool.js +47 -56
  46. package/src/DataTable/RenderCell.js +191 -0
  47. package/src/DataTable/RenderCell.jsx +191 -0
  48. package/src/DataTable/SearchBar.js +12 -5
  49. package/src/DataTable/SortableColumns.js +44 -39
  50. package/src/DataTable/ThComponent.js +44 -0
  51. package/src/DataTable/dataTableEnhancer.js +32 -295
  52. package/src/DataTable/index.js +2945 -3596
  53. package/src/DataTable/utils/getIdOrCodeOrIndex.js +1 -1
  54. package/src/DataTable/utils/handleCopyTable.js +16 -0
  55. package/src/DataTable/utils/index.js +7 -3
  56. package/src/DataTable/utils/primarySelectedValue.js +1 -0
  57. package/src/DataTable/utils/queryParams.js +110 -85
  58. package/src/DataTable/utils/removeCleanRows.js +3 -3
  59. package/src/DataTable/utils/rowClick.js +34 -9
  60. package/src/DataTable/utils/selection.js +1 -1
  61. package/src/DataTable/utils/useDeepEqualMemo.js +10 -0
  62. package/src/DataTable/utils/useTableEntities.js +38 -0
  63. package/src/DataTable/utils/useTableParams.js +362 -0
  64. package/src/DataTable/utils/withTableParams.js +244 -274
  65. package/src/DataTable/validateTableWideErrors.js +1 -1
  66. package/src/DataTable/viewColumn.js +5 -9
  67. package/src/DialogFooter/index.js +3 -3
  68. package/src/FillWindow.js +2 -3
  69. package/src/FormComponents/AbstractField.js +388 -0
  70. package/src/FormComponents/Uploader.js +674 -649
  71. package/src/FormComponents/index.js +505 -654
  72. package/src/FormComponents/tryToMatchSchemas.js +1 -6
  73. package/src/MatchHeaders.js +27 -22
  74. package/src/SimpleStepViz.js +19 -23
  75. package/src/TgSelect/index.js +1 -1
  76. package/src/TgSuggest/index.js +94 -106
  77. package/src/UploadCsvWizard.js +571 -577
  78. package/src/enhancers/withDialog/tg_modalState.js +1 -0
  79. package/src/index.js +10 -4
  80. package/src/showDialogOnDocBody.js +5 -9
  81. package/src/useDialog.js +25 -26
  82. package/src/utils/commandControls.js +2 -2
  83. package/src/utils/handlerHelpers.js +19 -25
  84. package/src/utils/hooks/index.js +1 -0
  85. package/src/utils/hooks/useDeepEqualMemo.js +10 -0
  86. package/src/utils/hooks/useStableReference.js +9 -0
  87. package/src/utils/renderOnDoc.js +8 -5
  88. package/src/utils/tagUtils.js +3 -3
  89. package/src/utils/useTraceUpdate.js +19 -0
  90. package/src/wrapDialog.js +0 -2
  91. package/style.css +251 -251
  92. package/useDialog.d.ts +2 -6
  93. package/utils/hooks/index.d.ts +1 -0
  94. package/utils/hooks/useDeepEqualMemo.d.ts +1 -0
  95. package/utils/hooks/useStableReference.d.ts +1 -0
  96. package/utils/renderOnDoc.d.ts +1 -1
  97. package/utils/tagUtils.d.ts +5 -1
  98. package/utils/useTraceUpdate.d.ts +1 -0
@@ -1,344 +1,252 @@
1
- import React, { useRef, useState } from "react";
1
+ import React, { useState, useEffect, useMemo, useCallback } from "react";
2
2
  import { reduxForm, change, formValueSelector, destroy } from "redux-form";
3
3
  import { Callout, Icon, Intent, Tab, Tabs } from "@blueprintjs/core";
4
4
  import immer from "immer";
5
- import { observer } from "mobx-react";
6
5
  import "./UploadCsvWizard.css";
7
6
  import { isFunction } from "lodash-es";
8
7
  import { compose } from "recompose";
9
8
  import SimpleStepViz from "./SimpleStepViz";
10
9
  import { nanoid } from "nanoid";
11
- import { tgFormValueSelector } from "./utils/tgFormValues";
12
10
  import { some } from "lodash-es";
13
11
  import { times } from "lodash-es";
14
12
  import DialogFooter from "./DialogFooter";
15
- import DataTable, { removeCleanRows } from "./DataTable";
13
+ import DataTable from "./DataTable";
14
+ import { useDeepEqualMemo } from "./utils/hooks";
15
+ import { removeCleanRows } from "./DataTable/utils";
16
16
  import wrapDialog from "./wrapDialog";
17
17
  import { omit } from "lodash-es";
18
- import { connect } from "react-redux";
18
+ import { useDispatch, useSelector } from "react-redux";
19
19
  import { MatchHeaders } from "./MatchHeaders";
20
20
  import { isEmpty } from "lodash-es";
21
21
  import { addSpecialPropToAsyncErrs } from "./FormComponents/tryToMatchSchemas";
22
22
  import { cloneDeep } from "lodash-es";
23
23
  import { InputField } from "./FormComponents";
24
24
 
25
+ const asyncValidateHelper = async (
26
+ validateAgainstSchema,
27
+ currentEnts,
28
+ changeForm,
29
+ tableName
30
+ ) => {
31
+ if (!validateAgainstSchema.tableWideAsyncValidation) return;
32
+ const res = await validateAgainstSchema.tableWideAsyncValidation({
33
+ entities: currentEnts
34
+ });
35
+ if (!isEmpty(res)) {
36
+ changeForm(tableName, "reduxFormCellValidation", {
37
+ ...addSpecialPropToAsyncErrs(res)
38
+ });
39
+ return true;
40
+ }
41
+ };
42
+
43
+ const maybeStripIdFromEntities = (ents, validateAgainstSchema) => {
44
+ let toRet;
45
+ if (validateAgainstSchema?.fields?.some(({ path }) => path === "id")) {
46
+ toRet = ents;
47
+ } else {
48
+ // if the schema we're validating against itself didn't have an id field,
49
+ // we don't want to include it in the returned entities
50
+ toRet = ents?.map(e => omit(e, ["id"]));
51
+ }
52
+ return toRet?.map(e => omit(e, ["_isClean"]));
53
+ };
54
+
55
+ const exampleData = { userData: times(5).map(() => ({ _isClean: true })) };
56
+
25
57
  const getInitialSteps = csvValidationIssue => [
26
58
  { text: "Review Headers", active: csvValidationIssue },
27
59
  { text: "Review Data", active: !csvValidationIssue }
28
60
  ];
29
61
 
30
- const UploadCsvWizardDialog = compose(
62
+ export const PreviewCsvData = props => {
63
+ const {
64
+ matchedHeaders,
65
+ isEditingExistingFile,
66
+ showDoesDataLookCorrectMsg,
67
+ headerMessage,
68
+ datatableFormName = "editableCellTable",
69
+ validateAgainstSchema,
70
+ userSchema = exampleData,
71
+ entities
72
+ } = props;
73
+
74
+ const data = useMemo(() => {
75
+ return (
76
+ userSchema.userData &&
77
+ userSchema.userData.length &&
78
+ userSchema.userData.map((row, i) => {
79
+ const toRet = {
80
+ _isClean: row._isClean
81
+ };
82
+ validateAgainstSchema.fields.forEach(({ path, defaultValue }) => {
83
+ const matchingKey = matchedHeaders?.[path];
84
+ if (!matchingKey) {
85
+ toRet[path] = defaultValue === undefined ? defaultValue : "";
86
+ } else {
87
+ toRet[path] = row[matchingKey];
88
+ }
89
+ if (toRet[path] === undefined || toRet[path] === "") {
90
+ if (defaultValue) {
91
+ if (isFunction(defaultValue)) {
92
+ toRet[path] = defaultValue(i, row);
93
+ } else toRet[path] = defaultValue;
94
+ } else {
95
+ // const exampleToUse = isArray(example) //this means that the row was not added by a user
96
+ // ? example[i1]
97
+ // : i1 === 0 && example;
98
+ toRet[path] = "";
99
+ // if (useExampleData && exampleToUse) {
100
+ // toRet[path] = exampleToUse;
101
+ // delete toRet._isClean;
102
+ // } else {
103
+ // }
104
+ }
105
+ }
106
+ });
107
+
108
+ if (row.id === undefined) {
109
+ toRet.id = nanoid();
110
+ } else {
111
+ toRet.id = row.id;
112
+ }
113
+ return toRet;
114
+ })
115
+ );
116
+ }, [matchedHeaders, userSchema, validateAgainstSchema.fields]);
117
+
118
+ return (
119
+ <div style={{ minWidth: 400 }}>
120
+ <Callout style={{ marginBottom: 5 }} intent="primary">
121
+ {headerMessage ||
122
+ (showDoesDataLookCorrectMsg
123
+ ? "Does this data look correct? Edit it as needed."
124
+ : `${
125
+ isEditingExistingFile ? "Edit" : "Input"
126
+ } your data here. Hover table headers for additional instructions.`)}
127
+ </Callout>
128
+ {validateAgainstSchema.description && (
129
+ <Callout>{validateAgainstSchema.description}</Callout>
130
+ )}
131
+ <div
132
+ style={{
133
+ display: "flex",
134
+ justifyContent: "space-between",
135
+ alignItems: "flex-start"
136
+ }}
137
+ >
138
+ {validateAgainstSchema.HeaderComp && (
139
+ <validateAgainstSchema.HeaderComp {...props} />
140
+ )}
141
+ </div>
142
+ <DataTable
143
+ maxWidth={800}
144
+ maxHeight={500}
145
+ destroyOnUnmount={false}
146
+ doNotValidateUntouchedRows
147
+ formName={datatableFormName}
148
+ isSimple
149
+ isCellEditable
150
+ entities={(entities ? entities : data) || []}
151
+ schema={validateAgainstSchema}
152
+ />
153
+ </div>
154
+ );
155
+ };
156
+
157
+ export const SimpleInsertDataDialog = compose(
31
158
  wrapDialog({
32
159
  canEscapeKeyClose: false,
160
+ title: "Build CSV File",
33
161
  style: { width: "fit-content" }
34
162
  }),
35
- reduxForm({
36
- form: "UploadCsvWizardDialog"
37
- }),
38
- connect(
39
- (state, props) => {
40
- if (props.filesWIssues.length > 0) {
41
- const reduxFormEntitiesArray = [];
42
- const finishedFiles = props.filesWIssues.map((f, i) => {
43
- const { reduxFormEntities, reduxFormCellValidation } =
44
- formValueSelector(`editableCellTable-${i}`)(
45
- state,
46
- "reduxFormEntities",
47
- "reduxFormCellValidation"
48
- );
49
- reduxFormEntitiesArray.push(reduxFormEntities);
50
- const { entsToUse, validationToUse } = removeCleanRows(
51
- reduxFormEntities,
52
- reduxFormCellValidation
53
- );
54
- return (
55
- entsToUse &&
56
- entsToUse.length &&
57
- !some(validationToUse, v => v) &&
58
- entsToUse
59
- );
60
- });
61
- return {
62
- reduxFormEntitiesArray,
63
- finishedFiles
64
- };
65
- }
66
- },
67
- { changeForm: change, destroyForms: destroy }
68
- ),
69
- observer
70
- )(function UploadCsvWizardDialogOuter({
71
- validateAgainstSchema,
72
- reduxFormEntitiesArray,
73
- filesWIssues: _filesWIssues,
74
- finishedFiles,
75
- onUploadWizardFinish,
76
- doAllFilesHaveSameHeaders,
77
- destroyForms,
78
- csvValidationIssue,
79
- ignoredHeadersMsg,
80
- searchResults,
163
+ reduxForm({ form: "SimpleInsertDataDialog" })
164
+ )(({
165
+ dataTableForm = "simpleInsertEditableTable",
166
+ entities,
167
+ handleSubmit,
168
+ headerMessage,
169
+ isEditingExistingFile,
81
170
  matchedHeaders,
171
+ onSimpleInsertDialogFinish,
172
+ showDoesDataLookCorrectMsg,
173
+ submitting,
82
174
  userSchema,
83
- flippedMatchedHeaders,
84
- changeForm
85
- }) {
86
- // will unmount state hook
87
- React.useEffect(() => {
88
- return () => {
89
- destroyForms(
90
- "editableCellTable",
91
- ...times(_filesWIssues.length, i => `editableCellTable-${i}`)
92
- );
93
- };
94
- // eslint-disable-next-line react-hooks/exhaustive-deps
95
- }, []);
96
- const [hasSubmittedOuter, setSubmittedOuter] = useState();
97
- const [steps, setSteps] = useState(getInitialSteps(true));
98
-
99
- const [focusedTab, setFocusedTab] = useState(0);
100
- const [filesWIssues, setFilesWIssues] = useState(
101
- _filesWIssues.map(cloneDeep) //do this little trick to stop immer from preventing the file from being modified
175
+ validateAgainstSchema,
176
+ initialValues
177
+ }) => {
178
+ const dispatch = useDispatch();
179
+ const _reduxFormEntities = useSelector(
180
+ state => state.form?.[dataTableForm]?.values.reduxFormEntities
102
181
  );
103
- if (filesWIssues.length > 1) {
104
- const tabs = (
105
- <>
106
- <Callout style={{ marginBottom: 10, flexGrow: 0 }} intent="warning">
107
- <div>
108
- Please look over each of the following files and correct any issues.
109
- </div>
110
- </Callout>
111
- <Tabs
112
- // renderActiveTabPanelOnly
113
- selectedTabId={focusedTab}
114
- onChange={i => {
115
- setFocusedTab(i);
116
- }}
117
- vertical
118
- >
119
- {filesWIssues.map((f, i) => {
120
- const isGood = finishedFiles[i];
121
-
122
- const isThisTheLastBadFile = finishedFiles.every((ff, j) => {
123
- if (i === j) {
124
- return true;
125
- } else {
126
- return !!ff;
127
- }
128
- });
129
- return (
130
- <Tab
131
- key={i}
132
- id={i}
133
- title={
134
- <div>
135
- <Icon
136
- intent={isGood ? "success" : "warning"}
137
- icon={isGood ? "tick-circle" : "warning-sign"}
138
- ></Icon>{" "}
139
- {f.file.name}
140
- </div>
141
- }
142
- panel={
143
- <UploadCsvWizardDialogInner
144
- {...{
145
- isThisTheLastBadFile,
146
- onBackClick:
147
- doAllFilesHaveSameHeaders &&
148
- (() => {
149
- setSubmittedOuter(false);
150
- setSteps(getInitialSteps(true));
151
- }),
152
- onMultiFileUploadSubmit: async () => {
153
- let nextUnfinishedFile;
154
- //find the next unfinished file
155
- for (
156
- let j = (i + 1) % finishedFiles.length;
157
- j < finishedFiles.length;
158
- j++
159
- ) {
160
- if (j === i) {
161
- break;
162
- } else if (!finishedFiles[j]) {
163
- nextUnfinishedFile = j;
164
- break;
165
- } else if (j === finishedFiles.length - 1) {
166
- j = -1;
167
- }
168
- }
169
-
170
- if (nextUnfinishedFile !== undefined) {
171
- //do async validation here if needed
172
-
173
- const currentEnts =
174
- reduxFormEntitiesArray[focusedTab];
175
-
176
- if (
177
- await asyncValidateHelper(
178
- validateAgainstSchema,
179
- currentEnts,
180
- changeForm,
181
- `editableCellTable-${focusedTab}`
182
- )
183
- )
184
- return;
185
-
186
- setFocusedTab(nextUnfinishedFile);
187
- } else {
188
- //do async validation here if needed
182
+ const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
183
+ useEffect(() => {
184
+ return () => dispatch(destroy(dataTableForm));
185
+ }, [dataTableForm, dispatch]);
189
186
 
190
- for (const [i, ents] of finishedFiles.entries()) {
191
- if (
192
- await asyncValidateHelper(
193
- validateAgainstSchema,
194
- ents,
195
- changeForm,
196
- `editableCellTable-${i}`
197
- )
198
- )
199
- return;
200
- }
201
-
202
- //we are done
203
- onUploadWizardFinish({
204
- res: finishedFiles.map(ents => {
205
- return maybeStripIdFromEntities(
206
- ents,
207
- f.validateAgainstSchema
208
- );
209
- })
210
- });
211
- }
212
- },
213
- validateAgainstSchema,
214
- reduxFormEntitiesArray,
215
- filesWIssues,
216
- finishedFiles,
217
- onUploadWizardFinish,
218
- doAllFilesHaveSameHeaders,
219
- destroyForms,
220
- setFilesWIssues,
221
- csvValidationIssue,
222
- ignoredHeadersMsg,
223
- searchResults,
224
- matchedHeaders,
225
- userSchema,
226
- flippedMatchedHeaders,
227
- // reduxFormEntities,
228
- changeForm,
229
- fileIndex: i,
230
- form: `correctCSVHeadersForm-${i}`,
231
- datatableFormName: `editableCellTable-${i}`,
232
- ...f,
233
- ...(doAllFilesHaveSameHeaders && {
234
- csvValidationIssue: false
235
- })
236
- }}
237
- />
238
- }
239
- ></Tab>
240
- );
241
- })}
242
- </Tabs>
243
- </>
244
- );
245
- let comp = tabs;
246
-
247
- if (doAllFilesHaveSameHeaders) {
248
- comp = (
249
- <>
250
- {doAllFilesHaveSameHeaders && (
251
- <SimpleStepViz
252
- style={{ marginTop: 8 }}
253
- steps={steps}
254
- ></SimpleStepViz>
255
- )}
187
+ const _reduxFormCellValidation = useSelector(
188
+ state => state.form?.[dataTableForm]?.values.reduxFormCellValidation
189
+ );
190
+ const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
256
191
 
257
- {!hasSubmittedOuter && (
258
- <MatchHeaders
259
- {...{
260
- doAllFilesHaveSameHeaders,
261
- datatableFormNames: filesWIssues.map((f, i) => {
262
- return `editableCellTable-${i}`;
263
- }),
264
- reduxFormEntitiesArray,
265
- // onMultiFileUploadSubmit,
266
- csvValidationIssue,
267
- ignoredHeadersMsg,
268
- searchResults,
269
- matchedHeaders,
270
- userSchema,
271
- flippedMatchedHeaders,
272
- // reduxFormEntities,
273
- changeForm,
274
- setFilesWIssues,
275
- filesWIssues,
276
- fileIndex: 0,
277
- ...filesWIssues[0]
278
- }}
279
- />
280
- )}
281
- {hasSubmittedOuter && tabs}
282
- {!hasSubmittedOuter && (
283
- <DialogFooter
284
- style={{ marginTop: 20 }}
285
- onClick={() => {
286
- setSubmittedOuter(true);
287
- setSteps(getInitialSteps(false));
288
- }}
289
- text="Review and Edit Data"
290
- ></DialogFooter>
291
- )}
292
- </>
293
- );
294
- }
295
- return (
296
- <div
297
- style={{
298
- padding: 10
299
- }}
300
- >
301
- {comp}
192
+ const { entsToUse, validationToUse } = useMemo(
193
+ () => removeCleanRows(reduxFormEntities, reduxFormCellValidation),
194
+ [reduxFormEntities, reduxFormCellValidation]
195
+ );
196
+ return (
197
+ <>
198
+ <div className="bp3-dialog-body">
199
+ <InputField
200
+ isRequired
201
+ rightElement={
202
+ <div style={{ paddingTop: 6, paddingRight: 5 }}>.csv</div>
203
+ }
204
+ inlineLabel
205
+ label="File Name:"
206
+ defaultValue={initialValues?.fileName ?? "manual_data_entry"}
207
+ name="fileName"
208
+ />
209
+ <PreviewCsvData
210
+ datatableFormName={dataTableForm}
211
+ entities={entities}
212
+ headerMessage={headerMessage}
213
+ isEditingExistingFile={isEditingExistingFile}
214
+ matchedHeaders={matchedHeaders}
215
+ showDoesDataLookCorrectMsg={showDoesDataLookCorrectMsg}
216
+ userSchema={userSchema}
217
+ validateAgainstSchema={validateAgainstSchema}
218
+ />
302
219
  </div>
303
- );
304
- } else {
305
- return (
306
- <UploadCsvWizardDialogInner
307
- form="correctCSVHeadersForm"
308
- {...{
309
- validateAgainstSchema,
310
- userSchema,
311
- searchResults,
312
- onUploadWizardFinish,
313
- csvValidationIssue,
314
- ignoredHeadersMsg,
315
- matchedHeaders,
316
- //fromRedux:
317
- changeForm,
318
- setFilesWIssues,
319
- // doAllFilesHaveSameHeaders,
320
- filesWIssues,
321
- flippedMatchedHeaders,
322
- // reduxFormEntities,
323
- // datatableFormNames
324
- fileIndex: 0,
325
- ...filesWIssues[0]
326
- }}
220
+ <DialogFooter
221
+ submitting={submitting}
222
+ onClick={handleSubmit(async ({ fileName }) => {
223
+ if (some(validationToUse, e => e)) return;
224
+ //do async validation here if needed
225
+ if (
226
+ await asyncValidateHelper(
227
+ validateAgainstSchema,
228
+ entsToUse,
229
+ (...args) => dispatch(change(...args)),
230
+ "simpleInsertEditableTable"
231
+ )
232
+ )
233
+ return;
234
+ onSimpleInsertDialogFinish({
235
+ fileName: fileName + ".csv",
236
+ newEntities: maybeStripIdFromEntities(
237
+ entsToUse,
238
+ validateAgainstSchema
239
+ )
240
+ });
241
+ })}
242
+ disabled={!entsToUse?.length || some(validationToUse, e => e)}
243
+ text={isEditingExistingFile ? "Edit Data" : "Add File"}
327
244
  />
328
- );
329
- }
245
+ </>
246
+ );
330
247
  });
331
248
 
332
- const UploadCsvWizardDialogInner = compose(
333
- reduxForm(),
334
- connect((state, props) => {
335
- return formValueSelector(props.datatableFormName || "editableCellTable")(
336
- state,
337
- "reduxFormEntities",
338
- "reduxFormCellValidation"
339
- );
340
- })
341
- )(function UploadCsvWizardDialogInner({
249
+ const UploadCsvWizardDialogInner = reduxForm()(({
342
250
  validateAgainstSchema,
343
251
  userSchema,
344
252
  searchResults,
@@ -346,12 +254,9 @@ const UploadCsvWizardDialogInner = compose(
346
254
  csvValidationIssue,
347
255
  ignoredHeadersMsg,
348
256
  matchedHeaders,
349
- //fromRedux:
350
257
  handleSubmit,
351
258
  fileIndex,
352
- reduxFormEntities,
353
259
  onBackClick,
354
- reduxFormCellValidation,
355
260
  changeForm,
356
261
  setFilesWIssues,
357
262
  doAllFilesHaveSameHeaders,
@@ -360,42 +265,51 @@ const UploadCsvWizardDialogInner = compose(
360
265
  onMultiFileUploadSubmit,
361
266
  isThisTheLastBadFile,
362
267
  submitting
363
- }) {
268
+ }) => {
364
269
  const [hasSubmitted, setSubmitted] = useState(!csvValidationIssue);
365
270
  const [steps, setSteps] = useState(getInitialSteps(csvValidationIssue));
366
271
 
272
+ const {
273
+ reduxFormEntities: _reduxFormEntities,
274
+ reduxFormCellValidation: _reduxFormCellValidation
275
+ } = useSelector(state =>
276
+ formValueSelector(datatableFormName)(
277
+ state,
278
+ "reduxFormEntities",
279
+ "reduxFormCellValidation"
280
+ )
281
+ );
282
+
283
+ const reduxFormEntities = useDeepEqualMemo(_reduxFormEntities);
284
+ const reduxFormCellValidation = useDeepEqualMemo(_reduxFormCellValidation);
285
+
367
286
  let inner;
368
287
  if (hasSubmitted) {
369
288
  inner = (
370
289
  <PreviewCsvData
371
- {...{
372
- datatableFormName,
373
- showDoesDataLookCorrectMsg: true,
374
- initialEntities: reduxFormEntities || null,
375
- matchedHeaders,
376
- validateAgainstSchema,
377
- userSchema
378
- }}
379
- ></PreviewCsvData>
290
+ datatableFormName={datatableFormName}
291
+ showDoesDataLookCorrectMsg
292
+ entities={reduxFormEntities || null}
293
+ matchedHeaders={matchedHeaders}
294
+ validateAgainstSchema={validateAgainstSchema}
295
+ userSchema={userSchema}
296
+ />
380
297
  );
381
298
  } else {
382
299
  inner = (
383
300
  <MatchHeaders
384
- {...{
385
- onMultiFileUploadSubmit,
386
- csvValidationIssue,
387
- ignoredHeadersMsg,
388
- searchResults,
389
- matchedHeaders,
390
- userSchema,
391
- reduxFormEntitiesArray: [reduxFormEntities],
392
- changeForm,
393
- datatableFormName,
394
- setFilesWIssues,
395
- filesWIssues,
396
- fileIndex
397
- }}
398
- ></MatchHeaders>
301
+ onMultiFileUploadSubmit={onMultiFileUploadSubmit}
302
+ csvValidationIssue={csvValidationIssue}
303
+ ignoredHeadersMsg={ignoredHeadersMsg}
304
+ searchResults={searchResults}
305
+ matchedHeaders={matchedHeaders}
306
+ userSchema={userSchema}
307
+ reduxFormEntitiesArray={reduxFormEntities ? [reduxFormEntities] : []}
308
+ datatableFormName={datatableFormName}
309
+ setFilesWIssues={setFilesWIssues}
310
+ filesWIssues={filesWIssues}
311
+ fileIndex={fileIndex}
312
+ />
399
313
  );
400
314
  }
401
315
  const { entsToUse, validationToUse } = removeCleanRows(
@@ -406,7 +320,7 @@ const UploadCsvWizardDialogInner = compose(
406
320
  return (
407
321
  <div>
408
322
  {!doAllFilesHaveSameHeaders && (
409
- <SimpleStepViz style={{ marginTop: 8 }} steps={steps}></SimpleStepViz>
323
+ <SimpleStepViz style={{ marginTop: 8 }} steps={steps} />
410
324
  )}
411
325
  <div className="bp3-dialog-body">{inner}</div>
412
326
  <DialogFooter
@@ -443,7 +357,7 @@ const UploadCsvWizardDialogInner = compose(
443
357
  setSubmitted(false);
444
358
  })
445
359
  })}
446
- onClick={handleSubmit(async function () {
360
+ onClick={handleSubmit(async () => {
447
361
  if (!hasSubmitted) {
448
362
  //step 1 submit
449
363
  setSteps(
@@ -454,272 +368,352 @@ const UploadCsvWizardDialogInner = compose(
454
368
  })
455
369
  );
456
370
  setSubmitted(true);
457
- } else {
458
- if (!onMultiFileUploadSubmit) {
459
- //do async validation here if needed
460
- if (
461
- await asyncValidateHelper(
462
- validateAgainstSchema,
463
- entsToUse,
464
- changeForm,
465
- `editableCellTable`
466
- )
371
+ return;
372
+ }
373
+ if (!onMultiFileUploadSubmit) {
374
+ //do async validation here if needed
375
+ if (
376
+ await asyncValidateHelper(
377
+ validateAgainstSchema,
378
+ entsToUse,
379
+ changeForm,
380
+ datatableFormName
467
381
  )
468
- return;
469
- }
470
- //step 2 submit
471
- const payload = maybeStripIdFromEntities(
472
- entsToUse,
473
- validateAgainstSchema
474
- );
475
- return onMultiFileUploadSubmit
476
- ? await onMultiFileUploadSubmit()
477
- : onUploadWizardFinish({ res: [payload] });
382
+ )
383
+ return;
478
384
  }
385
+ //step 2 submit
386
+ const payload = maybeStripIdFromEntities(
387
+ entsToUse,
388
+ validateAgainstSchema
389
+ );
390
+ return onMultiFileUploadSubmit
391
+ ? await onMultiFileUploadSubmit()
392
+ : onUploadWizardFinish({ res: [payload] });
479
393
  })}
480
394
  style={{ alignSelf: "end" }}
481
- ></DialogFooter>
395
+ />
482
396
  </div>
483
397
  );
484
398
  });
485
399
 
486
- export default UploadCsvWizardDialog;
487
-
488
- const exampleData = { userData: times(5).map(() => ({ _isClean: true })) };
489
- export const PreviewCsvData = observer(function (props) {
490
- const {
491
- matchedHeaders,
492
- isEditingExistingFile,
493
- showDoesDataLookCorrectMsg,
494
- headerMessage,
495
- datatableFormName,
496
- // onlyShowRowsWErrors,
497
- validateAgainstSchema,
498
- userSchema = exampleData,
499
- initialEntities
500
- } = props;
501
- const rerenderKey = useRef(0);
502
- rerenderKey.current = rerenderKey.current + 1;
503
- // const useExampleData = userSchema === exampleData;
504
- // const [loading, setLoading] = useState(true);
505
- // useEffect(() => {
506
- // // simulate layout change outside of React lifecycle
507
- // setTimeout(() => {
508
- // setLoading(false);
509
- // }, 400);
510
- // }, []);
511
-
512
- // const [val, forceUpdate] = useForceUpdate();
513
-
514
- const data =
515
- userSchema.userData &&
516
- userSchema.userData.length &&
517
- userSchema.userData.map((row, i) => {
518
- const toRet = {
519
- _isClean: row._isClean
520
- };
521
- validateAgainstSchema.fields.forEach(({ path, defaultValue }) => {
522
- const matchingKey = matchedHeaders?.[path];
523
- if (!matchingKey) {
524
- toRet[path] = defaultValue === undefined ? defaultValue : "";
525
- } else {
526
- toRet[path] = row[matchingKey];
527
- }
528
- if (toRet[path] === undefined || toRet[path] === "") {
529
- if (defaultValue) {
530
- if (isFunction(defaultValue)) {
531
- toRet[path] = defaultValue(i, row);
532
- } else toRet[path] = defaultValue;
533
- } else {
534
- // const exampleToUse = isArray(example) //this means that the row was not added by a user
535
- // ? example[i1]
536
- // : i1 === 0 && example;
537
- toRet[path] = "";
538
- // if (useExampleData && exampleToUse) {
539
- // toRet[path] = exampleToUse;
540
- // delete toRet._isClean;
541
- // } else {
542
- // }
543
- }
544
- }
545
- });
546
-
547
- if (row.id === undefined) {
548
- toRet.id = nanoid();
549
- } else {
550
- toRet.id = row.id;
551
- }
552
- return toRet;
553
- });
554
- return (
555
- <div style={{ minWidth: 400 }}>
556
- <Callout style={{ marginBottom: 5 }} intent="primary">
557
- {headerMessage ||
558
- (showDoesDataLookCorrectMsg
559
- ? "Does this data look correct? Edit it as needed."
560
- : `${
561
- isEditingExistingFile ? "Edit" : "Input"
562
- } your data here. Hover table headers for additional instructions.`)}
400
+ const MultipleFileDialog = ({
401
+ focusedTab,
402
+ setFocusedTab,
403
+ filesWIssues,
404
+ finishedFiles,
405
+ doAllFilesHaveSameHeaders,
406
+ setSubmittedOuter,
407
+ setSteps,
408
+ reduxFormEntitiesArray,
409
+ validateAgainstSchema,
410
+ changeForm,
411
+ onUploadWizardFinish,
412
+ setFilesWIssues,
413
+ csvValidationIssue,
414
+ ignoredHeadersMsg,
415
+ searchResults,
416
+ matchedHeaders,
417
+ userSchema,
418
+ flippedMatchedHeaders,
419
+ steps,
420
+ hasSubmittedOuter
421
+ }) => {
422
+ const tabs = (
423
+ <>
424
+ <Callout style={{ marginBottom: 10, flexGrow: 0 }} intent="warning">
425
+ <div>
426
+ Please look over each of the following files and correct any issues.
427
+ </div>
563
428
  </Callout>
564
- {validateAgainstSchema.description && (
565
- <Callout>{validateAgainstSchema.description}</Callout>
566
- )}
567
- <div
568
- style={{
569
- display: "flex",
570
- justifyContent: "space-between",
571
- alignItems: "flex-start"
429
+ <Tabs
430
+ // renderActiveTabPanelOnly
431
+ selectedTabId={focusedTab}
432
+ onChange={i => {
433
+ setFocusedTab(i);
572
434
  }}
435
+ vertical
573
436
  >
574
- {validateAgainstSchema.HeaderComp && (
575
- <validateAgainstSchema.HeaderComp
576
- {...props}
577
- // {...{ forceUpdate }}
578
- ></validateAgainstSchema.HeaderComp>
579
- )}
580
- </div>
581
- <DataTable
582
- maxWidth={800}
583
- maxHeight={500}
584
- rerenderKey={rerenderKey.current} //pass this since to force rerenders since validateAgainstSchema changing doesn't always trigger a rerender
585
- destroyOnUnmount={false}
586
- doNotValidateUntouchedRows
587
- formName={datatableFormName || "editableCellTable"}
588
- isSimple
589
- keepDirtyOnReinitialize
590
- isCellEditable
591
- initialEntities={(initialEntities ? initialEntities : data) || []}
592
- entities={(initialEntities ? initialEntities : data) || []}
593
- schema={validateAgainstSchema}
594
- ></DataTable>
595
- </div>
437
+ {filesWIssues.map((f, i) => {
438
+ const isGood = finishedFiles[i];
439
+ const isThisTheLastBadFile = finishedFiles.every((ff, j) => {
440
+ if (i === j) {
441
+ return true;
442
+ } else {
443
+ return !!ff;
444
+ }
445
+ });
446
+ return (
447
+ <Tab
448
+ key={i}
449
+ id={i}
450
+ title={
451
+ <div>
452
+ <Icon
453
+ intent={isGood ? "success" : "warning"}
454
+ icon={isGood ? "tick-circle" : "warning-sign"}
455
+ />{" "}
456
+ {f.file.name}
457
+ </div>
458
+ }
459
+ panel={
460
+ <UploadCsvWizardDialogInner
461
+ isThisTheLastBadFile={isThisTheLastBadFile}
462
+ onBackClick={
463
+ doAllFilesHaveSameHeaders &&
464
+ (() => {
465
+ setSubmittedOuter(false);
466
+ setSteps(getInitialSteps(true));
467
+ })
468
+ }
469
+ onMultiFileUploadSubmit={async () => {
470
+ let nextUnfinishedFile;
471
+ //find the next unfinished file
472
+ for (
473
+ let j = (i + 1) % finishedFiles.length;
474
+ j < finishedFiles.length;
475
+ j++
476
+ ) {
477
+ if (j === i) {
478
+ break;
479
+ } else if (!finishedFiles[j]) {
480
+ nextUnfinishedFile = j;
481
+ break;
482
+ } else if (j === finishedFiles.length - 1) {
483
+ j = -1;
484
+ }
485
+ }
486
+ if (nextUnfinishedFile !== undefined) {
487
+ //do async validation here if needed
488
+ const currentEnts = reduxFormEntitiesArray[focusedTab];
489
+ if (
490
+ await asyncValidateHelper(
491
+ validateAgainstSchema,
492
+ currentEnts,
493
+ changeForm,
494
+ `editableCellTable-${focusedTab}`
495
+ )
496
+ )
497
+ return;
498
+ setFocusedTab(nextUnfinishedFile);
499
+ } else {
500
+ //do async validation here if needed
501
+ for (const [i, ents] of finishedFiles.entries()) {
502
+ if (
503
+ await asyncValidateHelper(
504
+ validateAgainstSchema,
505
+ ents,
506
+ changeForm,
507
+ `editableCellTable-${i}`
508
+ )
509
+ )
510
+ return;
511
+ }
512
+ //we are done
513
+ onUploadWizardFinish({
514
+ res: finishedFiles.map(ents => {
515
+ return maybeStripIdFromEntities(
516
+ ents,
517
+ f.validateAgainstSchema
518
+ );
519
+ })
520
+ });
521
+ }
522
+ }}
523
+ validateAgainstSchema={validateAgainstSchema}
524
+ reduxFormEntitiesArray={reduxFormEntitiesArray}
525
+ filesWIssues={filesWIssues}
526
+ finishedFiles={finishedFiles}
527
+ onUploadWizardFinish={onUploadWizardFinish}
528
+ doAllFilesHaveSameHeaders={doAllFilesHaveSameHeaders}
529
+ setFilesWIssues={setFilesWIssues}
530
+ csvValidationIssue={csvValidationIssue}
531
+ ignoredHeadersMsg={ignoredHeadersMsg}
532
+ searchResults={searchResults}
533
+ matchedHeader={matchedHeaders}
534
+ userSchema={userSchema}
535
+ flippedMatchedHeaders={flippedMatchedHeaders}
536
+ changeForm={changeForm}
537
+ fileIndex={i}
538
+ form={`correctCSVHeadersForm-${i}`}
539
+ datatableFormName={`editableCellTable-${i}`}
540
+ {...f}
541
+ {...(doAllFilesHaveSameHeaders && {
542
+ csvValidationIssue: false
543
+ })}
544
+ />
545
+ }
546
+ />
547
+ );
548
+ })}
549
+ </Tabs>
550
+ </>
596
551
  );
597
- });
552
+ const comp = doAllFilesHaveSameHeaders ? (
553
+ <>
554
+ {doAllFilesHaveSameHeaders && (
555
+ <SimpleStepViz style={{ marginTop: 8 }} steps={steps} />
556
+ )}
598
557
 
599
- export const SimpleInsertDataDialog = compose(
558
+ {!hasSubmittedOuter && (
559
+ <MatchHeaders
560
+ doAllFilesHaveSameHeaders={doAllFilesHaveSameHeaders}
561
+ datatableFormNames={filesWIssues.map((f, i) => {
562
+ return `editableCellTable-${i}`;
563
+ })}
564
+ reduxFormEntitiesArray={reduxFormEntitiesArray}
565
+ csvValidationIssue={csvValidationIssue}
566
+ ignoredHeadersMsg={ignoredHeadersMsg}
567
+ searchResults={searchResults}
568
+ matchedHeaders={matchedHeaders}
569
+ userSchema={userSchema}
570
+ flippedMatchedHeaders={flippedMatchedHeaders}
571
+ setFilesWIssues={setFilesWIssues}
572
+ filesWIssues={filesWIssues}
573
+ fileIndex={0}
574
+ {...filesWIssues[0]}
575
+ />
576
+ )}
577
+ {hasSubmittedOuter && tabs}
578
+ {!hasSubmittedOuter && (
579
+ <DialogFooter
580
+ style={{ marginTop: 20 }}
581
+ onClick={() => {
582
+ setSubmittedOuter(true);
583
+ setSteps(getInitialSteps(false));
584
+ }}
585
+ text="Review and Edit Data"
586
+ />
587
+ )}
588
+ </>
589
+ ) : (
590
+ tabs
591
+ );
592
+
593
+ return <div style={{ padding: 10 }}>{comp}</div>;
594
+ };
595
+
596
+ const UploadCsvWizardDialog = compose(
600
597
  wrapDialog({
601
598
  canEscapeKeyClose: false,
602
- title: "Build CSV File",
603
599
  style: { width: "fit-content" }
604
600
  }),
605
- reduxForm({ form: "SimpleInsertDataDialog" }),
606
- tgFormValueSelector(
607
- "simpleInsertEditableTable",
608
- "reduxFormEntities",
609
- "reduxFormCellValidation"
610
- ),
611
- connect(undefined, { changeForm: change }),
612
- observer
613
- )(function SimpleInsertDataDialog({
614
- onSimpleInsertDialogFinish,
615
- reduxFormEntities,
616
- reduxFormCellValidation,
617
- validateAgainstSchema,
618
- changeForm,
619
- submitting,
620
- isEditingExistingFile,
601
+ reduxForm({
602
+ form: "UploadCsvWizardDialog"
603
+ })
604
+ )(({
605
+ csvValidationIssue,
606
+ doAllFilesHaveSameHeaders,
607
+ filesWIssues: _filesWIssues,
608
+ flippedMatchedHeaders,
609
+ ignoredHeadersMsg,
621
610
  matchedHeaders,
622
- showDoesDataLookCorrectMsg,
623
- headerMessage,
624
- handleSubmit,
611
+ onUploadWizardFinish,
612
+ searchResults,
625
613
  userSchema,
626
- initialEntities
627
- }) {
628
- const { entsToUse, validationToUse } = removeCleanRows(
629
- reduxFormEntities,
630
- reduxFormCellValidation
631
- );
632
-
633
- return (
634
- <>
635
- <div className="bp3-dialog-body">
636
- <InputField
637
- isRequired
638
- rightElement={
639
- <div style={{ paddingTop: 6, paddingRight: 5 }}>.csv</div>
640
- }
641
- inlineLabel
642
- label="File Name:"
643
- defaultValue={"manual_data_entry"}
644
- name="fileName"
645
- ></InputField>
646
- <PreviewCsvData
647
- {...{
648
- matchedHeaders,
649
- isEditingExistingFile,
650
- showDoesDataLookCorrectMsg,
651
- headerMessage,
652
- // onlyShowRowsWErrors,
653
- validateAgainstSchema,
654
- userSchema,
655
- initialEntities,
656
- datatableFormName: "simpleInsertEditableTable"
657
- }}
658
- ></PreviewCsvData>
659
- </div>
660
- <DialogFooter
661
- submitting={submitting}
662
- onClick={handleSubmit(async ({ fileName }) => {
663
- if (some(validationToUse, e => e)) return;
664
- //do async validation here if needed
665
- if (
666
- await asyncValidateHelper(
667
- validateAgainstSchema,
668
- entsToUse,
669
- changeForm,
670
- "simpleInsertEditableTable"
671
- )
672
- )
673
- return;
674
- onSimpleInsertDialogFinish({
675
- fileName: fileName + ".csv",
676
- newEntities: maybeStripIdFromEntities(
677
- entsToUse,
678
- validateAgainstSchema
679
- )
680
- });
681
- })}
682
- disabled={!entsToUse?.length || some(validationToUse, e => e)}
683
- text={isEditingExistingFile ? "Edit Data" : "Add File"}
684
- ></DialogFooter>
685
- </>
614
+ validateAgainstSchema
615
+ }) => {
616
+ const dispatch = useDispatch();
617
+ const changeForm = useCallback(
618
+ (...args) => dispatch(change(...args)),
619
+ [dispatch]
686
620
  );
687
- });
621
+ // will unmount state hook
622
+ useEffect(() => {
623
+ return () => {
624
+ dispatch(
625
+ destroy(
626
+ "editableCellTable",
627
+ ...times(_filesWIssues.length, i => `editableCellTable-${i}`)
628
+ )
629
+ );
630
+ };
631
+ }, [_filesWIssues.length, dispatch]);
688
632
 
689
- async function asyncValidateHelper(
690
- validateAgainstSchema,
691
- currentEnts,
692
- changeForm,
693
- tableName
694
- ) {
695
- if (!validateAgainstSchema.tableWideAsyncValidation) return;
696
- const res = await validateAgainstSchema.tableWideAsyncValidation({
697
- entities: currentEnts
633
+ const { _reduxFormEntitiesArray, _finishedFiles } = useSelector(state => {
634
+ if (_filesWIssues.length > 0) {
635
+ const reduxFormEntitiesArray = [];
636
+ const finishedFiles = _filesWIssues.map((f, i) => {
637
+ const { reduxFormEntities, reduxFormCellValidation } =
638
+ formValueSelector(`editableCellTable-${i}`)(
639
+ state,
640
+ "reduxFormEntities",
641
+ "reduxFormCellValidation"
642
+ );
643
+ reduxFormEntitiesArray.push(reduxFormEntities);
644
+ const { entsToUse, validationToUse } = removeCleanRows(
645
+ reduxFormEntities,
646
+ reduxFormCellValidation
647
+ );
648
+ return (
649
+ entsToUse &&
650
+ entsToUse.length &&
651
+ !some(validationToUse, v => v) &&
652
+ entsToUse
653
+ );
654
+ });
655
+ return {
656
+ _reduxFormEntitiesArray: reduxFormEntitiesArray,
657
+ _finishedFiles: finishedFiles
658
+ };
659
+ }
698
660
  });
699
- if (!isEmpty(res)) {
700
- changeForm(tableName, "reduxFormCellValidation", {
701
- ...addSpecialPropToAsyncErrs(res)
702
- });
703
- return true;
704
- }
705
- }
661
+ const reduxFormEntitiesArray = useDeepEqualMemo(_reduxFormEntitiesArray);
662
+ const finishedFiles = useDeepEqualMemo(_finishedFiles);
706
663
 
707
- function maybeStripIdFromEntities(ents, validateAgainstSchema) {
708
- let toRet;
709
- if (validateAgainstSchema?.fields?.some(({ path }) => path === "id")) {
710
- toRet = ents;
664
+ const [hasSubmittedOuter, setSubmittedOuter] = useState();
665
+ const [steps, setSteps] = useState(getInitialSteps(true));
666
+
667
+ const [focusedTab, setFocusedTab] = useState(0);
668
+ const [filesWIssues, setFilesWIssues] = useState(
669
+ _filesWIssues.map(cloneDeep) //do this little trick to stop immer from preventing the file from being modified
670
+ );
671
+
672
+ if (filesWIssues.length > 1) {
673
+ return (
674
+ <MultipleFileDialog
675
+ focusedTab={focusedTab}
676
+ setFocusedTab={setFocusedTab}
677
+ filesWIssues={filesWIssues}
678
+ finishedFiles={finishedFiles}
679
+ doAllFilesHaveSameHeaders={doAllFilesHaveSameHeaders}
680
+ setSubmittedOuter={setSubmittedOuter}
681
+ setSteps={setSteps}
682
+ reduxFormEntitiesArray={reduxFormEntitiesArray}
683
+ validateAgainstSchema={validateAgainstSchema}
684
+ changeForm={changeForm}
685
+ onUploadWizardFinish={onUploadWizardFinish}
686
+ setFilesWIssues={setFilesWIssues}
687
+ csvValidationIssue={csvValidationIssue}
688
+ ignoredHeadersMsg={ignoredHeadersMsg}
689
+ searchResults={searchResults}
690
+ matchedHeaders={matchedHeaders}
691
+ userSchema={userSchema}
692
+ flippedMatchedHeaders={flippedMatchedHeaders}
693
+ steps={steps}
694
+ hasSubmittedOuter={hasSubmittedOuter}
695
+ />
696
+ );
711
697
  } else {
712
- // if the schema we're validating against itself didn't have an id field,
713
- // we don't want to include it in the returned entities
714
- toRet = ents?.map(e => omit(e, ["id"]));
698
+ return (
699
+ <UploadCsvWizardDialogInner
700
+ form="correctCSVHeadersForm"
701
+ validateAgainstSchema={validateAgainstSchema}
702
+ userSchema={userSchema}
703
+ searchResults={searchResults}
704
+ onUploadWizardFinish={onUploadWizardFinish}
705
+ csvValidationIssue={csvValidationIssue}
706
+ ignoredHeadersMsg={ignoredHeadersMsg}
707
+ matchedHeaders={matchedHeaders}
708
+ changeForm={changeForm}
709
+ setFilesWIssues={setFilesWIssues}
710
+ filesWIssues={filesWIssues}
711
+ flippedMatchedHeaders={flippedMatchedHeaders}
712
+ fileIndex={0}
713
+ {...filesWIssues[0]}
714
+ />
715
+ );
715
716
  }
716
- return toRet?.map(e => omit(e, ["_isClean"]));
717
- }
717
+ });
718
718
 
719
- //create your forceUpdate hook
720
- // function useForceUpdate() {
721
- // const [val, setValue] = useState(0); // integer state
722
- // return [val, () => setValue(value => value + 1)]; // update state to force render
723
- // // A function that increment 👆🏻 the previous state like here
724
- // // is better than directly setting `setValue(value + 1)`
725
- // }
719
+ export default UploadCsvWizardDialog;