Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

fix(patient): Input validation and error feedback #1977

Merged
merged 15 commits into from
Apr 12, 2020
Merged
2 changes: 1 addition & 1 deletion src/__tests__/patients/new/NewPatient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('New Patient', () => {

wrapper.update()
expect(wrapper.find(GeneralInformation).prop('errorMessage')).toMatch(
'patient.errors.patientGivenNameRequiredOnCreate',
'patient.errors.createPatientError',
)
expect(wrapper.update.isInvalid === true)
})
Expand Down
6 changes: 5 additions & 1 deletion src/components/input/DatePickerWithLabelFormGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ interface Props {
isEditable?: boolean
onChange?: (date: Date) => void
isRequired?: boolean
feedback?: string
isInvalid?: boolean
}

const DatePickerWithLabelFormGroup = (props: Props) => {
const { onChange, label, name, isEditable, value, isRequired } = props
const { onChange, label, name, isEditable, value, isRequired, feedback, isInvalid } = props
const id = `${name}DatePicker`
return (
<div className="form-group">
Expand All @@ -24,6 +26,8 @@ const DatePickerWithLabelFormGroup = (props: Props) => {
timeIntervals={30}
withPortal={false}
disabled={!isEditable}
feedback={feedback}
isInvalid={isInvalid}
onChange={(inputDate) => {
if (onChange) {
onChange(inputDate)
Expand Down
6 changes: 3 additions & 3 deletions src/locales/enUs/translations/patient/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export default {
patient: {
code: 'Code',
firstName: 'First Name',
lastName: 'Last Name',
suffix: 'Suffix',
Expand Down Expand Up @@ -85,9 +84,10 @@ export default {
private: 'Private',
},
errors: {
patientGivenNameRequiredOnCreate: 'Could not create new patient.',
patientGivenNameRequiredOnUpdate: 'Could not update patient.',
createPatientError: 'Could not create new patient.',
updatePatientError: 'Could not update patient.',
patientGivenNameFeedback: 'Given Name is required.',
patientDateOfBirthFeedback: 'Date of Birth can not be greater than today',
},
},
}
8 changes: 4 additions & 4 deletions src/locales/ptBr/translations/patient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ export default {
private: 'Particular',
},
errors: {
patientGivenNameRequiredOnCreate: 'Nome do Paciente é necessário.',
// todo Portuguese translation
patientGivenNameRequiredOnUpdate: '',
patientGivenNameFeedback: '',
createPatientError: 'Não foi possível criar um paciente.',
updatePatientError: 'Não foi possível atualizar o paciente',
patientGivenNameFeedback: 'Nome do Paciente é necessário.',
patientDateOfBirthFeedback: 'Data de Nascimento não pode ser maior que hoje.',
},
},
}
29 changes: 17 additions & 12 deletions src/patients/GeneralInformation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,28 @@ import TextInputWithLabelFormGroup from '../components/input/TextInputWithLabelF
import SelectWithLabelFormGroup from '../components/input/SelectWithLableFormGroup'
import DatePickerWithLabelFormGroup from '../components/input/DatePickerWithLabelFormGroup'

interface Feedback {
givenName: string
dateOfBirth: string
}

interface Invalids {
givenName: boolean
dateOfBirth: boolean
}

interface Props {
patient: Patient
isEditable?: boolean
errorMessage?: string
onFieldChange?: (key: string, value: string | boolean) => void
isInvalid?: boolean
patientGivenNameFeedback?: string
invalids?: Invalids
matteovivona marked this conversation as resolved.
Show resolved Hide resolved
feedback?: Feedback
}

const GeneralInformation = (props: Props) => {
const { t } = useTranslation()
const {
patient,
isEditable,
onFieldChange,
errorMessage,
isInvalid,
patientGivenNameFeedback,
} = props
const { patient, isEditable, onFieldChange, errorMessage, invalids, feedback } = props

const onSelectChange = (event: React.ChangeEvent<HTMLSelectElement>, fieldName: string) =>
onFieldChange && onFieldChange(fieldName, event.target.value)
Expand Down Expand Up @@ -81,8 +84,8 @@ const GeneralInformation = (props: Props) => {
onInputElementChange(event, 'givenName')
}}
isRequired
isInvalid={isInvalid}
feedback={patientGivenNameFeedback}
isInvalid={invalids?.givenName}
feedback={feedback?.givenName}
/>
</div>
<div className="col-md-4">
Expand Down Expand Up @@ -163,6 +166,8 @@ const GeneralInformation = (props: Props) => {
? new Date(patient.dateOfBirth)
: undefined
}
isInvalid={invalids?.dateOfBirth}
feedback={feedback?.dateOfBirth}
onChange={(date: Date) => {
onDateOfBirthChange(date)
}}
Expand Down
53 changes: 44 additions & 9 deletions src/patients/edit/EditPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { Spinner, Button, Toast } from '@hospitalrun/components'

import { parseISO } from 'date-fns'
import GeneralInformation from '../GeneralInformation'
import useTitle from '../../page-header/useTitle'
import Patient from '../../model/Patient'
Expand All @@ -27,8 +28,14 @@ const EditPatient = () => {

const [patient, setPatient] = useState({} as Patient)
const [errorMessage, setErrorMessage] = useState('')
const [isInvalid, setIsInvalid] = useState(false)
const [patientGivenNameFeedback, setPatientGivenNameFeedback] = useState('')
const [updateInvalids, setUpdateInvalids] = useState({
givenName: false,
dateOfBirth: false,
})
const [updateFeedback, setUpdateFeedback] = useState({
givenName: '',
dateOfBirth: '',
})
const { patient: reduxPatient, isLoading } = useSelector((state: RootState) => state.patient)

useTitle(
Expand Down Expand Up @@ -68,12 +75,40 @@ const EditPatient = () => {
)
}

const onSave = () => {
const validateInput = () => {
let inputIsValid = true

if (!patient.givenName) {
setErrorMessage(t('patient.errors.patientGivenNameRequiredOnUpdate'))
setIsInvalid(true)
setPatientGivenNameFeedback(t('patient.errors.patientGivenNameFeedback'))
} else {
inputIsValid = false
setErrorMessage(t('patient.errors.updatePatientError'))
setUpdateInvalids((prevState) => ({
...prevState,
givenName: true,
}))
setUpdateFeedback((prevState) => ({
...prevState,
givenName: t('patient.errors.patientGivenNameFeedback'),
}))
}
if (patient.dateOfBirth) {
if (parseISO(patient.dateOfBirth) > new Date(Date.now())) {
inputIsValid = false
setErrorMessage(t('patient.errors.updatePatientError'))
setUpdateInvalids((prevState) => ({
...prevState,
dateOfBirth: true,
}))
setUpdateFeedback((prevState) => ({
...prevState,
dateOfBirth: t('patient.errors.patientDateOfBirthFeedback'),
}))
}
}
return inputIsValid
}

const onSave = () => {
if (validateInput()) {
dispatch(
updatePatient(
{
Expand Down Expand Up @@ -104,8 +139,8 @@ const EditPatient = () => {
patient={patient}
onFieldChange={onFieldChange}
errorMessage={errorMessage}
isInvalid={isInvalid}
patientGivenNameFeedback={patientGivenNameFeedback}
invalids={updateInvalids}
feedback={updateFeedback}
/>
<div className="row float-right">
<div className="btn-group btn-group-lg">
Expand Down
54 changes: 45 additions & 9 deletions src/patients/new/NewPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import { Button, Toast } from '@hospitalrun/components'

import { parseISO } from 'date-fns'
import GeneralInformation from '../GeneralInformation'
import useTitle from '../../page-header/useTitle'
import Patient from '../../model/Patient'
Expand All @@ -23,8 +24,14 @@ const NewPatient = () => {

const [patient, setPatient] = useState({} as Patient)
const [errorMessage, setErrorMessage] = useState('')
const [isInvalid, setIsInvalid] = useState(false)
const [patientGivenNameFeedback, setPatientGivenNameFeedback] = useState('')
const [createInvalids, setCreateInvalids] = useState({
givenName: false,
dateOfBirth: false,
})
const [createFeedback, setCreateFeedback] = useState({
givenName: '',
dateOfBirth: '',
})

useTitle(t('patients.newPatient'))
useAddBreadcrumbs(breadcrumbs, true)
Expand All @@ -42,12 +49,40 @@ const NewPatient = () => {
)
}

const onSave = () => {
const validateInput = () => {
let inputIsValid = true

if (!patient.givenName) {
setErrorMessage(t('patient.errors.patientGivenNameRequiredOnCreate'))
setIsInvalid(true)
setPatientGivenNameFeedback(t('patient.errors.patientGivenNameFeedback'))
} else {
inputIsValid = false
setErrorMessage(t('patient.errors.createPatientError'))
setCreateInvalids((prevState) => ({
...prevState,
givenName: true,
}))
setCreateFeedback((prevState) => ({
...prevState,
givenName: t('patient.errors.patientGivenNameFeedback'),
}))
}
if (patient.dateOfBirth) {
if (parseISO(patient.dateOfBirth) > new Date(Date.now())) {
inputIsValid = false
setErrorMessage(t('patient.errors.createPatientError'))
setCreateInvalids((prevState) => ({
...prevState,
dateOfBirth: true,
}))
setCreateFeedback((prevState) => ({
...prevState,
dateOfBirth: t('patient.errors.patientDateOfBirthFeedback'),
}))
}
}
return inputIsValid
}

const onSave = () => {
if (validateInput()) {
dispatch(
createPatient(
{
Expand All @@ -65,6 +100,7 @@ const NewPatient = () => {
...patient,
[key]: value,
})
setErrorMessage('')
}

return (
Expand All @@ -74,8 +110,8 @@ const NewPatient = () => {
patient={patient}
onFieldChange={onFieldChange}
errorMessage={errorMessage}
isInvalid={isInvalid}
patientGivenNameFeedback={patientGivenNameFeedback}
invalids={createInvalids}
feedback={createFeedback}
/>
<div className="row float-right">
<div className="btn-group btn-group-lg mt-3">
Expand Down