From d835d6bb575a4be28651eedebdb2d81cee683927 Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Wed, 31 May 2023 14:09:53 +0530 Subject: [PATCH 01/22] add. new fields for person --- src/components/common/Dropdown.js | 37 +++++++++ src/containers/FormContainer.css | 10 +++ src/containers/FormContainer.js | 119 ++++++++++++++++++++++++--- src/containers/FormContainer.test.js | 4 +- 4 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 src/components/common/Dropdown.js diff --git a/src/components/common/Dropdown.js b/src/components/common/Dropdown.js new file mode 100644 index 0000000..2fbabed --- /dev/null +++ b/src/components/common/Dropdown.js @@ -0,0 +1,37 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; + +const Dropdown = props => { + const { name, required, title, items, onChange, value } = props; + return ( +
+
+ +
+
+ +
+
+ ); +}; + +Dropdown.propTypes = { + title: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired +}; + +export default Dropdown; diff --git a/src/containers/FormContainer.css b/src/containers/FormContainer.css index 1752eab..987b10a 100644 --- a/src/containers/FormContainer.css +++ b/src/containers/FormContainer.css @@ -80,6 +80,16 @@ color: red; } +.div-select { + border: 1.2px solid #9b9b9b; + border-radius: var(--border-radius); + height: var(--height); + width: 220px; + background-color: rgba(250, 250, 250, 0.5); + margin-bottom: 0.8rem; + outline: 0 none; +} + @media (max-width: 910px) { #root > div > form > div:nth-child(3) > fieldset > div { width: 80%; diff --git a/src/containers/FormContainer.js b/src/containers/FormContainer.js index be0ad6d..8e039b1 100644 --- a/src/containers/FormContainer.js +++ b/src/containers/FormContainer.js @@ -8,10 +8,12 @@ import ModalError from '../components/common/modals/ModalError'; import ModalSuccess from '../components/common/modals/ModalSuccess'; import moment from 'moment'; import './FormContainer.css'; +import Dropdown from '../components/common/Dropdown'; // Bahmni person API URL const url = process.env.REACT_APP_URL; const genderOptions = ['Male', 'Female', 'Other']; +const phoneTypes = ['', 'Mobile', 'Landline']; // set state class FormContainer extends Component { @@ -21,6 +23,11 @@ class FormContainer extends Component { middleName: '', lastName: '', gender: '', + organization: '', + email: '', + phoneNumber: '', + phoneType: '', + occupation: '', birthdate: moment(), birthdateEstimated: false }, @@ -98,6 +105,11 @@ class FormContainer extends Component { firstName: '', middleName: '', lastName: '', + organization: '', + email: '', + phoneNumber: '', + phoneType: '', + occupation: '', gender: '', birthdate: moment(), birthdateEstimated: false @@ -113,6 +125,11 @@ class FormContainer extends Component { firstName, lastName, gender, + organization, + email, + phoneNumber, + phoneType, + occupation, birthdate, birthdateEstimated } = this.state.person; @@ -192,6 +209,11 @@ class FormContainer extends Component { middleName, lastName, gender, + organization, + email, + phoneNumber, + phoneType, + occupation, birthdate, birthdateEstimated } = this.state.person; @@ -230,10 +252,7 @@ class FormContainer extends Component { return (
- +
@@ -366,12 +385,92 @@ class FormContainer extends Component {

-
+ {modal} diff --git a/src/containers/FormContainer.test.js b/src/containers/FormContainer.test.js index cb901a9..dea851d 100644 --- a/src/containers/FormContainer.test.js +++ b/src/containers/FormContainer.test.js @@ -11,8 +11,8 @@ describe('FormContainer', () => { wrapper = shallow(); }); - it('renders seven components', () => { - expect(wrapper.find(Input).length).toEqual(7); + it('renders eleven components', () => { + expect(wrapper.find(Input).length).toEqual(11); }); it('renders one component', () => { From d7cff8a781c418e4b07274fd008a71486046ff85 Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Wed, 31 May 2023 20:14:36 +0530 Subject: [PATCH 02/22] css changes for Form items --- src/components/common/Button.css | 3 ++- src/containers/FormContainer.css | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/common/Button.css b/src/components/common/Button.css index cc5e08c..2010bc0 100644 --- a/src/components/common/Button.css +++ b/src/components/common/Button.css @@ -1,6 +1,7 @@ .buttonContainer { display: flex; - justify-content: flex-end; + padding: 0 0 0 2%; + justify-content: space-evenly; } .buttonWithSpinner { diff --git a/src/containers/FormContainer.css b/src/containers/FormContainer.css index 987b10a..8b50acd 100644 --- a/src/containers/FormContainer.css +++ b/src/containers/FormContainer.css @@ -38,7 +38,7 @@ #hidden-title, #display-none { - opacity: 0; + display: none; } /* Radio buttons flex container */ From a226bbc36fe0a0daf6ed269f608290239f28dfed Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Wed, 31 May 2023 20:14:58 +0530 Subject: [PATCH 03/22] update required fields and add cancel button --- src/containers/FormContainer.js | 54 +++++++++++++-------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/src/containers/FormContainer.js b/src/containers/FormContainer.js index 8e039b1..39b8ca9 100644 --- a/src/containers/FormContainer.js +++ b/src/containers/FormContainer.js @@ -1,18 +1,17 @@ +import moment from 'moment'; import React, { Component } from 'react'; -import Navbar from '../components/common/Navbar'; -import Input from '../components/common/Input'; -import RadioButtonGroup from '../components/common/RadioButtonGroup'; -import Checkbox from '../components/common/Checkbox'; import Button from '../components/common/Button'; +import Checkbox from '../components/common/Checkbox'; +import Dropdown from '../components/common/Dropdown'; +import Input from '../components/common/Input'; +import Navbar from '../components/common/Navbar'; import ModalError from '../components/common/modals/ModalError'; import ModalSuccess from '../components/common/modals/ModalSuccess'; -import moment from 'moment'; import './FormContainer.css'; -import Dropdown from '../components/common/Dropdown'; // Bahmni person API URL const url = process.env.REACT_APP_URL; -const genderOptions = ['Male', 'Female', 'Other']; +const genderOptions = ['', 'Male', 'Female', 'Other']; const phoneTypes = ['', 'Mobile', 'Landline']; // set state @@ -225,12 +224,7 @@ class FormContainer extends Component { lastCreatedPerson } = this.state; - const isEnabled = - firstName.length > 0 && - lastName.length > 0 && - gender.length > 0 && - birthdate.length > 0 && - !isRequestLoading; + const isEnabled = firstName.length > 0 && !isRequestLoading; let modal = null; @@ -253,7 +247,7 @@ class FormContainer extends Component { return (
-
+
Name @@ -292,7 +286,6 @@ class FormContainer extends Component { onChange={this.handleChange} value={lastName} id="lastName" - required={true} />
@@ -314,7 +307,6 @@ class FormContainer extends Component { value={birthdate} id="birthdate" max={moment().format('YYYY-MM-DD')} - required={true} />
- Gender + Gender
-
@@ -399,7 +389,6 @@ class FormContainer extends Component { onChange={this.handleChange} value={organization} id="organization" - required={true} />
@@ -412,7 +401,6 @@ class FormContainer extends Component { onChange={this.handleChange} value={email} id="email" - required={true} />
@@ -425,7 +413,6 @@ class FormContainer extends Component { onChange={this.handleChange} value={phoneNumber} id="phoneNumber" - required={true} />
@@ -435,7 +422,6 @@ class FormContainer extends Component { value={phoneType} items={phoneTypes} onChange={this.handleChange} - required={true} />
@@ -448,7 +434,6 @@ class FormContainer extends Component { onChange={this.handleChange} value={occupation} id="occupation" - required={true} />
@@ -458,16 +443,19 @@ class FormContainer extends Component {
+
From 6c90c99f6d9451e2cd89b07894d1929eee0dd7fc Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Thu, 1 Jun 2023 16:03:01 +0530 Subject: [PATCH 04/22] update. test for new gender field --- src/containers/FormContainer.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/containers/FormContainer.test.js b/src/containers/FormContainer.test.js index dea851d..2c0119a 100644 --- a/src/containers/FormContainer.test.js +++ b/src/containers/FormContainer.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import FormContainer from './FormContainer'; import Input from '../components/common/Input'; -import SelectFromList from '../components/common/RadioButtonGroup'; +import SelectFromList from '../components/common/Dropdown'; describe('FormContainer', () => { let wrapper; @@ -15,8 +15,8 @@ describe('FormContainer', () => { expect(wrapper.find(Input).length).toEqual(11); }); - it('renders one component', () => { - expect(wrapper.find(SelectFromList).length).toEqual(1); + it('renders two component', () => { + expect(wrapper.find(SelectFromList).length).toEqual(2); }); it('renders firstName input', () => { @@ -48,7 +48,7 @@ describe('FormContainer', () => { let selectGender; beforeEach(() => { - selectGender = wrapper.find('#selectGender'); + selectGender = wrapper.find('#lastName'); selectGender.simulate('change', { target: { name: 'gender', value: exampleGenderSelected } }); From 6c7b87df6a8c42421bb1b29e77b8946953df78c8 Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Sat, 3 Jun 2023 16:50:06 +0530 Subject: [PATCH 05/22] add localStorage for person url --- src/components/common/Button.css | 17 +++++++++++++++++ src/components/common/Button.js | 4 +++- src/components/common/constants.js | 7 +++++++ src/containers/FormContainer.js | 9 +++++---- src/index.css | 2 +- src/setupTests.js | 9 +++++++++ 6 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/components/common/constants.js diff --git a/src/components/common/Button.css b/src/components/common/Button.css index 2010bc0..5fb9113 100644 --- a/src/components/common/Button.css +++ b/src/components/common/Button.css @@ -28,6 +28,23 @@ background-color: #9b9b9b; } +.cancelButtonWithSpinner { + border: none; + border-radius: var(--border-radius); + height: var(--height); + background-color: #393939; + color: white; + margin-bottom: 50px; + margin-right: 1rem; + padding: 0 45px; + width: 155px; + cursor: pointer; +} + +.cancelButtonWithSpinner:hover { + background-color: #595959; +} + .spinner { position: relative; border-radius: 50%; diff --git a/src/components/common/Button.js b/src/components/common/Button.js index 7f4ec19..0c4d8a4 100644 --- a/src/components/common/Button.js +++ b/src/components/common/Button.js @@ -37,7 +37,9 @@ const Button = props => { return (
From b9feaebf4c4e818a3f71b25e3e1def53d7aa9a79 Mon Sep 17 00:00:00 2001 From: Arjun-Go Date: Tue, 6 Jun 2023 07:18:57 +0530 Subject: [PATCH 07/22] update tests and optional fields --- src/api/personApi.js | 10 +- src/components/common/Button.js | 2 +- src/components/common/Dropdown.js | 2 +- src/components/common/constants.js | 1 - src/containers/FormContainer.js | 137 +++++++++++++++++++-------- src/containers/FormContainer.test.js | 8 +- 6 files changed, 110 insertions(+), 50 deletions(-) diff --git a/src/api/personApi.js b/src/api/personApi.js index 619e44b..bbfbe25 100644 --- a/src/api/personApi.js +++ b/src/api/personApi.js @@ -1,10 +1,10 @@ import { Constants } from '../components/common/constants'; -export const getPersonAttributeTypeUuid = async attributeName => { +export async function getPersonAttributeTypeUuid(attributeName) { try { const url = Constants.personAttributeType + '?q=' + attributeName + '&v=default'; - const response = await fetch(url).then(response => { + const response = await fetch(url).then(function(response) { if (!response.status === 200) { throw Error({ response: response }); } @@ -15,9 +15,9 @@ export const getPersonAttributeTypeUuid = async attributeName => { console.error(error); return error.response; } -}; +} -export const savePerson = async payload => { +export async function savePerson(payload) { try { return await fetch(Constants.person, { method: 'POST', @@ -32,4 +32,4 @@ export const savePerson = async payload => { console.error(error); return error.response; } -}; +} diff --git a/src/components/common/Button.js b/src/components/common/Button.js index 0c4d8a4..958c746 100644 --- a/src/components/common/Button.js +++ b/src/components/common/Button.js @@ -38,7 +38,7 @@ const Button = props => {
+
+
+ + {modal} + + + ); + } +} + +export default EditPerson; diff --git a/src/containers/EditPerson.test.js b/src/containers/EditPerson.test.js new file mode 100644 index 0000000..03f333e --- /dev/null +++ b/src/containers/EditPerson.test.js @@ -0,0 +1,61 @@ +import React from 'react'; +import { shallow, render as mount } from 'enzyme'; +import EditPerson from './EditPerson'; +import Input from '../components/common/Input'; +import SelectFromList from '../components/common/Dropdown'; + +describe('EditPerson', () => { + let wrapper; + + beforeEach(() => { + wrapper = shallow( + + ); + }); + + it('renders fourteen components', () => { + expect(wrapper.find(Input).length).toEqual(14); + }); + + it('renders one component', () => { + expect(wrapper.find(SelectFromList).length).toEqual(1); + }); + + it('renders firstName input', () => { + expect(wrapper.find('#firstName').length).toEqual(1); + }); + + it('Checks if firstName input is disabled', () => { + expect(wrapper.find('#firstName').prop('disabled')).toBe(true); + }); + + it('renders lastName input', () => { + expect(wrapper.find('#lastName').length).toEqual(1); + }); + + it('Checks if lastName input is disabled', () => { + expect(wrapper.find('#lastName').prop('disabled')).toBe(true); + }); + + it('renders email input', () => { + expect(wrapper.find('#email').length).toEqual(1); + }); + + describe('the user populates email input', () => { + const exampleEmail = 'Max@gmail.com'; + let firstNameInput; + + beforeEach(() => { + firstNameInput = wrapper.find('#email'); + firstNameInput.simulate('change', { + target: { value: exampleEmail, name: 'email' } + }); + }); + + it('should update the state property email', () => { + expect(wrapper.state().person.email).toEqual(exampleEmail); + }); + }); +}); // end of outer describe diff --git a/src/index.css b/src/index.css index 658ff13..fae669f 100644 --- a/src/index.css +++ b/src/index.css @@ -100,6 +100,10 @@ input[type='checkbox']:checked + label span { background: url('../src/assets/checkbox_checked.svg') 0px center no-repeat; } +input[type='checkbox']:disabled + label span { + cursor: not-allowed; +} + @media (max-width: 1080px) { form { width: 70%; From 1c7bb290c0b27e6ee719e2c51fa61027964232b8 Mon Sep 17 00:00:00 2001 From: Himabindu T Date: Thu, 29 Jun 2023 09:56:45 +0530 Subject: [PATCH 17/22] Bindu | BAH-3057 | Gender of person/patient is not consistent across the patient management app and patient registration page. (#153) --- src/containers/CreatePerson.js | 14 ++++++++++++++ src/containers/EditPerson.js | 12 ++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/containers/CreatePerson.js b/src/containers/CreatePerson.js index 80e8f4d..ad78d8e 100644 --- a/src/containers/CreatePerson.js +++ b/src/containers/CreatePerson.js @@ -170,6 +170,19 @@ class CreatePerson extends Component { return value === '' ? true : false; }; + getGender = gender => { + switch (gender) { + case 'Male': + return 'M'; + case 'Female': + return 'F'; + case 'Other': + return 'O'; + default: + return gender; + } + }; + createFormPayload = () => { const { firstName, @@ -262,6 +275,7 @@ class CreatePerson extends Component { submitRequest(formPayload) { const { firstName, lastName } = this.state.person; + formPayload.gender = this.getGender(formPayload.gender); this.setState({ isRequestLoading: true }); diff --git a/src/containers/EditPerson.js b/src/containers/EditPerson.js index 4e81ebd..1b684a1 100644 --- a/src/containers/EditPerson.js +++ b/src/containers/EditPerson.js @@ -125,12 +125,12 @@ class EditPerson extends Component { getGender = gender => { switch (gender) { - case 'M': - return 'Male'; - case 'F': - return 'Female'; - case 'O': - return 'Other'; + case 'Male': + return 'M'; + case 'Female': + return 'F'; + case 'Other': + return 'O'; default: return gender; } From 2109197fc7492e90ae9fa42db4d93d10fd500d3c Mon Sep 17 00:00:00 2001 From: Soorya Kumaran C <90232857+SooryaKumaranC-tw@users.noreply.github.com> Date: Fri, 4 Aug 2023 11:56:06 +0530 Subject: [PATCH 18/22] BAH-3139 | Fix DOB flakiness (#154) --- src/containers/CreatePerson.js | 97 +++++++++++++---------------- src/containers/CreatePerson.test.js | 70 +++++++++++++++++++++ 2 files changed, 113 insertions(+), 54 deletions(-) diff --git a/src/containers/CreatePerson.js b/src/containers/CreatePerson.js index ad78d8e..6f3e735 100644 --- a/src/containers/CreatePerson.js +++ b/src/containers/CreatePerson.js @@ -26,6 +26,11 @@ class CreatePerson extends Component { lastName: '', gender: '', birthdate: moment(), + age: { + years: 0, + months: 0, + days: 0 + }, birthdateEstimated: false, organization: '', email: '', @@ -85,7 +90,28 @@ class CreatePerson extends Component { handleChange = ({ target: input }) => { const person = { ...this.state.person }; - person[input.name] = input.value; + if (input.name === 'birthdate') { + var birthdate = input.value; + const today = moment(); + person.age.years = moment.duration(today.diff(birthdate)).years(); + person.age.months = moment.duration(today.diff(birthdate)).months(); + person.age.days = moment.duration(today.diff(birthdate)).days(); + person.birthdate = birthdate; + } else if ( + input.name === 'years' || + input.name === 'months' || + input.name === 'days' + ) { + person.age[input.name] = input.value; + const now = moment(); + const currentDOB = moment() + .year(now.year() - person.age.years) + .month(now.month() - person.age.months) + .date(now.date() - person.age.days); + person.birthdate = currentDOB.format('YYYY-MM-DD'); + } else { + person[input.name] = input.value; + } this.setState({ person }); }; @@ -101,50 +127,6 @@ class CreatePerson extends Component { }); }; - fromAgetoDate = e => { - // input name: years, months or days - let inputName = e.target.name; - // the user input for the years or months or days - let inputValue = e.target.value; - - // mapping the values with the momentsjs required format - const getMomentFormat = { - year: 'years', - month: 'months', - day: 'days' - }; - // takes two dates (now and current birthdate input) and calculates - // the difference between them in years, months and days - function toAge(date) { - let now = moment(); - let userPickedDate = moment(date); - const diffDuration = moment.duration(now.diff(userPickedDate)); - const age = { - year: diffDuration.years(), - month: diffDuration.months(), - day: diffDuration.days() - }; - return age; - } - - this.setState(prevState => { - const prevBirthdate = prevState.person.birthdate; - - const toAgeObject = toAge(prevBirthdate); - let diff = inputValue - toAgeObject[inputName]; - - const person = { ...this.state.person }; - person.birthdate = moment(prevBirthdate) - .subtract(diff, getMomentFormat[inputName]) - .subtract(1, 'days') - .format('YYYY-MM-DD'); - - return { - person - }; - }); - }; - handleClearForm() { this.setState({ person: { @@ -160,6 +142,11 @@ class CreatePerson extends Component { occupation: '', gender: '', birthdate: moment(), + age: { + years: 0, + months: 0, + days: 0 + }, birthdateEstimated: false }, isRequestError: false @@ -347,6 +334,8 @@ class CreatePerson extends Component { birthdateEstimated } = this.state.person; + const { years, months, days } = this.state.person.age; + const { isRequestError, isRequestLoading, @@ -457,11 +446,11 @@ class CreatePerson extends Component { { let wrapper; @@ -58,4 +59,73 @@ describe('CreatePerson', () => { expect(wrapper.state().person.gender).toEqual(exampleGenderSelected); }); }); // end of gender options describe + + describe('the user populates birthdate', () => { + const exampleDOB = '2019-07-01'; + let dobInput; + + beforeEach(() => { + dobInput = wrapper.find('#birthdate'); + dobInput.simulate('change', { + target: { value: exampleDOB, name: 'birthdate' } + }); + }); + + it('should update the state property birthdate', () => { + expect(wrapper.state().person.birthdate).toEqual(exampleDOB); + }); + + it('requires birthdate input', () => { + expect(dobInput.props().required).toBe(true); + }); + + it('should update the state property age', () => { + const age = { years: 0, months: 0, days: 0 }; + const today = moment(); + age.years = moment.duration(today.diff(exampleDOB)).years(); + age.months = moment.duration(today.diff(exampleDOB)).months(); + age.days = moment.duration(today.diff(exampleDOB)).days(); + expect(wrapper.state().person.age.years).toEqual(age.years); + expect(wrapper.state().person.age.months).toEqual(age.months); + expect(wrapper.state().person.age.days).toEqual(age.days); + }); + }); // end of birthdate describe + + describe('the user populates age (year, month and day)', () => { + const exampleYears = 4; + const exampleMonths = 1; + const exampleDays = 2; + let yearsInput, monthsInput, daysInput; + beforeEach(() => { + yearsInput = wrapper.find('#age'); + monthsInput = wrapper.find('#months'); + daysInput = wrapper.find('#days'); + yearsInput.simulate('change', { + target: { value: exampleYears, name: 'years' } + }); + monthsInput.simulate('change', { + target: { value: exampleMonths, name: 'months' } + }); + daysInput.simulate('change', { + target: { value: exampleDays, name: 'days' } + }); + }); + + it('should update the state property years, months and days', () => { + expect(wrapper.state().person.age.years).toEqual(exampleYears); + expect(wrapper.state().person.age.months).toEqual(exampleMonths); + expect(wrapper.state().person.age.days).toEqual(exampleDays); + }); + + it('should update the state property age', () => { + const now = moment(); + const dob = moment() + .year(now.year() - exampleYears) + .month(now.month() - exampleMonths) + .date(now.date() - exampleDays); + expect(wrapper.state().person.birthdate).toEqual( + dob.format('YYYY-MM-DD') + ); + }); + }); // end of birthdate describe }); // end of outer describe From 7f34b8aa51e0823f9936e2b21da341c9282d5091 Mon Sep 17 00:00:00 2001 From: Kavitha S Date: Mon, 14 Aug 2023 00:59:00 +0530 Subject: [PATCH 19/22] Kavitha | A-1205219540895165 | fix gender not shown in edit page --- src/containers/EditPerson.css | 1 + src/containers/EditPerson.js | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/containers/EditPerson.css b/src/containers/EditPerson.css index 532951b..3513375 100644 --- a/src/containers/EditPerson.css +++ b/src/containers/EditPerson.css @@ -89,6 +89,7 @@ background-color: rgba(250, 250, 250, 0.5); margin-bottom: 0.8rem; outline: 0 none; + color: black; } @media (max-width: 910px) { diff --git a/src/containers/EditPerson.js b/src/containers/EditPerson.js index 1b684a1..4e81ebd 100644 --- a/src/containers/EditPerson.js +++ b/src/containers/EditPerson.js @@ -125,12 +125,12 @@ class EditPerson extends Component { getGender = gender => { switch (gender) { - case 'Male': - return 'M'; - case 'Female': - return 'F'; - case 'Other': - return 'O'; + case 'M': + return 'Male'; + case 'F': + return 'Female'; + case 'O': + return 'Other'; default: return gender; } From 8622f39ed327e9e7597f04e11cd88c0f69fa5bab Mon Sep 17 00:00:00 2001 From: Arjun G <91885483+Arjun-Go@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:33:12 +0530 Subject: [PATCH 20/22] A-1206201728816127 | Other Attributes handled through Configuration (#156) * update. Other information to handle configurable fields * handle visibilty of other attributes --- src/api/personApi.js | 13 + src/components/common/constants.js | 13 +- src/containers/CreatePerson.css | 6 + src/containers/CreatePerson.js | 331 +++++++--------------- src/containers/EditPerson.js | 423 ++++++++--------------------- 5 files changed, 240 insertions(+), 546 deletions(-) diff --git a/src/api/personApi.js b/src/api/personApi.js index 5d95b38..082ab8c 100644 --- a/src/api/personApi.js +++ b/src/api/personApi.js @@ -81,3 +81,16 @@ export async function updatePerson(uuid, payload) { return error.response; } } + +export async function fetchPersonAttributeConfig() { + try { + const url = Constants.registrationConfig; + return await fetch(url, { + method: 'GET', + credentials: 'include' + }); + } catch (error) { + console.error(error); + return error.response; + } +} diff --git a/src/components/common/constants.js b/src/components/common/constants.js index ca88590..65e1bf4 100644 --- a/src/components/common/constants.js +++ b/src/components/common/constants.js @@ -2,19 +2,12 @@ const hostUrl = localStorage.getItem('host') ? 'https://' + localStorage.getItem('host') : ''; const RESTWS_V1 = hostUrl + '/openmrs/ws/rest/v1'; +const bahmniConfig = hostUrl + '/bahmni_config'; export const Constants = { person: RESTWS_V1 + '/person', - personAttributeType: RESTWS_V1 + '/personattributetype' + personAttributeType: RESTWS_V1 + '/personattributetype', + registrationConfig: bahmniConfig + '/openmrs/apps/registration/app.json' }; export const genderOptions = ['', 'Male', 'Female', 'Other']; -export const personAttributes = { - organization: 'organization', - email: 'email', - mobilePhone: 'mobilePhone', - workPhone: 'workPhone', - residencePhone: 'residencePhone', - otherPhone: 'otherPhone', - occupation: 'occupationNew' -}; export const phoneNumberPattern = '[0-9]{10}'; export const emailPattern = '[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,4}$'; diff --git a/src/containers/CreatePerson.css b/src/containers/CreatePerson.css index 532951b..0f3dd94 100644 --- a/src/containers/CreatePerson.css +++ b/src/containers/CreatePerson.css @@ -3,10 +3,16 @@ width: 80%; } +.other-attributes { + display: flex; + flex-wrap: wrap; +} + /* outer flex container with Name, Age, Gender sections */ .flex-container-row { display: flex; flex-wrap: wrap; + flex: 0 0 50%; width: 100%; padding-top: 10px; } diff --git a/src/containers/CreatePerson.js b/src/containers/CreatePerson.js index 6f3e735..11ae611 100644 --- a/src/containers/CreatePerson.js +++ b/src/containers/CreatePerson.js @@ -1,17 +1,16 @@ import moment from 'moment'; import React, { Component } from 'react'; -import { getPersonAttributeTypeUuid, savePerson } from '../api/personApi'; +import { + getPersonAttributeTypeUuid, + savePerson, + fetchPersonAttributeConfig +} from '../api/personApi'; import Button from '../components/common/Button'; import Checkbox from '../components/common/Checkbox'; import Dropdown from '../components/common/Dropdown'; import Input from '../components/common/Input'; import Navbar from '../components/common/Navbar'; -import { - emailPattern, - genderOptions, - personAttributes, - phoneNumberPattern -} from '../components/common/constants'; +import { genderOptions } from '../components/common/constants'; import ModalError from '../components/common/modals/ModalError'; import ModalSuccess from '../components/common/modals/ModalSuccess'; import './CreatePerson.css'; @@ -31,61 +30,51 @@ class CreatePerson extends Component { months: 0, days: 0 }, - birthdateEstimated: false, - organization: '', - email: '', - mobilePhone: '', - workPhone: '', - residencePhone: '', - otherPhone: '', - occupation: '' + birthdateEstimated: false }, showModal: false, isAPIError: false, isRequestError: false, isRequestLoading: false, lastCreatedPerson: '', - attributes: { - organizationUuid: '', - emailUuid: '', - mobilePhoneUuid: '', - workPhoneUuid: '', - residencePhoneUuid: '', - otherPhoneUuid: '', - occupationUuid: '' - } + attributes: [] }; this.handleClearForm = this.handleClearForm.bind(this); } componentDidMount() { - this.setPersonAttributeIDs(); - } + this.getAttributes().then(response => { + const attributes = response.config.personAttributesForRelations.map( + async attribute => { + const uuid = await getPersonAttributeTypeUuid( + attribute.attributeName + ); + return { + ...attribute, + value: '', + uuid: uuid + }; + } + ); - setPersonAttributeIDs = async () => { - this.setState({ - attributes: { - organizationUuid: await getPersonAttributeTypeUuid( - personAttributes.organization - ), - emailUuid: await getPersonAttributeTypeUuid(personAttributes.email), - mobilePhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.mobilePhone - ), - workPhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.workPhone - ), - residencePhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.residencePhone - ), - otherPhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.otherPhone - ), - occupationUuid: await getPersonAttributeTypeUuid( - personAttributes.occupation - ) - } + Promise.all(attributes).then(resolvedAttributes => { + this.setState({ + attributes: resolvedAttributes + }); + }); }); + } + + getAttributes = async () => { + const response = await fetchPersonAttributeConfig(); + if (response.status === 200) { + return response.json(); + } else { + return Promise.reject({ + status: response.status, + statusText: response.statusText + }); + } }; handleChange = ({ target: input }) => { @@ -115,6 +104,15 @@ class CreatePerson extends Component { this.setState({ person }); }; + handleOtherAttributesChange = ({ target: input }) => { + const attributes = [...this.state.attributes]; + const index = attributes.findIndex( + attribute => attribute.name === input.name + ); + attributes[index].value = input.value; + this.setState({ attributes }); + }; + handleCheckbox = ({ target: input }) => { const person = { ...this.state.person }; person[input.name] = input.checked; @@ -133,13 +131,6 @@ class CreatePerson extends Component { firstName: '', middleName: '', lastName: '', - organization: '', - email: '', - mobilePhone: '', - workPhone: '', - residencePhone: '', - otherPhone: '', - occupation: '', gender: '', birthdate: moment(), age: { @@ -151,6 +142,10 @@ class CreatePerson extends Component { }, isRequestError: false }); + this.state.attributes.map(attribute => { + attribute.value = ''; + return attribute; + }); } isVoided = value => { @@ -176,17 +171,20 @@ class CreatePerson extends Component { middleName, lastName, gender, - organization, - email, - mobilePhone, - workPhone, - residencePhone, - otherPhone, - occupation, birthdate, birthdateEstimated } = this.state.person; + const attributes = this.state.attributes.map(attribute => { + return { + attributeType: { + uuid: attribute.uuid + }, + voided: this.isVoided(attribute.value), + value: attribute.value + }; + }); + const formPayload = { names: [ { @@ -199,57 +197,7 @@ class CreatePerson extends Component { birthdate: birthdate + 'T12:00:00.000+0000', age: moment.duration(moment().diff(birthdate)).years(), birthdateEstimated, - attributes: [ - { - attributeType: { - uuid: this.state.attributes.organizationUuid - }, - voided: this.isVoided(organization), - value: organization - }, - { - attributeType: { - uuid: this.state.attributes.emailUuid - }, - voided: this.isVoided(email), - value: email - }, - { - attributeType: { - uuid: this.state.attributes.mobilePhoneUuid - }, - voided: this.isVoided(mobilePhone), - value: mobilePhone - }, - { - attributeType: { - uuid: this.state.attributes.workPhoneUuid - }, - voided: this.isVoided(workPhone), - value: workPhone - }, - { - attributeType: { - uuid: this.state.attributes.residencePhoneUuid - }, - voided: this.isVoided(residencePhone), - value: residencePhone - }, - { - attributeType: { - uuid: this.state.attributes.otherPhoneUuid - }, - voided: this.isVoided(otherPhone), - value: otherPhone - }, - { - attributeType: { - uuid: this.state.attributes.occupationUuid - }, - voided: this.isVoided(occupation), - value: occupation - } - ] + attributes: attributes }; return formPayload; }; @@ -323,16 +271,10 @@ class CreatePerson extends Component { middleName, lastName, gender, - organization, - email, - mobilePhone, - workPhone, - residencePhone, - otherPhone, - occupation, birthdate, birthdateEstimated } = this.state.person; + const personAttributes = this.state.attributes; const { years, months, days } = this.state.person.age; @@ -500,123 +442,54 @@ class CreatePerson extends Component { -
-
-
- Other Information + {personAttributes.length > 0 && ( +
+
+
+
+ Other Information + {personAttributes.map(attribute => { + return ( +
+
+ +
+
+ ); + })} +
+
+
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
-
-
-
-
-
-
-
-
+ )} {modal} diff --git a/src/containers/EditPerson.js b/src/containers/EditPerson.js index 4e81ebd..6af44cd 100644 --- a/src/containers/EditPerson.js +++ b/src/containers/EditPerson.js @@ -3,19 +3,15 @@ import React, { Component } from 'react'; import { fetchPerson, getPersonAttributeTypeUuid, - updatePerson + updatePerson, + fetchPersonAttributeConfig } from '../api/personApi'; import Button from '../components/common/Button'; import Checkbox from '../components/common/Checkbox'; import Dropdown from '../components/common/Dropdown'; import Input from '../components/common/Input'; import Navbar from '../components/common/Navbar'; -import { - emailPattern, - genderOptions, - personAttributes, - phoneNumberPattern -} from '../components/common/constants'; +import { genderOptions } from '../components/common/constants'; import ModalError from '../components/common/modals/ModalError'; import ModalSuccess from '../components/common/modals/ModalSuccess'; import './EditPerson.css'; @@ -31,62 +27,56 @@ class EditPerson extends Component { lastName: '', gender: '', birthdate: moment(), - birthdateEstimated: false, - organization: '', - email: '', - mobilePhone: '', - workPhone: '', - residencePhone: '', - otherPhone: '', - occupation: '' + birthdateEstimated: false }, showModal: false, isAPIError: false, isRequestError: false, isRequestLoading: false, lastUpdatedPerson: '', - attributes: { - organizationUuid: '', - emailUuid: '', - mobilePhoneUuid: '', - workPhoneUuid: '', - residencePhoneUuid: '', - otherPhoneUuid: '', - occupationUuid: '' - } + attributes: [], + attributesData: [] }; this.handleClearForm = this.handleClearForm.bind(this); } componentDidMount() { - this.setPersonAttributeIDs(); - this.loadPersonData(); - } + this.getAttributes().then(response => { + const attributes = response.config.personAttributesForRelations.map( + async attribute => { + const uuid = await getPersonAttributeTypeUuid( + attribute.attributeName + ); + return { + ...attribute, + value: '', + uuid: uuid + }; + } + ); - setPersonAttributeIDs = async () => { - this.setState({ - attributes: { - organizationUuid: await getPersonAttributeTypeUuid( - personAttributes.organization - ), - emailUuid: await getPersonAttributeTypeUuid(personAttributes.email), - mobilePhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.mobilePhone - ), - workPhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.workPhone - ), - residencePhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.residencePhone - ), - otherPhoneUuid: await getPersonAttributeTypeUuid( - personAttributes.otherPhone - ), - occupationUuid: await getPersonAttributeTypeUuid( - personAttributes.occupation - ) - } + Promise.all(attributes) + .then(resolvedAttributes => { + this.setState({ + attributes: resolvedAttributes + }); + }) + .then(() => { + this.loadPersonData(); + }); }); + } + + getAttributes = async () => { + const response = await fetchPersonAttributeConfig(); + if (response.status === 200) { + return response.json(); + } else { + return Promise.reject({ + status: response.status, + statusText: response.statusText + }); + } }; loadPersonData = async () => { @@ -119,7 +109,10 @@ class EditPerson extends Component { birthdateEstimated: data.birthdateEstimated } }); - data.attributes && this.setPersonAttributeValues(data.attributes); + if (data.attributes != []) { + this.setState({ attributesData: data.attributes }); + this.setPersonAttributeValues(data.attributes); + } }); }; @@ -138,73 +131,16 @@ class EditPerson extends Component { setPersonAttributeValues = attributes => { attributes.forEach(attribute => { - switch (attribute.display.split(' = ')[0]) { - case personAttributes.organization: { - this.setState({ - person: { - ...this.state.person, - organization: attribute.display.split(' = ')[1] - } - }); - break; - } - case personAttributes.email: { - this.setState({ - person: { - ...this.state.person, - email: attribute.display.split(' = ')[1] - } - }); - break; - } - case personAttributes.mobilePhone: { - this.setState({ - person: { - ...this.state.person, - mobilePhone: attribute.display.split(' = ')[1] - } - }); - break; - } - case personAttributes.workPhone: { - this.setState({ - person: { - ...this.state.person, - workPhone: attribute.display.split(' = ')[1] - } - }); - break; - } - case personAttributes.residencePhone: { - this.setState({ - person: { - ...this.state.person, - residencePhone: attribute.display.split(' = ')[1] - } - }); - break; - } - case personAttributes.otherPhone: { - this.setState({ - person: { - ...this.state.person, - otherPhone: attribute.display.split(' = ')[1] - } - }); - break; - } - case personAttributes.occupation: { - this.setState({ - person: { - ...this.state.person, - occupation: attribute.display.split(' = ')[1] - } - }); - break; - } - default: - break; - } + const attributeName = attribute.display.split(' = ')[0]; + const attributeValue = attribute.display.split(' = ')[1]; + this.setState(prevState => ({ + attributes: prevState.attributes.map(stateAttribute => { + if (stateAttribute.attributeName === attributeName) { + return { ...stateAttribute, value: attributeValue }; + } + return stateAttribute; + }) + })); }); }; @@ -234,16 +170,8 @@ class EditPerson extends Component { }; handleClearForm() { + this.setPersonAttributeValues(this.state.attributesData); this.setState({ - person: { - organization: '', - email: '', - mobilePhone: '', - workPhone: '', - residencePhone: '', - otherPhone: '', - occupation: '' - }, isRequestError: false }); } @@ -258,17 +186,20 @@ class EditPerson extends Component { middleName, lastName, gender, - organization, - email, - mobilePhone, - workPhone, - residencePhone, - otherPhone, - occupation, birthdate, birthdateEstimated } = this.state.person; + const attributes = this.state.attributes.map(attribute => { + return { + attributeType: { + uuid: attribute.uuid + }, + voided: this.isVoided(attribute.value), + value: !this.isVoided(attribute.value) ? attribute.value : null + }; + }); + const formPayload = { names: [ { @@ -279,68 +210,11 @@ class EditPerson extends Component { ], gender, birthdateEstimated, - attributes: [ - { - attributeType: { - uuid: this.state.attributes.organizationUuid - }, - voided: this.isVoided(organization), - value: organization - }, - { - attributeType: { - uuid: this.state.attributes.emailUuid - }, - voided: this.isVoided(email), - value: email - }, - { - attributeType: { - uuid: this.state.attributes.mobilePhoneUuid - }, - voided: this.isVoided(mobilePhone), - value: mobilePhone - }, - { - attributeType: { - uuid: this.state.attributes.workPhoneUuid - }, - voided: this.isVoided(workPhone), - value: workPhone - }, - { - attributeType: { - uuid: this.state.attributes.residencePhoneUuid - }, - voided: this.isVoided(residencePhone), - value: residencePhone - }, - { - attributeType: { - uuid: this.state.attributes.otherPhoneUuid - }, - voided: this.isVoided(otherPhone), - value: otherPhone - }, - { - attributeType: { - uuid: this.state.attributes.occupationUuid - }, - voided: this.isVoided(occupation), - value: occupation - } - ] + attributes: attributes }; if (this.isVoided(birthdate)) { formPayload.birthdate = birthdate + 'T12:00:00.000+0000'; } - this.isVoided(organization) && delete formPayload.attributes[0].value; - this.isVoided(email) && delete formPayload.attributes[1].value; - this.isVoided(mobilePhone) && delete formPayload.attributes[2].value; - this.isVoided(workPhone) && delete formPayload.attributes[3].value; - this.isVoided(residencePhone) && delete formPayload.attributes[4].value; - this.isVoided(otherPhone) && delete formPayload.attributes[5].value; - this.isVoided(occupation) && delete formPayload.attributes[6].value; return formPayload; }; @@ -350,6 +224,15 @@ class EditPerson extends Component { this.updateRequest(payload); }; + handleOtherAttributesChange = ({ target: input }) => { + const attributes = [...this.state.attributes]; + const index = attributes.findIndex( + attribute => attribute.name === input.name + ); + attributes[index].value = input.value; + this.setState({ attributes }); + }; + updateRequest(formPayload) { const { firstName, lastName } = this.state.person; this.setState({ @@ -412,13 +295,6 @@ class EditPerson extends Component { middleName, lastName, gender, - organization, - email, - mobilePhone, - workPhone, - residencePhone, - otherPhone, - occupation, birthdate, birthdateEstimated } = this.state.person; @@ -430,6 +306,8 @@ class EditPerson extends Component { lastUpdatedPerson: lastCreatedPerson } = this.state; + const personAttributes = this.state.attributes; + const isEnabled = !isRequestLoading; let modal = null; @@ -582,123 +460,54 @@ class EditPerson extends Component { -
-
-
- Other Information + {personAttributes.length > 0 && ( +
+
+
+
+ Other Information + {personAttributes.map(attribute => { + return ( +
+
+ +
+
+ ); + })} +
+
+
- -
-
- -
-
- -
-
-
- -
-
- -
-
-
-
-
-
-
-
-
-
-
-
+ )} {modal} From 6af5cf08566cfafcf3fadfb597bfc7033cbe1a81 Mon Sep 17 00:00:00 2001 From: Arjun G <91885483+Arjun-Go@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:23:34 +0530 Subject: [PATCH 21/22] add. pre commit hook and update tests (#157) --- .git-hooks/pre-commit | 5 ++ .github/workflows/validate_PR.yaml | 24 ++++++++ src/containers/CreatePerson.test.js | 40 ++++++++++++- src/containers/EditPerson.test.js | 90 ++++++++++++++++++++++++----- 4 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 .git-hooks/pre-commit create mode 100644 .github/workflows/validate_PR.yaml diff --git a/.git-hooks/pre-commit b/.git-hooks/pre-commit new file mode 100644 index 0000000..3a5f413 --- /dev/null +++ b/.git-hooks/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh + +yarn lint + +yarn test \ No newline at end of file diff --git a/.github/workflows/validate_PR.yaml b/.github/workflows/validate_PR.yaml new file mode 100644 index 0000000..0f2fd27 --- /dev/null +++ b/.github/workflows/validate_PR.yaml @@ -0,0 +1,24 @@ +name: Workflow to validate PRs + +on: + pull_request: + branches: [master] + +jobs: + build-release: + name: Build the web app and make a zip release + runs-on: ubuntu-latest + steps: + - name: Checkout to repository + uses: actions/checkout@v3 + + - name: Setup Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: "16.x" + + - name: Install dependencies + run: yarn install + + - name: Run tests + run: yarn test diff --git a/src/containers/CreatePerson.test.js b/src/containers/CreatePerson.test.js index 571f43a..609dccd 100644 --- a/src/containers/CreatePerson.test.js +++ b/src/containers/CreatePerson.test.js @@ -5,6 +5,44 @@ import Input from '../components/common/Input'; import SelectFromList from '../components/common/Dropdown'; import moment from 'moment'; +jest.mock('../api/personApi', () => { + return { + fetchPersonAttributeConfig: jest.fn(() => + Promise.resolve({ + status: 200, + json: jest.fn(() => + Promise.resolve({ + config: { + personAttributesForRelations: [ + { + name: 'occupation', + attributeName: 'occupationNew', + text: 'Occupation' + } + ] + } + }) + ) + }) + ), + getPersonAttributeTypeUuid: jest.fn(() => + Promise.resolve({ + status: 200, + json: jest.fn(() => + Promise.resolve({ + results: [ + { + uuid: '8d871d18-c2cc-11de-8d13-0010c6dffd0f', + display: 'Occupation' + } + ] + }) + ) + }) + ) + }; +}); + describe('CreatePerson', () => { let wrapper; @@ -13,7 +51,7 @@ describe('CreatePerson', () => { }); it('renders fourteen components', () => { - expect(wrapper.find(Input).length).toEqual(14); + expect(wrapper.find(Input).length).toEqual(7); }); it('renders one component', () => { diff --git a/src/containers/EditPerson.test.js b/src/containers/EditPerson.test.js index 03f333e..ab42e58 100644 --- a/src/containers/EditPerson.test.js +++ b/src/containers/EditPerson.test.js @@ -1,9 +1,66 @@ import React from 'react'; -import { shallow, render as mount } from 'enzyme'; +import { shallow } from 'enzyme'; import EditPerson from './EditPerson'; import Input from '../components/common/Input'; import SelectFromList from '../components/common/Dropdown'; +jest.mock('../api/personApi', () => { + return { + fetchPerson: jest.fn(() => { + return Promise.resolve({ + status: 200, + json: jest.fn(() => + Promise.resolve({ + display: 'John Doe', + uuid: '819ba69e-6c13-4803-8a14-d7abfac66e99', + gender: 'M', + birthdate: '1990-01-01', + birthdateEstimated: false, + attributes: [ + { + display: 'occupation = Teacher' + } + ] + }) + ) + }); + }), + fetchPersonAttributeConfig: jest.fn(() => + Promise.resolve({ + status: 200, + json: jest.fn(() => + Promise.resolve({ + config: { + personAttributesForRelations: [ + { + name: 'occupation', + attributeName: 'occupationNew', + text: 'Occupation' + } + ] + } + }) + ) + }) + ), + getPersonAttributeTypeUuid: jest.fn(() => + Promise.resolve({ + status: 200, + json: jest.fn(() => + Promise.resolve({ + results: [ + { + uuid: '8d871d18-c2cc-11de-8d13-0010c6dffd0f', + display: 'Occupation' + } + ] + }) + ) + }) + ) + }; +}); + describe('EditPerson', () => { let wrapper; @@ -16,7 +73,7 @@ describe('EditPerson', () => { }); it('renders fourteen components', () => { - expect(wrapper.find(Input).length).toEqual(14); + expect(wrapper.find(Input).length).toEqual(7); }); it('renders one component', () => { @@ -39,23 +96,28 @@ describe('EditPerson', () => { expect(wrapper.find('#lastName').prop('disabled')).toBe(true); }); - it('renders email input', () => { - expect(wrapper.find('#email').length).toEqual(1); + it('renders occupation input', () => { + setTimeout(() => { + wrapper.update(); + expect(wrapper.find('#occupation').length).toEqual(1); + done(); + }, 1000); }); describe('the user populates email input', () => { - const exampleEmail = 'Max@gmail.com'; + const exampleOccupation = 'Teacher'; let firstNameInput; - beforeEach(() => { - firstNameInput = wrapper.find('#email'); - firstNameInput.simulate('change', { - target: { value: exampleEmail, name: 'email' } - }); - }); - it('should update the state property email', () => { - expect(wrapper.state().person.email).toEqual(exampleEmail); + setTimeout(() => { + wrapper.update(); + firstNameInput = wrapper.find('#occupation'); + firstNameInput.simulate('change', { + target: { value: exampleOccupation, name: 'occupation' } + }); + expect(wrapper.state().attributes[0].value).toEqual(exampleOccupation); + done(); + }, 1000); }); }); -}); // end of outer describe +}); From e82138b0f85abe811a4f82145dc20d275c5953ed Mon Sep 17 00:00:00 2001 From: Arjun G <91885483+Arjun-Go@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:36:10 +0530 Subject: [PATCH 22/22] Arjun | Fix linting issues showing as warnings (#158) --- src/containers/EditPerson.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/containers/EditPerson.js b/src/containers/EditPerson.js index 6af44cd..ee918eb 100644 --- a/src/containers/EditPerson.js +++ b/src/containers/EditPerson.js @@ -109,6 +109,7 @@ class EditPerson extends Component { birthdateEstimated: data.birthdateEstimated } }); + // eslint-disable-next-line if (data.attributes != []) { this.setState({ attributesData: data.attributes }); this.setPersonAttributeValues(data.attributes);