fuse-importer 0.26.3 → 0.26.5

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.
@@ -31,20 +31,6 @@ import { Stepper } from "./Stepper/index";
31
31
  import { TrialModeBanner } from "./TrialModeBanner";
32
32
  import Uploader from "./Uploader";
33
33
  import { useTranslation } from "react-i18next";
34
- var steps = [
35
- {
36
- label: "general:steps.upload",
37
- component: _jsx(Uploader, {}),
38
- },
39
- {
40
- label: "general:steps.match",
41
- component: _jsx(Matcher, {}),
42
- },
43
- {
44
- label: "general:steps.review",
45
- component: _jsx(Review, {}),
46
- },
47
- ];
48
34
  export var Layout = styled(Div)(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n background: ", ";\n display: flex;\n flex-direction: column;\n height: 100vh;\n left: 0;\n overflow-y: auto;\n position: fixed;\n top: 0;\n width: 100vw;\n z-index: ", ";\n ", ";\n"], ["\n background: ", ";\n display: flex;\n flex-direction: column;\n height: 100vh;\n left: 0;\n overflow-y: auto;\n position: fixed;\n top: 0;\n width: 100vw;\n z-index: ", ";\n ", ";\n"])), function (_a) {
49
35
  var theme = _a.theme;
50
36
  return theme.colors.backgroundInterface;
@@ -60,8 +46,13 @@ export var ImporterWithContext = function () {
60
46
  useEffect(function () {
61
47
  layoutRef.current.scrollTo(0, 0);
62
48
  }, [currentStepIndex, matcherSubstep]);
49
+ var stepLabels = [
50
+ t("general:steps.upload"),
51
+ t("general:steps.match"),
52
+ t("general:steps.review"),
53
+ ];
63
54
  function renderContent() {
64
- return (_jsxs(_Fragment, { children: [_jsxs(Div, { children: [previewMode && _jsx(PreviewModeBanner, {}), !previewMode && _jsx(TrialModeBanner, {}), _jsx(Stepper, { stepLabels: steps.map(function (step) { return t(step.label); }), logoURL: logoURL })] }), _jsx(Content, { children: steps[currentStepIndex].component }), inTrialMode && (_jsx(Content, __assign({ inTrialMode: inTrialMode }, { children: _jsx(Footer, {}) })))] }));
55
+ return (_jsxs(_Fragment, { children: [_jsxs(Div, { children: [previewMode && _jsx(PreviewModeBanner, {}), !previewMode && _jsx(TrialModeBanner, {}), _jsx(Stepper, { stepLabels: stepLabels, logoURL: logoURL })] }), _jsxs(Content, { children: [currentStepIndex === 0 && _jsx(Uploader, {}), currentStepIndex === 1 && _jsx(Matcher, {}), currentStepIndex === 2 && _jsx(Review, {})] }), inTrialMode && (_jsx(Content, __assign({ inTrialMode: inTrialMode }, { children: _jsx(Footer, {}) })))] }));
65
56
  }
66
57
  var shouldShowPreviewFirstTime = previewFirstTime && previewMode;
67
58
  return (_jsxs(ThemeProvider, __assign({ theme: theme }, { children: [_jsx(Toast, {}), _jsx(GlobalStyle, {}), _jsx(ImporterFonts, {}), _jsx(ReviewContextProvider, { children: _jsx(Layout, __assign({ zIndex: zIndex, ref: layoutRef }, { children: shouldShowPreviewFirstTime ? _jsx(PreviewFirstTime, {}) : renderContent() })) })] })));
@@ -118,6 +118,10 @@ export var ReviewContextProvider = function (_a) {
118
118
  var _j = useState(null), previewInitialDataSet = _j[0], setPreviewInitialDataSet = _j[1];
119
119
  var _k = useState([]), reviewFields = _k[0], setReviewFields = _k[1];
120
120
  var _l = useState(null), reviewInitialDataSet = _l[0], setReviewInitialDataSet = _l[1];
121
+ useEffect(function () {
122
+ if (currentStepIndex === 1)
123
+ setReviewInitialDataSet(null);
124
+ }, [currentStepIndex]);
121
125
  var reviewDataSetRef = useRef(null);
122
126
  var options = useMemo(function () { return ({
123
127
  batchValidationDelayMs: batchValidationDelayMs,
@@ -137,7 +141,7 @@ export var ReviewContextProvider = function (_a) {
137
141
  link.click();
138
142
  }, [reviewFields, selectedRows]);
139
143
  var onSubmitData = useCallback(function () { return __awaiter(void 0, void 0, void 0, function () {
140
- var dataSet, dataSetValues, validDataSetValues, validDataSetLength, invalidRecords, applicationResponse, isAppResponseSuccessful, _a, errors, isDataValid, invalidDataSet, error_2;
144
+ var dataSet, dataSetValues, validDataSetValues, validDataSetLength, invalidRecords_1, applicationResponse, isAppResponseSuccessful, _a, errors, isDataValid, recordsToDisplayFiltered, invalidDataSet, error_2;
141
145
  return __generator(this, function (_b) {
142
146
  switch (_b.label) {
143
147
  case 0:
@@ -151,19 +155,26 @@ export var ReviewContextProvider = function (_a) {
151
155
  return !record._meta.isInvalid && !isRecordEmpty(record, reviewFields);
152
156
  });
153
157
  validDataSetLength = validDataSetValues.length;
154
- invalidRecords = dataSetValues.filter(function (v) { return v._meta.isInvalid; });
158
+ invalidRecords_1 = dataSetValues.filter(function (v) { return v._meta.isInvalid; });
155
159
  return [4 /*yield*/, onSubmit(validDataSetValues)];
156
160
  case 1:
157
161
  applicationResponse = _b.sent();
158
162
  isAppResponseSuccessful = getImportWasSuccessful(applicationResponse);
159
163
  if (!isAppResponseSuccessful) {
160
164
  _a = (applicationResponse !== null && applicationResponse !== void 0 ? applicationResponse : {}).errors, errors = _a === void 0 ? {} : _a;
161
- invalidRecords = __spreadArray(__spreadArray([], filterAndMapWithBackendErrors(dataSet, errors), true), invalidRecords, true);
165
+ invalidRecords_1 = __spreadArray(__spreadArray([], filterAndMapWithBackendErrors(dataSet, errors), true), invalidRecords_1, true);
162
166
  }
163
- isDataValid = invalidRecords.length === 0;
167
+ isDataValid = invalidRecords_1.length === 0;
164
168
  setImportWasSuccessful(isDataValid && isAppResponseSuccessful);
165
169
  setAppResponse(applicationResponse);
166
- invalidDataSet = recordListToDataSet(invalidRecords, reviewFields);
170
+ if (applicationResponse.recordsToDisplay) {
171
+ recordsToDisplayFiltered = applicationResponse.recordsToDisplay.filter(function (record) {
172
+ var recordId = record._meta.id;
173
+ return !invalidRecords_1.some(function (invalidRecord) { return invalidRecord._meta.id === recordId; });
174
+ });
175
+ invalidRecords_1 = __spreadArray(__spreadArray([], invalidRecords_1, true), (recordsToDisplayFiltered || []), true);
176
+ }
177
+ invalidDataSet = recordListToDataSet(invalidRecords_1, reviewFields);
167
178
  setReviewInitialDataSet(invalidDataSet);
168
179
  if (!!previewMode) return [3 /*break*/, 4];
169
180
  return [4 /*yield*/, trackImportedRowsCount(fuseApi, templateSlug, validDataSetLength)];
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { SpreadsheetOptions } from "../../..";
3
- import { Field, OnValidateRecord, Record, RecordDataSet } from "../../../types";
3
+ import { BatchValidationErrors, Field, Record, RecordDataSet } from "../../../types";
4
+ import { SortBy, SpreadsheetProps } from "./common/types";
4
5
  import { ValidationState } from "./hooks";
5
6
  export type OnDataSetChanged = (dataSet: RecordDataSet) => void;
6
7
  export type OnValidationStateChanged = (state: ValidationState) => void;
@@ -9,6 +10,8 @@ type State = {
9
10
  fields: Field[];
10
11
  isLoading: boolean;
11
12
  isReadOnly?: boolean;
13
+ errors: BatchValidationErrors;
14
+ warnings: BatchValidationErrors;
12
15
  highlightFirstRow: boolean;
13
16
  options: SpreadsheetOptions;
14
17
  sortByFieldErrors: string;
@@ -28,20 +31,12 @@ type State = {
28
31
  addNewRowsToSpreadsheet: () => void;
29
32
  checkboxColumnWidth: number;
30
33
  isFiltering: boolean;
34
+ sortBy: SortBy | null;
35
+ setSortBy: React.Dispatch<React.SetStateAction<SortBy>>;
31
36
  } & ValidationState;
32
- type Props = {
33
- initialDataSet: RecordDataSet;
34
- isReadOnly?: boolean;
37
+ type Props = SpreadsheetProps & {
35
38
  children: JSX.Element;
36
- fields: Field[];
37
- options: SpreadsheetOptions;
38
- highlightFirstRow: boolean;
39
- onValidationStateChanged?: OnValidationStateChanged;
40
- onValidateRecord?: OnValidateRecord;
41
- onDataSetChanged?: OnDataSetChanged;
42
- selectedRows: string[];
43
- setSelectedRows: React.Dispatch<React.SetStateAction<string[]>>;
44
39
  };
45
- export declare const SpreadsheetContextProvider: ({ initialDataSet, fields, options, isReadOnly, highlightFirstRow, children, onValidateRecord, onDataSetChanged, onValidationStateChanged, selectedRows, setSelectedRows, }: Props) => JSX.Element;
40
+ export declare const SpreadsheetContextProvider: ({ initialDataSet, fields, options, isReadOnly, highlightFirstRow, children, onValidateRecord, onDataSetChanged, onValidationStateChanged, selectedRows, setSelectedRows, getSortCompareFn, }: Props) => JSX.Element;
46
41
  export declare const useSpreadsheetContext: () => State;
47
42
  export {};
@@ -25,18 +25,33 @@ import { get } from "lodash";
25
25
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
26
26
  import { addMeta } from "../../../Importer/data";
27
27
  import { useValidationState } from "./hooks";
28
+ var getDefaultSortCompareFn = function (sortBy) {
29
+ return function (a, b) {
30
+ var aField = get(a, sortBy.field);
31
+ var bField = get(b, sortBy.field);
32
+ if (aField === bField)
33
+ return a._meta.rowIndex > b._meta.rowIndex ? 1 : -1;
34
+ if (!aField)
35
+ return 1;
36
+ if (!bField)
37
+ return -1;
38
+ if (sortBy.direction === "asc")
39
+ return aField > bField ? 1 : -1;
40
+ return aField < bField ? 1 : -1;
41
+ };
42
+ };
28
43
  var SpreadsheetContext = createContext({});
29
44
  export var SpreadsheetContextProvider = function (_a) {
30
- var initialDataSet = _a.initialDataSet, fields = _a.fields, options = _a.options, isReadOnly = _a.isReadOnly, highlightFirstRow = _a.highlightFirstRow, children = _a.children, onValidateRecord = _a.onValidateRecord, onDataSetChanged = _a.onDataSetChanged, onValidationStateChanged = _a.onValidationStateChanged, selectedRows = _a.selectedRows, setSelectedRows = _a.setSelectedRows;
31
- var _b = useState(initialDataSet), dataSet = _b[0], setDataSet = _b[1];
32
- var _c = useState(false), isLoading = _c[0], setIsLoading = _c[1];
33
- var _d = useState(null), sortByFieldErrors = _d[0], setSortByFieldErrors = _d[1];
34
- var _e = useState(null), sortByFieldWarnings = _e[0], setSortByFieldWarnings = _e[1];
35
- var _f = useState(""), filteredInputValue = _f[0], setFilteredInputValue = _f[1];
36
- var _g = useState(""), selectedErrorToFilter = _g[0], setSelectedErrorToFilter = _g[1];
37
- var sortByCurrentErrorsRef = useRef({});
38
- var sortByCurrentWarningsRef = useRef({});
39
- var _h = useValidationState({
45
+ var initialDataSet = _a.initialDataSet, fields = _a.fields, options = _a.options, isReadOnly = _a.isReadOnly, highlightFirstRow = _a.highlightFirstRow, children = _a.children, onValidateRecord = _a.onValidateRecord, onDataSetChanged = _a.onDataSetChanged, onValidationStateChanged = _a.onValidationStateChanged, selectedRows = _a.selectedRows, setSelectedRows = _a.setSelectedRows, _b = _a.getSortCompareFn, getSortCompareFn = _b === void 0 ? getDefaultSortCompareFn : _b;
46
+ var _c = useState(initialDataSet), dataSet = _c[0], setDataSet = _c[1];
47
+ var _d = useState(false), isLoading = _d[0], setIsLoading = _d[1];
48
+ var _e = useState(null), sortByFieldErrors = _e[0], setSortByFieldErrors = _e[1];
49
+ var _f = useState(null), sortByFieldWarnings = _f[0], setSortByFieldWarnings = _f[1];
50
+ var _g = useState(""), filteredInputValue = _g[0], setFilteredInputValue = _g[1];
51
+ var _h = useState(""), selectedErrorToFilter = _h[0], setSelectedErrorToFilter = _h[1];
52
+ var _j = useState(null), sortBy = _j[0], setSortBy = _j[1];
53
+ var addRowTimeoutRef = useRef(null);
54
+ var _k = useValidationState({
40
55
  dataSet: dataSet,
41
56
  initialDataSet: initialDataSet,
42
57
  fields: fields,
@@ -45,57 +60,60 @@ export var SpreadsheetContextProvider = function (_a) {
45
60
  setDataSet: setDataSet,
46
61
  setIsLoading: setIsLoading,
47
62
  onValidateRecord: onValidateRecord,
48
- }), errorsRef = _h.errorsRef, warningsRef = _h.warningsRef, updateRecordAndRevalidate = _h.updateRecordAndRevalidate, deleteRecords = _h.deleteRecords, filterRecordsBySearchValue = _h.filterRecordsBySearchValue, filterRecordsByError = _h.filterRecordsByError, validationState = __rest(_h, ["errorsRef", "warningsRef", "updateRecordAndRevalidate", "deleteRecords", "filterRecordsBySearchValue", "filterRecordsByError"]);
49
- var addNewRowsToSpreadsheet = useCallback(function () {
50
- var highestRowIndex = Math.max.apply(Math, Object.values(dataSet).map(function (obj) { return obj._meta.rowIndex; }));
51
- var newRow = addMeta({}, highestRowIndex + 1);
52
- setDataSet(function (oldValues) {
53
- var _a;
54
- return (__assign(__assign({}, oldValues), (_a = {}, _a[newRow._meta.id] = newRow, _a)));
55
- });
56
- }, [dataSet]);
57
- var handleDeleteErrors = useCallback(function (selectedRows) {
58
- var errors = errorsRef.current;
59
- var warnings = errorsRef.current;
60
- var selectedRecordsLength = selectedRows.length;
61
- var dataSetLength = Object.keys(dataSet).length - 1;
62
- var isDeletingAllRows = selectedRecordsLength === dataSetLength;
63
- deleteRecords(setDataSet, selectedRows, errors, warnings, isDeletingAllRows);
64
- }, [dataSet, errorsRef.current, errorsRef.current]);
65
- // sort data based on errors (errors group on top and not errors group at bottom)
66
- var data = useMemo(function () {
63
+ }), updateRecordAndRevalidate = _k.updateRecordAndRevalidate, deleteRecords = _k.deleteRecords, filterRecordsBySearchValue = _k.filterRecordsBySearchValue, filterRecordsByError = _k.filterRecordsByError, warnings = _k.warnings, errors = _k.errors, validationState = __rest(_k, ["updateRecordAndRevalidate", "deleteRecords", "filterRecordsBySearchValue", "filterRecordsByError", "warnings", "errors"]);
64
+ var addNewRowsToSpreadsheet = function () {
65
+ addRowTimeoutRef.current = setTimeout(function () {
66
+ var highestRowIndex = Math.max.apply(Math, Object.values(dataSet).map(function (obj) { return obj._meta.rowIndex; }));
67
+ var newRow = addMeta({}, highestRowIndex + 1);
68
+ setDataSet(function (oldValues) {
69
+ var _a;
70
+ return (__assign(__assign({}, oldValues), (_a = {}, _a[newRow._meta.id] = newRow, _a)));
71
+ });
72
+ addRowTimeoutRef.current = null;
73
+ }, 300);
74
+ };
75
+ var handleDeleteErrors = function (selectedRows) {
76
+ deleteRecords(selectedRows, errors, warnings);
77
+ };
78
+ var dataSnapshot = useMemo(function () {
67
79
  var filteredDatasetByString = filteredInputValue
68
80
  ? filterRecordsBySearchValue(dataSet, filteredInputValue, fields)
69
81
  : dataSet;
70
82
  var filteredDatasetByError = selectedErrorToFilter
71
- ? filterRecordsByError(filteredDatasetByString, selectedErrorToFilter, errorsRef.current, fields)
83
+ ? filterRecordsByError(filteredDatasetByString, selectedErrorToFilter, errors, fields)
72
84
  : filteredDatasetByString;
73
85
  var dataSetValues = Object.values(filteredDatasetByError);
74
86
  if (dataSetValues.length === 0) {
75
87
  setSelectedErrorToFilter("");
76
88
  }
77
- var errors = sortByCurrentErrorsRef.current; //Sort errors snapshot
78
- var warnings = sortByCurrentWarningsRef.current; //Sort warnings snapshot
79
- if (!sortByFieldErrors && !sortByFieldWarnings)
80
- return dataSetValues;
81
- return dataSetValues.sort(function (a, b) {
82
- var aHasErrors = !!get(sortByFieldErrors ? errors : warnings, "".concat(get(a, "_meta.id"), ".").concat(sortByFieldErrors || sortByFieldWarnings), false);
83
- var bHasErrors = !!get(sortByFieldErrors ? errors : warnings, "".concat(get(b, "_meta.id"), ".").concat(sortByFieldErrors || sortByFieldWarnings), false);
84
- if (aHasErrors === bHasErrors) {
85
- return a._meta.rowIndex > b._meta.rowIndex ? 1 : -1;
86
- }
87
- return bHasErrors ? 1 : -1;
88
- });
89
+ if (sortBy) {
90
+ return dataSetValues.sort(getSortCompareFn(sortBy));
91
+ }
92
+ else if (!!sortByFieldErrors || !!sortByFieldWarnings)
93
+ return dataSetValues.sort(function (a, b) {
94
+ var aHasErrors = !!get(sortByFieldErrors ? errors : warnings, "".concat(get(a, "_meta.id"), ".").concat(sortByFieldErrors || sortByFieldWarnings), false);
95
+ var bHasErrors = !!get(sortByFieldErrors ? errors : warnings, "".concat(get(b, "_meta.id"), ".").concat(sortByFieldErrors || sortByFieldWarnings), false);
96
+ if (aHasErrors === bHasErrors) {
97
+ return a._meta.rowIndex > b._meta.rowIndex ? 1 : -1;
98
+ }
99
+ return bHasErrors ? 1 : -1;
100
+ });
101
+ return dataSetValues;
89
102
  }, [
90
- dataSet,
91
103
  filteredInputValue,
92
104
  selectedErrorToFilter,
105
+ sortBy,
106
+ Object.keys(dataSet).length,
93
107
  sortByFieldErrors,
94
108
  sortByFieldWarnings,
95
109
  ]);
96
110
  var isFiltering = useMemo(function () { return !!filteredInputValue; }, [filteredInputValue]);
97
111
  useEffect(function () {
98
112
  onDataSetChanged && onDataSetChanged(dataSet);
113
+ if (addRowTimeoutRef.current) {
114
+ // avoid adding new rows when the user is typing
115
+ clearTimeout(addRowTimeoutRef.current);
116
+ }
99
117
  var dataSetKeys = Object.keys(dataSet);
100
118
  var latestDataSet = dataSet[dataSetKeys[dataSetKeys.length - 1]];
101
119
  var latestDataSetValuesWithoutMeta = Object.values(latestDataSet).filter(function (key) { return key !== "_meta"; });
@@ -109,33 +127,35 @@ export var SpreadsheetContextProvider = function (_a) {
109
127
  }
110
128
  }, [dataSet]);
111
129
  useEffect(function () {
112
- setDataSet(initialDataSet);
113
- }, [initialDataSet]);
114
- useEffect(function () {
115
- onValidationStateChanged && onValidationStateChanged(validationState);
130
+ onValidationStateChanged &&
131
+ onValidationStateChanged(__assign(__assign({}, validationState), { errors: errors, warnings: warnings }));
116
132
  }, [validationState, onValidationStateChanged]);
117
- var handleSortByFieldErrors = useCallback(function (value) {
118
- //current errors snapshot for use on sort to keep data sorted without changing with each edit or revalidation.
119
- sortByCurrentErrorsRef.current = __assign({}, errorsRef.current);
120
- setSortByFieldErrors(value);
133
+ var handleSortByFieldErrors = useCallback(function (field) {
134
+ setSortByFieldErrors(field === sortByFieldErrors ? null : field);
121
135
  setSortByFieldWarnings(null);
122
- }, []);
123
- var handleSortByFieldWarnings = useCallback(function (value) {
124
- //current errors snapshot for use on sort to keep data sorted without changing with each edit or revalidation.
125
- sortByCurrentWarningsRef.current = __assign({}, warningsRef.current);
126
- setSortByFieldWarnings(value);
136
+ setSortBy(null);
137
+ }, [sortByFieldErrors]);
138
+ var handleSortByFieldWarnings = useCallback(function (field) {
139
+ setSortByFieldWarnings(field === sortByFieldWarnings ? null : field);
127
140
  setSortByFieldErrors(null);
128
- }, []);
141
+ setSortBy(null);
142
+ }, [sortByFieldWarnings]);
129
143
  var lastClickedIndexRef = useRef(null);
130
144
  var checkboxColumnWidth = useMemo(function () {
131
- var _a, _b, _c, _d;
132
- var lastRowIndexLength = (_d = (_c = (_b = (_a = data[data.length - 1]) === null || _a === void 0 ? void 0 : _a._meta) === null || _b === void 0 ? void 0 : _b.rowIndex) === null || _c === void 0 ? void 0 : _c.toString()) === null || _d === void 0 ? void 0 : _d.length;
145
+ var lastRowIndexLength = dataSnapshot.length.toString().length;
133
146
  var singleLetterWidth = 15;
134
147
  var horizontalSpacing = 8;
135
148
  var baseCheckboxColumnWidth = lastRowIndexLength * singleLetterWidth + horizontalSpacing;
136
149
  return baseCheckboxColumnWidth < 40 ? 40 : baseCheckboxColumnWidth;
137
- }, [data]);
138
- var value = __assign(__assign({ data: data, fields: fields, isLoading: isLoading }, validationState), { isReadOnly: isReadOnly, sortByFieldErrors: sortByFieldErrors, sortByFieldWarnings: sortByFieldWarnings, highlightFirstRow: highlightFirstRow, options: options, setIsLoading: setIsLoading, setSortByFieldErrors: handleSortByFieldErrors, setSortByFieldWarnings: handleSortByFieldWarnings, onValidateRecord: onValidateRecord, updateRecord: updateRecordAndRevalidate, selectedRows: selectedRows, setSelectedRows: setSelectedRows, deleteRecords: handleDeleteErrors, filteredInputValue: filteredInputValue, setFilteredInputValue: setFilteredInputValue, lastClickedIndexRef: lastClickedIndexRef, addNewRowsToSpreadsheet: addNewRowsToSpreadsheet, checkboxColumnWidth: checkboxColumnWidth, selectedErrorToFilter: selectedErrorToFilter, setSelectedErrorToFilter: setSelectedErrorToFilter, isFiltering: isFiltering });
150
+ }, [dataSnapshot]);
151
+ var updatedDataSnapshot = useMemo(function () {
152
+ return dataSnapshot.map(function (data) {
153
+ var _a;
154
+ var updatedData = dataSet[(_a = data._meta) === null || _a === void 0 ? void 0 : _a.id];
155
+ return updatedData;
156
+ });
157
+ }, [dataSet, dataSnapshot]);
158
+ var value = __assign(__assign({ data: updatedDataSnapshot, fields: fields, isLoading: isLoading }, validationState), { isReadOnly: isReadOnly, sortByFieldErrors: sortByFieldErrors, sortByFieldWarnings: sortByFieldWarnings, highlightFirstRow: highlightFirstRow, options: options, sortBy: sortBy, warnings: warnings, errors: errors, setIsLoading: setIsLoading, setSortByFieldErrors: handleSortByFieldErrors, setSortByFieldWarnings: handleSortByFieldWarnings, onValidateRecord: onValidateRecord, updateRecord: updateRecordAndRevalidate, selectedRows: selectedRows, setSelectedRows: setSelectedRows, deleteRecords: handleDeleteErrors, filteredInputValue: filteredInputValue, setFilteredInputValue: setFilteredInputValue, lastClickedIndexRef: lastClickedIndexRef, addNewRowsToSpreadsheet: addNewRowsToSpreadsheet, checkboxColumnWidth: checkboxColumnWidth, selectedErrorToFilter: selectedErrorToFilter, setSelectedErrorToFilter: setSelectedErrorToFilter, setSortBy: setSortBy, isFiltering: isFiltering });
139
159
  return (_jsx(SpreadsheetContext.Provider, __assign({ value: value }, { children: children })));
140
160
  };
141
161
  export var useSpreadsheetContext = function () { return useContext(SpreadsheetContext); };
@@ -0,0 +1,24 @@
1
+ /// <reference types="react" />
2
+ import { RecordDataSet, Field, SpreadsheetOptions, OnValidateRecord, Record } from "../../../../types";
3
+ import { OnDataSetChanged, OnValidationStateChanged } from "../SpreadsheetContextProvider";
4
+ export declare enum SortOrder {
5
+ ASC = "asc",
6
+ DESC = "desc"
7
+ }
8
+ export type SortBy = {
9
+ field: string;
10
+ direction: SortOrder;
11
+ };
12
+ export type SpreadsheetProps = {
13
+ initialDataSet: RecordDataSet;
14
+ isReadOnly?: boolean;
15
+ fields?: Field[];
16
+ highlightFirstRow?: boolean;
17
+ options?: SpreadsheetOptions;
18
+ onValidateRecord?: OnValidateRecord;
19
+ onDataSetChanged?: OnDataSetChanged;
20
+ onValidationStateChanged?: OnValidationStateChanged;
21
+ selectedRows?: string[];
22
+ setSelectedRows?: React.Dispatch<React.SetStateAction<string[]>>;
23
+ getSortCompareFn?: (sortBy: SortBy) => (a: Record, b: Record) => number;
24
+ };
@@ -0,0 +1,5 @@
1
+ export var SortOrder;
2
+ (function (SortOrder) {
3
+ SortOrder["ASC"] = "asc";
4
+ SortOrder["DESC"] = "desc";
5
+ })(SortOrder || (SortOrder = {}));
@@ -1,8 +1,15 @@
1
1
  import React from "react";
2
2
  import { Field } from "../../../../types";
3
+ import { SortOrder } from "../common/types";
4
+ type SortIndicatorProps = {
5
+ direction: SortOrder;
6
+ };
7
+ export declare const SortIndicator: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, import("../../../../styled/utils").CSSProps & SortIndicatorProps, never>;
3
8
  export type FieldHeaderProps = {
4
9
  field: Field;
5
10
  sortByFieldErrors: (name: string) => void;
6
11
  sortByFieldWarnings: (name: string) => void;
7
12
  };
8
13
  export declare const FieldHeader: React.FC<FieldHeaderProps>;
14
+ export declare const chevronDown: JSX.Element;
15
+ export {};
@@ -19,19 +19,45 @@ import styled, { css } from "styled-components";
19
19
  import { TooltipProvider } from "../../../../common";
20
20
  import { Div } from "../../../../styled/utils";
21
21
  import { useSpreadsheetContext } from "../SpreadsheetContextProvider";
22
+ import { SortOrder } from "../common/types";
22
23
  var ErrorCount = styled(Div)(function (_a) {
23
24
  var _b = _a.theme, colors = _b.colors, c = _b.css;
24
25
  return css(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n color: ", ";\n background: ", ";\n border: solid 1px ", ";\n padding: 4px 7px;\n border-radius: 56px;\n min-width: 24px;\n ", ";\n "], ["\n color: ", ";\n background: ", ";\n border: solid 1px ", ";\n padding: 4px 7px;\n border-radius: 56px;\n min-width: 24px;\n ", ";\n "])), colors.error, colors.red100, colors.error, c.centered);
25
26
  });
27
+ export var SortIndicator = styled(Div)(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n ", "\n svg {\n path {\n fill: ", ";\n }\n }\n"], ["\n ", "\n svg {\n path {\n fill: ", ";\n }\n }\n"])), function (p) {
28
+ return p.direction === SortOrder.ASC && css(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n transform: rotate(180deg);\n "], ["\n transform: rotate(180deg);\n "])));
29
+ }, function (p) { return p.theme.colors.gray300; });
26
30
  export var FieldHeader = function (_a) {
27
31
  var field = _a.field, sortByFieldErrors = _a.sortByFieldErrors, sortByFieldWarnings = _a.sortByFieldWarnings;
28
- var _b = useSpreadsheetContext(), columnErrorCounts = _b.columnErrorCounts, columnWarningCounts = _b.columnWarningCounts;
32
+ var _b = useSpreadsheetContext(), columnErrorCounts = _b.columnErrorCounts, columnWarningCounts = _b.columnWarningCounts, sortBy = _b.sortBy, filteredInputValue = _b.filteredInputValue, setSortBy = _b.setSortBy;
29
33
  var fieldErrorCount = columnErrorCounts[field.name];
30
34
  var fieldWarningCount = columnWarningCounts[field.name];
31
- return (_jsxs(Div, __assign({ "data-test-id": "column-header-".concat(field.name), p: "0 16px", spaceBetween: true, w100: true, alignCenter: true }, { children: [_jsxs(Div, __assign({ truncateText: true }, { children: [field.label, field.isRequired && "*"] })), fieldErrorCount && (_jsx(TooltipProvider, __assign({ mr: 3, w: 35, tooltip: "Sort by ".concat(field.label, " errors") }, { children: _jsx(ErrorCount, __assign({ "data-test-id": "column-errors-count-".concat(field.name), onClick: function () { return sortByFieldErrors(field.name); } }, { children: _jsx(Div, __assign({ centered: true }, { children: _jsx(Div, __assign({ w: 35, textAlignCenter: true, label3: true, truncateText: true }, { children: fieldErrorCount })) })) })) }))), !fieldErrorCount && fieldWarningCount && (_jsx(TooltipProvider, __assign({ mr: 3, w: 35, tooltip: "Sort by ".concat(field.label, " errors") }, { children: _jsx(ErrorCount, __assign({ style: {
35
+ var shouldRenderErrorCount = fieldErrorCount && filteredInputValue === "";
36
+ var onHeaderClick = function () {
37
+ if ((sortBy === null || sortBy === void 0 ? void 0 : sortBy.field) === field.name && (sortBy === null || sortBy === void 0 ? void 0 : sortBy.direction) === SortOrder.ASC) {
38
+ setSortBy({
39
+ field: field.name,
40
+ direction: SortOrder.DESC,
41
+ });
42
+ }
43
+ else {
44
+ setSortBy({
45
+ field: field.name,
46
+ direction: SortOrder.ASC,
47
+ });
48
+ }
49
+ };
50
+ return (_jsxs(Div, __assign({ "data-test-id": "column-header-".concat(field.name), p: "0 16px", spaceBetween: true, w100: true, alignCenter: true, onClick: onHeaderClick }, { children: [_jsxs(Div, __assign({ dflex: true }, { children: [_jsxs(Div, __assign({ truncateText: true, selectNone: true }, { children: [field.label, field.isRequired && "*"] })), _jsx(Div, __assign({ ml: 8, centered: true }, { children: (sortBy === null || sortBy === void 0 ? void 0 : sortBy.field) === field.name && (_jsx(SortIndicator, __assign({ direction: sortBy === null || sortBy === void 0 ? void 0 : sortBy.direction }, { children: chevronDown }))) }))] })), shouldRenderErrorCount && (_jsx(TooltipProvider, __assign({ mr: 3, w: 35, tooltip: "Sort by ".concat(field.label, " errors") }, { children: _jsx(ErrorCount, __assign({ "data-test-id": "column-errors-count-".concat(field.name), onClick: function (ev) {
51
+ ev.stopPropagation();
52
+ sortByFieldErrors(field.name);
53
+ } }, { children: _jsx(Div, __assign({ centered: true }, { children: _jsx(Div, __assign({ w: 35, textAlignCenter: true, label3: true, truncateText: true }, { children: fieldErrorCount })) })) })) }))), !fieldErrorCount && fieldWarningCount && (_jsx(TooltipProvider, __assign({ mr: 3, w: 35, tooltip: "Sort by ".concat(field.label, " errors") }, { children: _jsx(ErrorCount, __assign({ style: {
32
54
  color: colors.orange1200,
33
55
  background: colors.orange1100,
34
56
  boxShadow: "0px 0px 0px 1px ".concat(colors.orange500, " inset"),
35
- }, "data-test-id": "column-errors-count-".concat(field.name), onClick: function () { return sortByFieldWarnings(field.name); } }, { children: _jsx(Div, __assign({ centered: true }, { children: _jsx(Div, __assign({ w: 35, textAlignCenter: true, label3: true, truncateText: true }, { children: fieldWarningCount })) })) })) })))] })));
57
+ }, "data-test-id": "column-errors-count-".concat(field.name), onClick: function (ev) {
58
+ ev.stopPropagation();
59
+ sortByFieldWarnings(field.name);
60
+ } }, { children: _jsx(Div, __assign({ centered: true }, { children: _jsx(Div, __assign({ w: 35, textAlignCenter: true, label3: true, truncateText: true }, { children: fieldWarningCount })) })) })) })))] })));
36
61
  };
37
- var templateObject_1;
62
+ export var chevronDown = (_jsx("svg", __assign({ width: "9", height: "5", viewBox: "0 0 9 5", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, { children: _jsx("path", { d: "M4.31848 4.93945L0.421366 0.439453L8.2156 0.439454L4.31848 4.93945Z", fill: "#102622" }) })));
63
+ var templateObject_1, templateObject_2, templateObject_3;
@@ -68,26 +68,16 @@ var SpreadsheetData = function () {
68
68
  */
69
69
  return res * horizontalScrollVisibilityRatio;
70
70
  })();
71
- var sortByErrors = useCallback(function (field) {
72
- return setSortByFieldErrors(function (sortByFieldErrors) {
73
- return sortByFieldErrors === field ? null : field;
74
- });
75
- }, []);
76
- var sortByWarnings = useCallback(function (field) {
77
- return setSortByFieldWarnings(function (sortByFieldWarnings) {
78
- return sortByFieldWarnings === field ? null : field;
79
- });
80
- }, []);
81
71
  var columns = useMemo(function () {
82
72
  return fields.map(function (field) { return ({
83
- Header: function () { return (_jsx(FieldHeader, { field: field, sortByFieldErrors: sortByErrors, sortByFieldWarnings: sortByWarnings })); },
73
+ Header: function () { return (_jsx(FieldHeader, { field: field, sortByFieldErrors: setSortByFieldErrors, sortByFieldWarnings: setSortByFieldWarnings })); },
84
74
  Content: function (record) {
85
75
  var Input = inputTypes[field.type];
86
76
  var rowIndex = record.rowIndex, recordProp = __rest(record, ["rowIndex"]);
87
77
  return (_jsx(Input, { id: "".concat(field.name, "-").concat(rowIndex), fieldType: field.type, name: field.name, options: field.options, record: recordProp }));
88
78
  },
89
79
  }); });
90
- }, [fields, sortByErrors]);
80
+ }, [fields, setSortByFieldErrors]);
91
81
  var onTableScroll = useCallback(function (scrollTop) {
92
82
  if (rowNumbersGridRef.current) {
93
83
  var grid = rowNumbersGridRef.current;
@@ -72,12 +72,9 @@ export var SpreadsheetRowCheckboxes = function (_a) {
72
72
  var handleRowSelect = function (checked, rowIndex, rowMetaId) {
73
73
  var lastRowMetaId = data[data.length - 1]._meta.id;
74
74
  var isLastRow = rowMetaId === lastRowMetaId;
75
- var isSelectingRowHeader = firstRowIsHeader && rowIndex === 0;
76
- // Prevent user from deleting the last row and
77
- // If the first row is a header, we don't want to select it
78
- if (isSelectingRowHeader || isLastRow) {
75
+ var isLastRowAndEmpty = isLastRow && rowIsEmpty(data[rowIndex]);
76
+ if (isLastRowAndEmpty)
79
77
  return;
80
- }
81
78
  // If the first row is a header, we need to adjust the rowIndex
82
79
  var adjustedRowIndex = firstRowIsHeader ? rowIndex - 1 : rowIndex;
83
80
  if (checked) {
@@ -101,8 +98,9 @@ export var SpreadsheetRowCheckboxes = function (_a) {
101
98
  var rangeEndIndex_1 = Math.max(clickedRowIndex, lastClickedIndex);
102
99
  var lastRowId_1 = data[data.length - 1]._meta.id;
103
100
  var updatedSelectedRows = selectedRows.filter(function (value, index) {
104
- return (index < rangeStartIndex_1 && value !== lastRowId_1) ||
105
- (index > rangeEndIndex_1 && value !== lastRowId_1);
101
+ return index < rangeStartIndex_1 ||
102
+ index > rangeEndIndex_1 ||
103
+ (value === lastRowId_1 && rowIsEmpty(data[data.length - 1]));
106
104
  });
107
105
  setSelectedRows(updatedSelectedRows);
108
106
  }
@@ -117,8 +115,10 @@ export var SpreadsheetRowCheckboxes = function (_a) {
117
115
  var range = data.slice(Math.min(rangeStartIndex, rangeEndIndex), Math.max(rangeStartIndex, rangeEndIndex) + 1);
118
116
  var rangeIds = range.map(function (row) { return row === null || row === void 0 ? void 0 : row._meta.id; });
119
117
  var lastRowId_2 = data[data.length - 1]._meta.id;
118
+ var isLastRowEmpty_1 = rowIsEmpty(data[data.length - 1]);
120
119
  var updatedSelectedRows = __spreadArray(__spreadArray([], selectedRows, true), rangeIds, true).filter(function (value, index, array) {
121
- return array.indexOf(value) === index && value !== lastRowId_2;
120
+ return array.indexOf(value) === index &&
121
+ (value !== lastRowId_2 || (value === lastRowId_2 && !isLastRowEmpty_1));
122
122
  });
123
123
  setSelectedRows(updatedSelectedRows);
124
124
  lastClickedIndexRef.current = rowIndex;
@@ -128,8 +128,9 @@ export var SpreadsheetRowCheckboxes = function (_a) {
128
128
  if (checked) {
129
129
  var allRows = data
130
130
  .filter(function (row) {
131
- var lastRowIndex = data[data.length - 1]._meta.id;
132
- return row._meta.id !== lastRowIndex;
131
+ var isLastRow = row._meta.id === data[data.length - 1]._meta.id;
132
+ var isLastRowAndEmpty = isLastRow && rowIsEmpty(row);
133
+ return !isLastRowAndEmpty;
133
134
  })
134
135
  .map(function (row) {
135
136
  return row === null || row === void 0 ? void 0 : row._meta.id;
@@ -70,7 +70,7 @@ export var useSpreadsheetField = function (name, record, fieldType) {
70
70
  updateRecord(newRecord);
71
71
  return [2 /*return*/];
72
72
  });
73
- }); }, 300), [record]);
73
+ }); }, 50), [record]);
74
74
  var updateValue = useCallback(function (value) {
75
75
  setValue(value);
76
76
  updateData(value);
@@ -31,8 +31,6 @@ type UseValidationStateReturnValue = ValidationState & {
31
31
  filterRecordsBySearchValue: FilterRecordsBySearchValue;
32
32
  filterRecordsByError: FilterRecordsByError;
33
33
  updateRecordAndRevalidate: (record: Record) => Promise<void>;
34
- errorsRef: React.MutableRefObject<BatchValidationErrors>;
35
- warningsRef: React.MutableRefObject<BatchValidationErrors>;
36
34
  };
37
35
  export declare const useValidationState: ({ initialDataSet, dataSet, fields, options, isReadOnly, setDataSet, setIsLoading, onValidateRecord, }: UseValidationStateArgs) => UseValidationStateReturnValue;
38
36
  export {};
@@ -46,7 +46,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import { omit, set } from "lodash";
49
- import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
49
+ import { useCallback, useEffect, useMemo, useState, } from "react";
50
50
  import { sleep } from "../../..";
51
51
  import { selectValidatorsFor, validateRecord } from "./validation";
52
52
  import { useTableActions, } from "../../../common/Table/TableActions/hooks";
@@ -61,9 +61,7 @@ export var useValidationState = function (_a) {
61
61
  var _c = useState({}), warnings = _c[0], setWarnings = _c[1];
62
62
  var _d = useState(0), validationPercentage = _d[0], setValidationPercentage = _d[1];
63
63
  var validatorsCache = useMemo(function () { return selectValidatorsFor(fields); }, [fields]);
64
- var errorsRef = useRef(errors);
65
- var warningsRef = useRef(warnings);
66
- var _e = useTableActions({ setErrors: setErrors, setWarnings: setWarnings, fields: fields }), deleteRecords = _e.deleteRecords, filterRecordsByError = _e.filterRecordsByError, filterRecordsBySearchValue = _e.filterRecordsBySearchValue;
64
+ var _e = useTableActions({ setDataSet: setDataSet, setErrors: setErrors, setWarnings: setWarnings }), deleteRecords = _e.deleteRecords, filterRecordsByError = _e.filterRecordsByError, filterRecordsBySearchValue = _e.filterRecordsBySearchValue;
67
65
  // Validate record and update dataSet
68
66
  var updateRecordAndRevalidate = useCallback(function (record) { return __awaiter(void 0, void 0, void 0, function () {
69
67
  var recordId, newDataSet, data, _a, recordErrors, recordWarnings, hasError, hasWarning;
@@ -93,7 +91,6 @@ export var useValidationState = function (_a) {
93
91
  var newState = hasError
94
92
  ? set(newErrors, recordId, recordErrors)
95
93
  : omit(newErrors, recordId);
96
- errorsRef.current = newState;
97
94
  return newState;
98
95
  });
99
96
  setWarnings(function (warnings) {
@@ -101,7 +98,6 @@ export var useValidationState = function (_a) {
101
98
  var newState = hasWarning
102
99
  ? set(newWarnings, recordId, recordWarnings)
103
100
  : omit(newWarnings, recordId);
104
- warningsRef.current = newState;
105
101
  return newState;
106
102
  });
107
103
  setDataSet(newDataSet); //Update the dataSet
@@ -171,9 +167,7 @@ export var useValidationState = function (_a) {
171
167
  return [4 /*yield*/, sleep(batchValidationDelayMs)];
172
168
  case 6:
173
169
  _d.sent();
174
- errorsRef.current = errors;
175
170
  setErrors(errors);
176
- warningsRef.current = warnings;
177
171
  setWarnings(warnings);
178
172
  _d.label = 7;
179
173
  case 7:
@@ -229,8 +223,6 @@ export var useValidationState = function (_a) {
229
223
  validationPercentage: validationPercentage,
230
224
  isValidationCompleted: isValidationCompleted,
231
225
  isValid: isValid,
232
- errorsRef: errorsRef,
233
- warningsRef: warningsRef,
234
226
  updateRecordAndRevalidate: updateRecordAndRevalidate,
235
227
  deleteRecords: deleteRecords,
236
228
  filterRecordsBySearchValue: filterRecordsBySearchValue,
@@ -1,20 +1,6 @@
1
1
  import React from "react";
2
- import { Field, OnValidateRecord, Record, RecordDataSet, SpreadsheetOptions } from "../../../types";
3
- import { OnDataSetChanged, OnValidationStateChanged } from "./SpreadsheetContextProvider";
4
- export type SpreadsheetProps = {
5
- initialDataSet: RecordDataSet;
6
- isReadOnly?: boolean;
7
- fields?: Field[];
8
- submittedData?: Record[];
9
- highlightFirstRow?: boolean;
10
- options?: SpreadsheetOptions;
11
- onValidateRecord?: OnValidateRecord;
12
- onDataSetChanged?: OnDataSetChanged;
13
- onValidationStateChanged?: OnValidationStateChanged;
14
- selectedRows?: string[];
15
- setSelectedRows?: React.Dispatch<React.SetStateAction<string[]>>;
16
- };
17
- declare const _default: React.MemoExoticComponent<({ isReadOnly, initialDataSet, fields, options, highlightFirstRow, onValidateRecord, onDataSetChanged, onValidationStateChanged, selectedRows, setSelectedRows, }: SpreadsheetProps) => JSX.Element>;
2
+ import { SpreadsheetProps } from "./common/types";
3
+ declare const _default: React.MemoExoticComponent<({ initialDataSet, ...props }: SpreadsheetProps) => JSX.Element>;
18
4
  export default _default;
19
- export * from "./hooks";
20
5
  export * from "./SpreadsheetContextProvider";
6
+ export * from "./hooks";