Skip to content

Commit

Permalink
fix: some fixes to ImportRecordsFlow component (#868)
Browse files Browse the repository at this point in the history
* fix: some fixes to ImportRecordsFlow component

* fix: scss font family lint error

* fix: drag behavior

* fix: improve example

* fix: add required and defaultTo to attributes and set the match field as required

* fix: read raw values from cells

* fix: normalize data types

* fix: change data structure send in onComplete event

* fix: eslintrc config indent rule

* fix: change select field to assign label
  • Loading branch information
LeandroTorresSicilia authored and TahimiLeonBravo committed Aug 6, 2019
1 parent 6fcb7b8 commit 6f2f622
Show file tree
Hide file tree
Showing 19 changed files with 392 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Expand Up @@ -4,7 +4,7 @@
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"indent": ["error", 4],
"indent": ["error", 4, { "SwitchCase": 1 }],
"react/jsx-indent": ["error", 4],
"react/jsx-indent-props": ["error", 4],
"react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx"] }],
Expand Down
@@ -1,5 +1,7 @@
export default function getAssignFieldsData(schemaFields, fieldsMap) {
return schemaFields.map(field => ({
export default function getAssignFieldsData(params) {
const { fieldsMap, attributes, matchField } = params;
return Object.keys(attributes).map(field => ({
required: attributes[field].required || field === matchField,
fileField: fieldsMap[field],
databaseField: field,
}));
Expand Down
79 changes: 64 additions & 15 deletions src/components/ImportRecordsFlow/helpers/getDataToImport.js
@@ -1,16 +1,65 @@
export default function getDataToImport(data, fieldsMap) {
const fields = Object.keys(fieldsMap);
return data.map(item => ({
...fields.reduce((acc, field) => {
const keys = fieldsMap[field].split(',');
const value = keys.reduce(
(accumulator, key) => `${accumulator} ${item[key]}`.trim(),
'',
);
return {
...acc,
[field]: value,
};
}, {}),
}));
import ImportRecordsFlow from '../';

function getDateValue(value) {
const date = new Date(value);
const isValidDate = !isNaN(date.getTime());
return isValidDate ? date : '';
}

function getTypeValue(value, dataType) {
switch (dataType.name) {
case 'String':
return value;

case 'Number':
return !isNaN(Number(value)) ? Number(value) : '';

case 'Boolean':
return !!value;

case 'Date':
return getDateValue(value);

default:
return value || '';
}
}

function getNormalizedFieldValue(value, attributeDef) {
if (typeof attributeDef === 'function') {
return getTypeValue(value, attributeDef);
}
if (typeof attributeDef.type === 'function') {
return getTypeValue(value, attributeDef.type);
}
return value || '';
}

export default function getDataToImport(params) {
const actionTypeMap = {
'add-records': ImportRecordsFlow.ADD_RECORDS,
'merge-records': ImportRecordsFlow.MERGE_RECORDS,
};
const { data, fieldsMap, schema, actionOption, matchField } = params;

return {
collection: schema.collection,
actionType: actionTypeMap[actionOption],
mergeByKey: matchField === 'default' ? '' : matchField,
data: data.map(item => ({
...Object.keys(fieldsMap).reduce((acc, field) => {
const keys = fieldsMap[field].split(',');
const schemaField = schema.attributes[field];
const value =
keys.reduce(
(accumulator, key) => `${accumulator} ${item[key] || ''}`.trim(),
'',
) || schemaField.defaultTo;
return {
...acc,
[field]: getNormalizedFieldValue(value, schemaField),
};
}, {}),
})),
};
}
@@ -0,0 +1,45 @@
function getDateValue(value) {
const date = new Date(value);
const isValidDate = !isNaN(date.getTime());
return isValidDate ? value : '';
}

function getTypeValue(value, dataType) {
switch (dataType.name) {
case 'String':
return value;

case 'Number':
return !isNaN(Number(value)) ? value : '';

case 'Boolean':
return value ? 'true' : 'false';

case 'Date':
return getDateValue(value);

default:
return value || '';
}
}

function getNormalizedFieldValue(value, attributeDef) {
if (typeof attributeDef === 'function') {
return getTypeValue(value, attributeDef);
}
if (typeof attributeDef.type === 'function') {
return getTypeValue(value, attributeDef.type);
}
return value || '';
}

export default function getFieldAssignedPreviewData(data, field, fileFields, attributes) {
return data.map(item => {
const value =
fileFields.reduce((acc, fileField) => `${acc} ${item[fileField] || ''}`.trim(), '') ||
attributes[field].defaultTo;
return {
[field]: getNormalizedFieldValue(value, attributes[field]),
};
});
}
49 changes: 49 additions & 0 deletions src/components/ImportRecordsFlow/helpers/getPreviewDataToImport.js
@@ -0,0 +1,49 @@
function getDateValue(value) {
const date = new Date(value);
const isValidDate = !isNaN(date.getTime());
return isValidDate ? value : '';
}

function getTypeValue(value, dataType) {
switch (dataType.name) {
case 'String':
return value;

case 'Number':
return !isNaN(Number(value)) ? value : '';

case 'Boolean':
return value ? 'true' : 'false';

case 'Date':
return getDateValue(value);

default:
return value || '';
}
}

function getNormalizedFieldValue(value, attributeDef) {
if (typeof attributeDef === 'function') {
return getTypeValue(value, attributeDef);
}
if (typeof attributeDef.type === 'function') {
return getTypeValue(value, attributeDef.type);
}
return value || '';
}

export default function getPreviewDataToImport(data, fieldsMap, attributes) {
return data.map(item => ({
...Object.keys(fieldsMap).reduce((acc, field) => {
const keys = fieldsMap[field].split(',');
const value =
keys.reduce((accumulator, key) => `${accumulator} ${item[key] || ''}`.trim(), '') ||
attributes[field].defaultTo;
return {
...acc,
[field]: getNormalizedFieldValue(value, attributes[field]),
};
}, {}),
}));
}
@@ -0,0 +1,9 @@
export default function isStepThreeNextButtonDisabled(params) {
const { fieldsMap, attributes, matchField } = params;
const fieldsArray = Object.keys(attributes);
return (
fieldsArray.some(
field => (attributes[field].required || field === matchField) && !fieldsMap[field],
) || fieldsArray.every(field => !fieldsMap[field])
);
}
42 changes: 29 additions & 13 deletions src/components/ImportRecordsFlow/index.js
Expand Up @@ -5,6 +5,7 @@ import Modal from '../Modal';
import getDataFromWorkbook from './helpers/getDataFromWorkbook';
import getHeaderRowFromWorkbook from './helpers/getHeaderRowFromWorkbook';
import getDataToImport from './helpers/getDataToImport';
import isStepThreeNextButtonDisabled from './helpers/isStepThreeNextButtonDisabled';
import Footer from './footer';
import StepOne from './stepOne';
import StepTwo from './stepTwo';
Expand Down Expand Up @@ -32,7 +33,10 @@ function EmptyComponent() {
return null;
}

export default function ImportRecordsFlow(props) {
const ADD_RECORDS = Symbol('add-records');
const MERGE_RECORDS = Symbol('merge-records');

function ImportRecordsFlow(props) {
const { className, style, isOpen, onRequestClose, schema, onComplete } = props;
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [actionOption, setActionOption] = useState('');
Expand All @@ -45,21 +49,14 @@ export default function ImportRecordsFlow(props) {
const [data, setData] = useState([]);
const [fieldsMap, setFieldsMap] = useState({});
const [schemaFields, setSchemaFields] = useState([]);
const [dataToImport, setDataToImport] = useState([]);

const isBackButtonDisabled = currentStepIndex === 0;
const currentStep = stepNames[currentStepIndex];
const StepComponent = stepsMap[currentStep] || EmptyComponent;

useEffect(() => {
setSchemaFields(Object.keys(schema.attributes));
}, [schema]);

useEffect(() => {
if (currentStepIndex === 3) {
setDataToImport(getDataToImport(data, fieldsMap));
}
}, [currentStepIndex, data, fieldsMap]);
}, [schema.attributes]);

const getModalTitle = () => {
if (currentStepIndex === 1 && hasFileSelected) {
Expand All @@ -77,7 +74,15 @@ export default function ImportRecordsFlow(props) {

const goNextStep = () => {
if (currentStepIndex === 3) {
onComplete(dataToImport);
onComplete(
getDataToImport({
data,
fieldsMap,
schema,
actionOption,
matchField,
}),
);
}
if (currentStepIndex < stepNames.length - 1) {
const nextStepIndex = currentStepIndex + 1;
Expand All @@ -93,7 +98,11 @@ export default function ImportRecordsFlow(props) {
return !hasFileSelected || isLoading;
}
if (currentStepIndex === 2) {
return !schemaFields.some(field => fieldsMap[field]);
return isStepThreeNextButtonDisabled({
fieldsMap,
attributes: schema.attributes,
matchField,
});
}
return false;
};
Expand All @@ -107,7 +116,7 @@ export default function ImportRecordsFlow(props) {
const reader = new FileReader();
reader.onload = event => {
const uInt8ArrayData = new Uint8Array(event.target.result);
const workbook = XLSX.read(uInt8ArrayData, { type: 'array' });
const workbook = XLSX.read(uInt8ArrayData, { type: 'array', raw: true });
setColumns(getHeaderRowFromWorkbook(workbook));
setData(getDataFromWorkbook(workbook));
setIsLoading(false);
Expand All @@ -134,6 +143,8 @@ export default function ImportRecordsFlow(props) {
const handleCloseModal = () => {
setCurrentStepIndex(0);
removeFile();
setActionOption('');
setMatchField('default');
onRequestClose();
};

Expand All @@ -157,6 +168,7 @@ export default function ImportRecordsFlow(props) {
>
<StepComponent
schemaFields={schemaFields}
attributes={schema.attributes}
actionOption={actionOption}
onChangeAction={setActionOption}
matchField={matchField}
Expand All @@ -171,7 +183,6 @@ export default function ImportRecordsFlow(props) {
onRemoveFile={removeFile}
onAssignField={assignField}
fieldsMap={fieldsMap}
dataToImport={dataToImport}
/>
</Modal>
);
Expand All @@ -197,3 +208,8 @@ ImportRecordsFlow.defaultProps = {
onComplete: () => {},
schema: {},
};

ImportRecordsFlow.MERGE_RECORDS = MERGE_RECORDS;
ImportRecordsFlow.ADD_RECORDS = ADD_RECORDS;

export default ImportRecordsFlow;
17 changes: 10 additions & 7 deletions src/components/ImportRecordsFlow/readme.md
Expand Up @@ -8,9 +8,16 @@
const schema = {
collection: 'users',
attributes: {
name: {},
email: {},
phone: {},
name: {
type: String,
required: true,
},
email: String,
driver: {
type: Number,
defaultTo: 0,
},
date: Date,
},
};

Expand Down Expand Up @@ -59,9 +66,5 @@
<div className="rainbow-m-right_medium">
<ImportRecordsFlowModal />
</div>
<ButtonGroup>
<ButtonIcon icon={<FontAwesomeIcon icon={faPlus} />} variant="border-filled" disabled />
<ButtonIcon icon={<FontAwesomeIcon icon={faEllipsisV} />} variant="border-filled" disabled />
</ButtonGroup>
</GlobalHeader>
</div>
13 changes: 9 additions & 4 deletions src/components/ImportRecordsFlow/stepFour/index.js
Expand Up @@ -2,10 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import Table from '../../Table';
import Column from '../../Column';
import getPreviewDataToImport from '../helpers/getPreviewDataToImport';

export default function StepFour(props) {
const { schemaFields, dataToImport } = props;
const previewData = dataToImport.slice(0, 5);
const { schemaFields, data, fieldsMap, attributes } = props;
const previewData = getPreviewDataToImport(data.slice(0, 5), fieldsMap, attributes);

return (
<Table className="rainbow-import-records-flow_table" keyField="id" data={previewData}>
Expand All @@ -19,10 +20,14 @@ export default function StepFour(props) {

StepFour.propTypes = {
schemaFields: PropTypes.array,
dataToImport: PropTypes.array,
data: PropTypes.array,
fieldsMap: PropTypes.object,
attributes: PropTypes.object,
};

StepFour.defaultProps = {
schemaFields: [],
dataToImport: [],
data: [],
fieldsMap: {},
attributes: {},
};

0 comments on commit 6f2f622

Please sign in to comment.