@vuu-ui/vuu-table-extras 4.0.0-alpha.9 → 4.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -18
- package/src/cell-renderers/pct-progress-cell/PctProgressCell.css.js +2 -2
- package/src/cell-renderers/progress-cell/ProgressCell.css.js +2 -2
- package/src/column-expression-input/ColumnExpressionInput.css.js +5 -5
- package/src/column-expression-input/column-language-parser/column-expression-parse-utils.js +1 -1
- package/src/column-expression-input/theme.js +1 -1
- package/src/column-expression-input/useColumnExpressionEditor.js +1 -0
- package/src/column-expression-input/useColumnExpressionSuggestionProvider.js +2 -2
- package/src/column-formatting-settings/ColumnFormattingPanel.js +12 -9
- package/src/column-formatting-settings/DateTimeFormattingSettings.js +107 -3
- package/src/column-formatting-settings/LongTypeFormattingSettings.js +8 -3
- package/src/column-picker/ColumnModel.js +3 -2
- package/src/column-picker/ColumnPicker.js +1 -3
- package/src/column-settings-panel/ColumnNameLabel.css.js +1 -1
- package/src/csv-export/export-utils.js +76 -20
- package/src/csv-export/index.js +1 -1
- package/src/csv-export/useCsvExport.js +9 -1
- package/src/csv-upload/CsvUpload.css.js +36 -2
- package/src/csv-upload/CsvUpload.js +107 -63
- package/src/csv-upload/data-upload-preview/DataUploadPreview.js +1 -1
- package/src/csv-upload/parse/csv-errors.js +1 -1
- package/src/csv-upload/parse/csv-parse.js +1 -1
- package/src/csv-upload/parse/csv-schema-validation.js +46 -7
- package/src/csv-upload/parse/csv-upload-utils.js +47 -2
- package/src/csv-upload/session/index.js +2 -0
- package/src/csv-upload/session/useImportSchema.js +44 -0
- package/src/csv-upload/session/useImportSession.js +204 -0
- package/src/csv-upload/useCsvUpload.js +54 -199
- package/src/freeze-control/FreezeControl.css.js +4 -4
- package/src/freeze-control/FrozenBanner.css.js +2 -2
- package/src/table-footer/TableFooter.js +3 -3
- package/types/calculated-column/CalculatedColumnPanel.d.ts +1 -1
- package/types/cell-renderers/background-cell/BackgroundCell.d.ts +1 -1
- package/types/cell-renderers/background-cell/BackgroundCellConfigurationEditor.d.ts +1 -1
- package/types/cell-renderers/button-cell/IconButtonCell.d.ts +1 -1
- package/types/cell-renderers/dropdown-cell/DropdownCell.d.ts +1 -1
- package/types/cell-renderers/lookup-cell/LookupCell.d.ts +1 -1
- package/types/cell-renderers/pct-progress-cell/PctProgressCell.d.ts +1 -1
- package/types/column-expression-input/ColumnExpressionInput.d.ts +1 -1
- package/types/column-expression-input/column-language-parser/column-expression-parse-utils.d.ts +1 -1
- package/types/column-expression-input/column-language-parser/test.d.mts +1 -1
- package/types/column-expression-input/useColumnExpressionEditor.d.ts +4 -4
- package/types/column-formatting-settings/BaseNumericFormattingSettings.d.ts +1 -1
- package/types/column-formatting-settings/ColumnFormattingPanel.d.ts +1 -1
- package/types/column-formatting-settings/DateTimeFormattingSettings.d.ts +1 -1
- package/types/column-formatting-settings/LongTypeFormattingSettings.d.ts +1 -2
- package/types/column-menu/ColumnMenu.d.ts +1 -1
- package/types/column-picker/ColumnPicker.d.ts +4 -4
- package/types/column-picker/ColumnPickerAction.d.ts +1 -1
- package/types/column-settings-panel/ColumnNameLabel.d.ts +1 -1
- package/types/column-settings-panel/ColumnSettingsPanel.d.ts +1 -1
- package/types/csv-export/export-utils.d.ts +22 -9
- package/types/csv-export/index.d.ts +1 -1
- package/types/csv-export/useCsvExport.d.ts +5 -2
- package/types/csv-upload/CsvUpload.d.ts +2 -6
- package/types/csv-upload/data-upload-preview/DataUploadPreview.d.ts +1 -1
- package/types/csv-upload/parse/csv-errors.d.ts +1 -1
- package/types/csv-upload/parse/csv-schema-validation.d.ts +8 -2
- package/types/csv-upload/parse/csv-upload-utils.d.ts +9 -0
- package/types/csv-upload/session/index.d.ts +2 -0
- package/types/csv-upload/session/useImportSchema.d.ts +13 -0
- package/types/csv-upload/session/useImportSession.d.ts +26 -0
- package/types/csv-upload/useCsvUpload.d.ts +3 -3
- package/types/datasource-stats/DatasourceStats.d.ts +1 -1
- package/types/freeze-control/FreezeControl.d.ts +1 -1
- package/types/freeze-control/FrozenBanner.d.ts +1 -1
- package/types/inline-add-row/InlineAddRow.d.ts +1 -1
- package/types/inline-add-row/useInlineAddRow.d.ts +2 -1
- package/types/tabbed-table-config-panel/TabbedTableConfigPanel.d.ts +5 -5
- package/types/tabbed-table-config-panel/TabbedTableSettingsAction.d.ts +1 -1
- package/types/table-footer/TableFooter.d.ts +4 -4
- package/types/table-provider/TableProvider.d.ts +1 -1
- package/types/table-settings-panel/TableSettingsPanel.d.ts +1 -1
- package/types/src/calculated-column/CalculatedColumnPanel.d.ts +0 -12
- package/types/src/calculated-column/useCalculatedColumnPanel.d.ts +0 -10
- package/types/src/calculated-column/useEditCalculatedColumn.d.ts +0 -13
- package/types/src/cell-edit-validators/CaseValidator.d.ts +0 -2
- package/types/src/cell-edit-validators/PatternValidator.d.ts +0 -2
- package/types/src/cell-edit-validators/index.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/BackgroundCell.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/BackgroundCellConfigurationEditor.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/index.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/useDirection.d.ts +0 -3
- package/types/src/cell-renderers/button-cell/IconButtonCell.d.ts +0 -6
- package/types/src/cell-renderers/dropdown-cell/DropdownCell.d.ts +0 -2
- package/types/src/cell-renderers/dropdown-cell/index.d.ts +0 -1
- package/types/src/cell-renderers/index.d.ts +0 -6
- package/types/src/cell-renderers/lookup-cell/LookupCell.d.ts +0 -2
- package/types/src/cell-renderers/lookup-cell/index.d.ts +0 -1
- package/types/src/cell-renderers/pct-progress-cell/PctProgressCell.d.ts +0 -2
- package/types/src/cell-renderers/pct-progress-cell/index.d.ts +0 -1
- package/types/src/cell-renderers/progress-cell/ProgressCell.d.ts +0 -1
- package/types/src/cell-renderers/progress-cell/index.d.ts +0 -1
- package/types/src/column-expression-input/ColumnExpressionInput.d.ts +0 -10
- package/types/src/column-expression-input/column-function-descriptors.d.ts +0 -15
- package/types/src/column-expression-input/column-language-parser/ColumnExpressionLanguage.d.ts +0 -2
- package/types/src/column-expression-input/column-language-parser/ColumnExpressionTreeWalker.d.ts +0 -57
- package/types/src/column-expression-input/column-language-parser/column-expression-parse-utils.d.ts +0 -5
- package/types/src/column-expression-input/column-language-parser/generated/column-parser.d.ts +0 -2
- package/types/src/column-expression-input/column-language-parser/generated/column-parser.terms.d.ts +0 -1
- package/types/src/column-expression-input/column-language-parser/index.d.ts +0 -3
- package/types/src/column-expression-input/column-language-parser/test.d.mts +0 -1
- package/types/src/column-expression-input/functionDocInfo.d.ts +0 -2
- package/types/src/column-expression-input/highlighting.d.ts +0 -1
- package/types/src/column-expression-input/index.d.ts +0 -4
- package/types/src/column-expression-input/theme.d.ts +0 -1
- package/types/src/column-expression-input/useColumnAutoComplete.d.ts +0 -6
- package/types/src/column-expression-input/useColumnExpressionEditor.d.ts +0 -25
- package/types/src/column-expression-input/useColumnExpressionSuggestionProvider.d.ts +0 -8
- package/types/src/column-formatting-settings/BaseNumericFormattingSettings.d.ts +0 -2
- package/types/src/column-formatting-settings/ColumnFormattingPanel.d.ts +0 -9
- package/types/src/column-formatting-settings/DateTimeFormattingSettings.d.ts +0 -3
- package/types/src/column-formatting-settings/LongTypeFormattingSettings.d.ts +0 -3
- package/types/src/column-formatting-settings/index.d.ts +0 -3
- package/types/src/column-menu/ColumnMenu.d.ts +0 -10
- package/types/src/column-menu/column-action-types.d.ts +0 -11
- package/types/src/column-menu/column-menu-utils.d.ts +0 -19
- package/types/src/column-menu/useColumnActions.d.ts +0 -16
- package/types/src/column-picker/ColumnModel.d.ts +0 -81
- package/types/src/column-picker/ColumnPicker.d.ts +0 -10
- package/types/src/column-picker/ColumnPickerAction.d.ts +0 -5
- package/types/src/column-picker/useColumnPicker.d.ts +0 -16
- package/types/src/column-picker/useTableColumnPicker.d.ts +0 -7
- package/types/src/column-picker/useTableColumnPickerDeprecated.d.ts +0 -14
- package/types/src/column-settings-panel/ColumnNameLabel.d.ts +0 -7
- package/types/src/column-settings-panel/ColumnSettingsPanel.d.ts +0 -5
- package/types/src/column-settings-panel/useColumnSettings.d.ts +0 -30
- package/types/src/csv-export/export-utils.d.ts +0 -42
- package/types/src/csv-export/index.d.ts +0 -2
- package/types/src/csv-export/useCsvExport.d.ts +0 -14
- package/types/src/csv-upload/CsvUpload.d.ts +0 -69
- package/types/src/csv-upload/data-upload-preview/DataUploadPreview.d.ts +0 -10
- package/types/src/csv-upload/data-upload-preview/useDataUploadPreview.d.ts +0 -17
- package/types/src/csv-upload/index.d.ts +0 -7
- package/types/src/csv-upload/parse/csv-constants.d.ts +0 -3
- package/types/src/csv-upload/parse/csv-errors.d.ts +0 -47
- package/types/src/csv-upload/parse/csv-parse.d.ts +0 -11
- package/types/src/csv-upload/parse/csv-schema-validation.d.ts +0 -18
- package/types/src/csv-upload/parse/csv-upload-utils.d.ts +0 -21
- package/types/src/csv-upload/parse/index.d.ts +0 -5
- package/types/src/csv-upload/useCsvUpload.d.ts +0 -43
- package/types/src/datasource-stats/DatasourceStats.d.ts +0 -17
- package/types/src/datasource-stats/useDatasourceStats.d.ts +0 -15
- package/types/src/freeze-control/FreezeControl.d.ts +0 -15
- package/types/src/freeze-control/FrozenBanner.d.ts +0 -6
- package/types/src/freeze-control/freezeControlBadge.d.ts +0 -11
- package/types/src/freeze-control/useFreezeControl.d.ts +0 -10
- package/types/src/index.d.ts +0 -26
- package/types/src/inline-add-row/InlineAddRow.d.ts +0 -3
- package/types/src/inline-add-row/index.d.ts +0 -2
- package/types/src/inline-add-row/useInlineAddRow.d.ts +0 -59
- package/types/src/tabbed-table-config-panel/TabbedTableConfigPanel.d.ts +0 -17
- package/types/src/tabbed-table-config-panel/TabbedTableSettingsAction.d.ts +0 -7
- package/types/src/tabbed-table-config-panel/useTabbedTableConfigPanel.d.ts +0 -13
- package/types/src/table-footer/TableFooter.d.ts +0 -10
- package/types/src/table-provider/TableProvider.d.ts +0 -18
- package/types/src/table-settings-panel/TableSettingsPanel.d.ts +0 -22
- package/types/src/table-settings-panel/useTableSettings.d.ts +0 -14
|
@@ -14,10 +14,44 @@ const css = `
|
|
|
14
14
|
display: grid;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
.vuuCsvUpload-errorContainer {
|
|
18
|
+
margin-top: var(--salt-spacing-100);
|
|
19
|
+
width: 100%;
|
|
20
|
+
max-width: 480px;
|
|
21
|
+
max-height: 120px;
|
|
22
|
+
padding: var(--salt-spacing-100) var(--salt-spacing-200);
|
|
23
|
+
background-color: var(--salt-status-error-background);
|
|
24
|
+
border-radius: var(--salt-palette-corner);
|
|
25
|
+
text-align: left;
|
|
26
|
+
overflow-y: auto;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.vuuCsvUpload-errorList {
|
|
30
|
+
padding-left: var(--salt-spacing-200);
|
|
31
|
+
margin: 0;
|
|
32
|
+
list-style-type: disc;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.vuuCsvUpload-errorItem {
|
|
36
|
+
font-size: var(--salt-text-label-fontSize);
|
|
37
|
+
color: var(--salt-status-error-foreground-informative);
|
|
38
|
+
line-height: var(--salt-text-label-lineHeight);
|
|
39
|
+
word-break: break-word;
|
|
40
|
+
margin-bottom: var(--salt-spacing-50);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.vuuCsvUpload-importErrorItem {
|
|
44
|
+
text-align: center;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.vuuCsvUpload-rectifyPrompt {
|
|
48
|
+
margin-top: var(--salt-spacing-100);
|
|
49
|
+
}
|
|
50
|
+
|
|
17
51
|
.vuuCsvUpload-errors {
|
|
18
52
|
border: solid 1px var(--salt-status-error-borderColor);
|
|
19
|
-
border-radius: var(--salt-
|
|
20
|
-
color: var(--salt-status-error-foreground);
|
|
53
|
+
border-radius: var(--salt-palette-corner);
|
|
54
|
+
color: var(--salt-status-error-foreground-informative);
|
|
21
55
|
gap: var(--salt-spacing-100);
|
|
22
56
|
padding: var(--salt-spacing-200);
|
|
23
57
|
display: grid;
|
|
@@ -2,12 +2,12 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button, Dialog, DialogActions, DialogContent, DialogHeader, FileDropZone, FileDropZoneIcon, FileDropZoneTrigger, Text } from "@salt-ds/core";
|
|
3
3
|
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
4
4
|
import { useWindow } from "@salt-ds/window";
|
|
5
|
-
import { useCallback, useState } from "react";
|
|
5
|
+
import { useCallback, useMemo, useState } from "react";
|
|
6
6
|
import { useCsvUpload } from "./useCsvUpload.js";
|
|
7
7
|
import CsvUpload from "./CsvUpload.css";
|
|
8
8
|
const classBase = "vuuCsvUpload";
|
|
9
9
|
const CsvUpload_CsvUpload = (props)=>{
|
|
10
|
-
const { children, dialogTitle = "Import CSV", embedded = false, onCancel, onClose, open
|
|
10
|
+
const { children, dialogTitle = "Import CSV", embedded = false, onCancel, onClose, open } = props;
|
|
11
11
|
const isControlledOpen = void 0 !== open;
|
|
12
12
|
const [internalOpen, setInternalOpen] = useState(open ?? true);
|
|
13
13
|
const dialogOpen = isControlledOpen ? open : internalOpen;
|
|
@@ -18,7 +18,23 @@ const CsvUpload_CsvUpload = (props)=>{
|
|
|
18
18
|
window: targetWindow
|
|
19
19
|
});
|
|
20
20
|
const { canImport, cancelImport, isProcessingFile, isImporting, importData, onDrop, onTriggerChange, schema, validation, error } = useCsvUpload(props);
|
|
21
|
-
const
|
|
21
|
+
const errorsToRender = useMemo(()=>{
|
|
22
|
+
if (validation?.errors && validation.errors.length > 0) return validation.errors;
|
|
23
|
+
const parseErrors = error?.errors.schemaError?.parseError?.errors || error?.errors.validationError?.parseError?.errors;
|
|
24
|
+
if (parseErrors && parseErrors.length > 0) return parseErrors;
|
|
25
|
+
return [];
|
|
26
|
+
}, [
|
|
27
|
+
validation,
|
|
28
|
+
error
|
|
29
|
+
]);
|
|
30
|
+
const parsedSchemaErrors = useMemo(()=>{
|
|
31
|
+
const schemaErrorMsg = error?.errors.schemaError?.message || error?.errors.validationError?.message;
|
|
32
|
+
if (!schemaErrorMsg) return [];
|
|
33
|
+
const cleanedMsg = schemaErrorMsg.replace(/^CSV validation failed:\s*/, "");
|
|
34
|
+
return cleanedMsg.split(";").map((item)=>item.trim()).filter((item)=>item.length > 0);
|
|
35
|
+
}, [
|
|
36
|
+
error
|
|
37
|
+
]);
|
|
22
38
|
const hasErrors = !!error || validation && validation.errors.length > 0;
|
|
23
39
|
const isUploaded = canImport || isImporting;
|
|
24
40
|
const status = hasErrors ? "error" : isUploaded ? "success" : void 0;
|
|
@@ -37,73 +53,101 @@ const CsvUpload_CsvUpload = (props)=>{
|
|
|
37
53
|
importData,
|
|
38
54
|
onClose
|
|
39
55
|
]);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}),
|
|
53
|
-
/*#__PURE__*/ jsx("strong", {
|
|
54
|
-
children: "Upload completed"
|
|
55
|
-
})
|
|
56
|
-
]
|
|
57
|
-
}) : /*#__PURE__*/ jsxs(Fragment, {
|
|
58
|
-
children: [
|
|
59
|
-
/*#__PURE__*/ jsx(FileDropZoneIcon, {
|
|
60
|
-
status: hasErrors ? "error" : void 0
|
|
61
|
-
}),
|
|
62
|
-
validation && validation.errors.length > 0 ? /*#__PURE__*/ jsxs(Fragment, {
|
|
63
|
-
children: [
|
|
64
|
-
/*#__PURE__*/ jsx("div", {
|
|
65
|
-
children: "Your file contains errors"
|
|
66
|
-
}),
|
|
67
|
-
fileErrors.length > 0 && /*#__PURE__*/ jsx("ul", {
|
|
68
|
-
className: `${classBase}-errorList`,
|
|
69
|
-
children: fileErrors.map((error, i)=>/*#__PURE__*/ jsx("li", {
|
|
70
|
-
className: `${classBase}-errorItem`,
|
|
71
|
-
children: error.message
|
|
72
|
-
}, `${error.column}-${error.message}-${i}`))
|
|
73
|
-
}),
|
|
74
|
-
/*#__PURE__*/ jsx("div", {
|
|
75
|
-
children: "Please rectify and reupload"
|
|
76
|
-
})
|
|
77
|
-
]
|
|
78
|
-
}) : /*#__PURE__*/ jsx("div", {
|
|
79
|
-
children: "Drop a file here or"
|
|
80
|
-
}),
|
|
81
|
-
/*#__PURE__*/ jsx(FileDropZoneTrigger, {
|
|
82
|
-
accept: ".csv,text/csv",
|
|
83
|
-
onChange: onTriggerChange,
|
|
84
|
-
children: "BROWSE FILES"
|
|
85
|
-
}),
|
|
86
|
-
/*#__PURE__*/ jsx(Text, {
|
|
87
|
-
children: "Only .csv files"
|
|
88
|
-
}),
|
|
89
|
-
children
|
|
90
|
-
]
|
|
56
|
+
const renderErrorsContent = useMemo(()=>{
|
|
57
|
+
if (error?.errors.importError) return /*#__PURE__*/ jsxs(Fragment, {
|
|
58
|
+
children: [
|
|
59
|
+
/*#__PURE__*/ jsx("strong", {
|
|
60
|
+
children: "Import failed"
|
|
61
|
+
}),
|
|
62
|
+
/*#__PURE__*/ jsx("div", {
|
|
63
|
+
className: `${classBase}-errorContainer`,
|
|
64
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
65
|
+
className: `${classBase}-errorItem ${classBase}-importErrorItem`,
|
|
66
|
+
children: error.errors.importError.message
|
|
67
|
+
})
|
|
91
68
|
})
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
if (hasErrors) {
|
|
72
|
+
const headingText = error ? "Validation failed" : "Your file contains errors";
|
|
73
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
95
74
|
children: [
|
|
75
|
+
/*#__PURE__*/ jsx("strong", {
|
|
76
|
+
children: headingText
|
|
77
|
+
}),
|
|
96
78
|
/*#__PURE__*/ jsx("div", {
|
|
97
|
-
|
|
98
|
-
|
|
79
|
+
className: `${classBase}-errorContainer`,
|
|
80
|
+
children: errorsToRender.length > 0 ? /*#__PURE__*/ jsx("ul", {
|
|
81
|
+
className: `${classBase}-errorList`,
|
|
82
|
+
children: errorsToRender.map((error, i)=>/*#__PURE__*/ jsxs("li", {
|
|
83
|
+
className: `${classBase}-errorItem`,
|
|
84
|
+
children: [
|
|
85
|
+
error.rowNum > 0 ? `Row ${error.rowNum}: ` : "",
|
|
86
|
+
"*" === error.column ? "" : `${error.column}: `,
|
|
87
|
+
error.message
|
|
88
|
+
]
|
|
89
|
+
}, `${error.column}-${error.message}-${i}`))
|
|
90
|
+
}) : parsedSchemaErrors.length > 0 ? /*#__PURE__*/ jsx("ul", {
|
|
91
|
+
className: `${classBase}-errorList`,
|
|
92
|
+
children: parsedSchemaErrors.map((errMsg)=>/*#__PURE__*/ jsx("li", {
|
|
93
|
+
className: `${classBase}-errorItem`,
|
|
94
|
+
children: errMsg
|
|
95
|
+
}, errMsg))
|
|
96
|
+
}) : /*#__PURE__*/ jsx("div", {
|
|
97
|
+
className: `${classBase}-errorItem ${classBase}-importErrorItem`,
|
|
98
|
+
children: error?.errors.schemaError?.message || error?.errors.validationError?.message || "An unexpected error occurred during import"
|
|
99
99
|
})
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return /*#__PURE__*/ jsx("div", {
|
|
105
|
+
children: "Drop a file here or"
|
|
106
|
+
});
|
|
107
|
+
}, [
|
|
108
|
+
error,
|
|
109
|
+
hasErrors,
|
|
110
|
+
errorsToRender,
|
|
111
|
+
parsedSchemaErrors
|
|
112
|
+
]);
|
|
113
|
+
const content = /*#__PURE__*/ jsx("div", {
|
|
114
|
+
className: classBase,
|
|
115
|
+
children: /*#__PURE__*/ jsx(FileDropZone, {
|
|
116
|
+
className: `${classBase}-dropZone`,
|
|
117
|
+
disabled: void 0 === schema || isProcessingFile || isImporting,
|
|
118
|
+
onDrop: onDrop,
|
|
119
|
+
status: status,
|
|
120
|
+
children: isUploaded ? /*#__PURE__*/ jsxs(Fragment, {
|
|
121
|
+
children: [
|
|
122
|
+
/*#__PURE__*/ jsx(FileDropZoneIcon, {
|
|
123
|
+
status: "success"
|
|
100
124
|
}),
|
|
101
|
-
/*#__PURE__*/ jsx("
|
|
102
|
-
children:
|
|
125
|
+
/*#__PURE__*/ jsx("strong", {
|
|
126
|
+
children: "Upload completed"
|
|
103
127
|
})
|
|
104
128
|
]
|
|
105
|
-
})
|
|
106
|
-
|
|
129
|
+
}) : /*#__PURE__*/ jsxs(Fragment, {
|
|
130
|
+
children: [
|
|
131
|
+
/*#__PURE__*/ jsx(FileDropZoneIcon, {
|
|
132
|
+
status: hasErrors ? "error" : void 0
|
|
133
|
+
}),
|
|
134
|
+
renderErrorsContent,
|
|
135
|
+
hasErrors && /*#__PURE__*/ jsx(Text, {
|
|
136
|
+
className: `${classBase}-rectifyPrompt`,
|
|
137
|
+
children: "Please rectify and reupload"
|
|
138
|
+
}),
|
|
139
|
+
/*#__PURE__*/ jsx(FileDropZoneTrigger, {
|
|
140
|
+
accept: ".csv,text/csv",
|
|
141
|
+
onChange: onTriggerChange,
|
|
142
|
+
children: "BROWSE FILES"
|
|
143
|
+
}),
|
|
144
|
+
/*#__PURE__*/ jsx(Text, {
|
|
145
|
+
children: "Only .csv files"
|
|
146
|
+
}),
|
|
147
|
+
children
|
|
148
|
+
]
|
|
149
|
+
})
|
|
150
|
+
})
|
|
107
151
|
});
|
|
108
152
|
const actions = /*#__PURE__*/ jsxs(DialogActions, {
|
|
109
153
|
children: [
|
|
@@ -13,7 +13,7 @@ var csv_errors_CsvValidationErrorEnum = /*#__PURE__*/ function(CsvValidationErro
|
|
|
13
13
|
CsvValidationErrorEnum["MISSING_KEY_COLUMN"] = "MISSING_KEY_COLUMN";
|
|
14
14
|
CsvValidationErrorEnum["UNKNOWN_COLUMN"] = "UNKNOWN_COLUMN";
|
|
15
15
|
CsvValidationErrorEnum["MAX_ROWS_EXCEEDED"] = "MAX_ROWS_EXCEEDED";
|
|
16
|
-
CsvValidationErrorEnum["
|
|
16
|
+
CsvValidationErrorEnum["REQUIRED_FIELD_MISSING"] = "REQUIRED_FIELD_MISSING";
|
|
17
17
|
CsvValidationErrorEnum["TYPE_MISMATCH"] = "TYPE_MISMATCH";
|
|
18
18
|
CsvValidationErrorEnum["CUSTOM_VALIDATION"] = "CUSTOM_VALIDATION";
|
|
19
19
|
return CsvValidationErrorEnum;
|
|
@@ -68,7 +68,7 @@ const parseCsv = (csvText, options)=>{
|
|
|
68
68
|
}
|
|
69
69
|
const rows = records.slice(1);
|
|
70
70
|
rows.forEach((row, rowIndex)=>{
|
|
71
|
-
if (row.length !== header.length) addCsvRowError(errorState, rowIndex + CSV_FIRST_DATA_ROW_NUMBER, "*", CsvParseErrorEnum.ROW_COLUMN_COUNT_MISMATCH, "
|
|
71
|
+
if (row.length !== header.length) addCsvRowError(errorState, rowIndex + CSV_FIRST_DATA_ROW_NUMBER, "*", CsvParseErrorEnum.ROW_COLUMN_COUNT_MISMATCH, "This row has too many or too few columns compared to the header line. Check for extra or missing commas.", row.join(","));
|
|
72
72
|
});
|
|
73
73
|
if (hasCsvErrors(errorState.errorMap)) return withError("CSV rows must have the same number of columns as the header.", errorState.errorMap, errorState.errors, header, rows);
|
|
74
74
|
return {
|
|
@@ -4,10 +4,44 @@ import { CSV_FIRST_DATA_ROW_NUMBER, MAX_ROWS_IN_CSV } from "./csv-constants.js";
|
|
|
4
4
|
const INTERNAL_KEY_COLUMNS = new Set([
|
|
5
5
|
"vuuRowNum"
|
|
6
6
|
]);
|
|
7
|
+
const getFriendlyTypeName = (type)=>{
|
|
8
|
+
switch(type){
|
|
9
|
+
case "int":
|
|
10
|
+
case "long":
|
|
11
|
+
return "whole number";
|
|
12
|
+
case "double":
|
|
13
|
+
case "number":
|
|
14
|
+
case "decimal":
|
|
15
|
+
case "scaleddecimal":
|
|
16
|
+
case "scaleddecimal2":
|
|
17
|
+
case "scaleddecimal4":
|
|
18
|
+
case "scaleddecimal6":
|
|
19
|
+
case "scaleddecimal8":
|
|
20
|
+
return "decimal or number";
|
|
21
|
+
case "time":
|
|
22
|
+
return "time in 'HH:MM:SS' format";
|
|
23
|
+
case "epochtimestamp":
|
|
24
|
+
case "epochtimestampnano":
|
|
25
|
+
case "date/time":
|
|
26
|
+
return "date/time";
|
|
27
|
+
case "char":
|
|
28
|
+
return "single character";
|
|
29
|
+
case "boolean":
|
|
30
|
+
return "boolean (true/false)";
|
|
31
|
+
case "json":
|
|
32
|
+
return "JSON value";
|
|
33
|
+
default:
|
|
34
|
+
return type;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const getFriendlyErrorMessage = (errMessage, rawValue, toType)=>{
|
|
38
|
+
if (errMessage.includes("is not a valid")) return `'${rawValue}' is not a valid ${getFriendlyTypeName(toType)}`;
|
|
39
|
+
return errMessage;
|
|
40
|
+
};
|
|
7
41
|
const validateCsvAgainstSchema = (parsed, tableSchema, options)=>{
|
|
8
42
|
const schemaColumns = new Map(tableSchema.columns.map((col)=>[
|
|
9
43
|
col.name,
|
|
10
|
-
col
|
|
44
|
+
col
|
|
11
45
|
]));
|
|
12
46
|
const maxRows = options?.maxRows ?? MAX_ROWS_IN_CSV;
|
|
13
47
|
const errorState = createCsvErrorState();
|
|
@@ -21,22 +55,27 @@ const validateCsvAgainstSchema = (parsed, tableSchema, options)=>{
|
|
|
21
55
|
const rowNum = rowIndex + CSV_FIRST_DATA_ROW_NUMBER;
|
|
22
56
|
const typedRow = {};
|
|
23
57
|
const rawRow = parsed.header.reduce((acc, headerCol, idx)=>{
|
|
24
|
-
acc[headerCol] = rowValues[idx];
|
|
58
|
+
acc[headerCol] = rowValues[idx] ?? "";
|
|
25
59
|
return acc;
|
|
26
60
|
}, {});
|
|
27
61
|
parsed.header.forEach((columnName, columnIndex)=>{
|
|
28
|
-
const rawValue = rowValues[columnIndex];
|
|
29
|
-
const
|
|
30
|
-
|
|
62
|
+
const rawValue = rowValues[columnIndex] ?? "";
|
|
63
|
+
const schemaColumn = schemaColumns.get(columnName);
|
|
64
|
+
const schemaType = schemaColumn?.serverDataType;
|
|
65
|
+
const isRequired = schemaColumn?.required === true;
|
|
66
|
+
if (0 === rawValue.length) {
|
|
67
|
+
if (isRequired) addCsvRowError(errorState, rowNum, columnName, CsvValidationErrorEnum.REQUIRED_FIELD_MISSING, "This field is required and cannot be left blank", rawValue);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
31
70
|
let value;
|
|
32
71
|
const toType = schemaType ?? "string";
|
|
33
72
|
try {
|
|
34
73
|
value = getTypedValue(rawValue, toType);
|
|
35
74
|
} catch (err) {
|
|
36
|
-
addCsvRowError(errorState, rowNum, columnName, CsvValidationErrorEnum.TYPE_MISMATCH, err instanceof Error ? err.message : String(err), rawValue);
|
|
75
|
+
addCsvRowError(errorState, rowNum, columnName, CsvValidationErrorEnum.TYPE_MISMATCH, getFriendlyErrorMessage(err instanceof Error ? err.message : String(err), rawValue, toType), rawValue);
|
|
37
76
|
return;
|
|
38
77
|
}
|
|
39
|
-
if (void 0 === value) return void addCsvRowError(errorState, rowNum, columnName, CsvValidationErrorEnum.TYPE_MISMATCH, `
|
|
78
|
+
if (void 0 === value) return void addCsvRowError(errorState, rowNum, columnName, CsvValidationErrorEnum.TYPE_MISMATCH, `'${rawValue}' is not a valid ${getFriendlyTypeName(toType)}`, rawValue);
|
|
40
79
|
const customValidator = options?.validators?.[columnName];
|
|
41
80
|
if (customValidator) {
|
|
42
81
|
const validationResult = customValidator(rawValue, columnName, rawRow);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRpcError } from "@vuu-ui/vuu-utils";
|
|
1
|
+
import { isRpcError, metadataKeys } from "@vuu-ui/vuu-utils";
|
|
2
2
|
import { CSV_FIRST_DATA_ROW_NUMBER } from "./csv-constants.js";
|
|
3
3
|
const isRecord = (value)=>"object" == typeof value && null !== value;
|
|
4
4
|
const toErrorMessage = (error)=>error instanceof Error ? error.message : String(error);
|
|
@@ -56,4 +56,49 @@ const executeBatchRpcCalls = async (items, executor, errorFormatter)=>{
|
|
|
56
56
|
results
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
|
-
|
|
59
|
+
const getInlineRowErrorMessage = (result)=>{
|
|
60
|
+
if (isRecord(result) && "data" in result && isRecord(result.data)) {
|
|
61
|
+
const data = result.data;
|
|
62
|
+
const msg = data.vuuMsg ?? data.msg;
|
|
63
|
+
if ("string" == typeof msg && "" !== msg.trim()) return msg.trim();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const getRowVuuMsgError = (row, vuuMsgDataIndex, vuuRowNumDataIndex)=>{
|
|
67
|
+
if (-1 === vuuMsgDataIndex) return;
|
|
68
|
+
const msg = Array.isArray(row) ? row[vuuMsgDataIndex] : row?.vuuMsg;
|
|
69
|
+
if ("string" == typeof msg && "" !== msg.trim()) {
|
|
70
|
+
const rowNum = -1 !== vuuRowNumDataIndex && void 0 !== row[vuuRowNumDataIndex] ? Number(row[vuuRowNumDataIndex]) : Number(row[metadataKeys.IDX]) + CSV_FIRST_DATA_ROW_NUMBER;
|
|
71
|
+
return {
|
|
72
|
+
rowNum,
|
|
73
|
+
message: msg.trim()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const waitForSessionErrors = (isRemote, sessionErrors, receivedRowKeys, expectedRowCount, listeners, timeoutMs = 500)=>{
|
|
78
|
+
if (sessionErrors.size > 0 || !isRemote) return Promise.resolve([
|
|
79
|
+
...sessionErrors.values()
|
|
80
|
+
]);
|
|
81
|
+
return new Promise((resolve)=>{
|
|
82
|
+
let timeoutId;
|
|
83
|
+
const onUpdate = ()=>{
|
|
84
|
+
if (sessionErrors.size > 0 || receivedRowKeys.size >= expectedRowCount) {
|
|
85
|
+
cleanup();
|
|
86
|
+
resolve([
|
|
87
|
+
...sessionErrors.values()
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const cleanup = ()=>{
|
|
92
|
+
clearTimeout(timeoutId);
|
|
93
|
+
listeners.delete(onUpdate);
|
|
94
|
+
};
|
|
95
|
+
listeners.add(onUpdate);
|
|
96
|
+
timeoutId = setTimeout(()=>{
|
|
97
|
+
cleanup();
|
|
98
|
+
resolve([
|
|
99
|
+
...sessionErrors.values()
|
|
100
|
+
]);
|
|
101
|
+
}, timeoutMs);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
export { buildRowErrorMessage, createUploadError, executeBatchRpcCalls, getInlineRowErrorMessage, getRowVuuMsgError, hasFileParseErrors, isCsvParseError, mergeValidationWithParseErrors, normalizeTableData, toErrorMessage, toValidationErrorsFromParseRowErrors, waitForSessionErrors };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useData } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
const useImportSchema = ({ dataSource, importSchema, importTable })=>{
|
|
4
|
+
const { getServerAPI } = useData();
|
|
5
|
+
const [fetchedImportSchema, setFetchedImportSchema] = useState();
|
|
6
|
+
useEffect(()=>{
|
|
7
|
+
if (importSchema || void 0 === importTable) return void setFetchedImportSchema(void 0);
|
|
8
|
+
let cancelled = false;
|
|
9
|
+
(async ()=>{
|
|
10
|
+
try {
|
|
11
|
+
const server = await getServerAPI();
|
|
12
|
+
const tableSchema = await server.getTableSchema(importTable);
|
|
13
|
+
if (!cancelled) setFetchedImportSchema(tableSchema);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("[useImportSchema] failed to fetch import table schema", error);
|
|
16
|
+
}
|
|
17
|
+
})();
|
|
18
|
+
return ()=>{
|
|
19
|
+
cancelled = true;
|
|
20
|
+
};
|
|
21
|
+
}, [
|
|
22
|
+
getServerAPI,
|
|
23
|
+
importSchema,
|
|
24
|
+
importTable
|
|
25
|
+
]);
|
|
26
|
+
const resolvedImportSchema = importSchema ?? fetchedImportSchema;
|
|
27
|
+
const sessionOverrides = useMemo(()=>{
|
|
28
|
+
const columns = resolvedImportSchema?.columns.map(({ name })=>name);
|
|
29
|
+
return columns || importTable ? {
|
|
30
|
+
columns,
|
|
31
|
+
table: importTable
|
|
32
|
+
} : void 0;
|
|
33
|
+
}, [
|
|
34
|
+
resolvedImportSchema,
|
|
35
|
+
importTable
|
|
36
|
+
]);
|
|
37
|
+
const schema = importTable ? resolvedImportSchema : resolvedImportSchema ?? dataSource.tableSchema;
|
|
38
|
+
return {
|
|
39
|
+
resolvedImportSchema,
|
|
40
|
+
schema,
|
|
41
|
+
sessionOverrides
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export { useImportSchema };
|