diff --git a/src/components/edit/trackedEntity/TrackedEntityDialog.jsx b/src/components/edit/trackedEntity/TrackedEntityDialog.jsx index 754b3644b..056996639 100644 --- a/src/components/edit/trackedEntity/TrackedEntityDialog.jsx +++ b/src/components/edit/trackedEntity/TrackedEntityDialog.jsx @@ -1,16 +1,14 @@ import i18n from '@dhis2/d2-i18n' import { NoticeBox, IconErrorFilled24 } from '@dhis2/ui' import cx from 'classnames' -import PropTypes from 'prop-types' -import React, { Component, Fragment } from 'react' -import { connect } from 'react-redux' +import React, { useState, useEffect, useCallback, Fragment } from 'react' +import { useSelector, useDispatch } from 'react-redux' import { setTrackedEntityType, setProgram, setProgramStatus, setFollowUpStatus, setTrackedEntityRelationshipType, - setTrackedEntityRelationshipOutsideProgram, setPeriodType, setStartDate, setEndDate, @@ -20,7 +18,6 @@ import { setRelatedPointColor, setRelatedPointRadius, setRelationshipLineColor, - setStyleDataItem, } from '../../../actions/layerEdit.js' import { TEI_COLOR, @@ -53,422 +50,381 @@ import PeriodTypeSelect from './PeriodTypeSelect.jsx' import ProgramStatusSelect from './ProgramStatusSelect.jsx' import TrackedEntityRelationshipTypeSelect from './TrackedEntityRelationshipTypeSelect.jsx' -class TrackedEntityDialog extends Component { - static propTypes = { - setEndDate: PropTypes.func.isRequired, - setEventPointColor: PropTypes.func.isRequired, - setEventPointRadius: PropTypes.func.isRequired, - setFollowUpStatus: PropTypes.func.isRequired, - setOrgUnits: PropTypes.func.isRequired, - setPeriodType: PropTypes.func.isRequired, - setProgram: PropTypes.func.isRequired, - setProgramStatus: PropTypes.func.isRequired, - setRelatedPointColor: PropTypes.func.isRequired, - setRelatedPointRadius: PropTypes.func.isRequired, - setRelationshipLineColor: PropTypes.func.isRequired, - setStartDate: PropTypes.func.isRequired, - setTrackedEntityRelationshipType: PropTypes.func.isRequired, - setTrackedEntityType: PropTypes.func.isRequired, - validateLayer: PropTypes.bool.isRequired, - onLayerValidation: PropTypes.func.isRequired, - endDate: PropTypes.string, - eventPointColor: PropTypes.string, - eventPointRadius: PropTypes.number, - followUp: PropTypes.bool, - orgUnits: PropTypes.object, - periodType: PropTypes.string, - periodsSettings: PropTypes.object, - program: PropTypes.object, - programStatus: PropTypes.string, - relatedPointColor: PropTypes.string, - relatedPointRadius: PropTypes.number, - relationshipLineColor: PropTypes.string, - relationshipType: PropTypes.string, - rows: PropTypes.array, - startDate: PropTypes.string, - trackedEntityType: PropTypes.object, - } +const TrackedEntityDialog = () => { + const dispatch = useDispatch() - constructor(props, context) { - super(props, context) - this.state = { - tab: 'data', - showRelationshipsChecked: false, - } - } + // -------------------------- + // Redux state + // -------------------------- + const { + startDate, + endDate, + trackedEntityType, + program, + programStatus, + followUp, + relationshipType, + periodType, + rows, + orgUnits, + eventPointColor, + eventPointRadius, + relatedPointColor, + relatedPointRadius, + relationshipLineColor, + periodsSettings, + validateLayer, + onLayerValidation, + } = useSelector((state) => ({ + startDate: state.layerEdit.startDate, + endDate: state.layerEdit.endDate, + trackedEntityType: state.layerEdit.trackedEntityType, + program: state.layerEdit.program, + programStatus: state.layerEdit.programStatus, + followUp: state.layerEdit.followUp, + relationshipType: state.layerEdit.relationshipType, + periodType: state.layerEdit.periodType, + rows: state.layerEdit.rows, + orgUnits: state.layerEdit.orgUnits, + eventPointColor: state.layerEdit.eventPointColor, + eventPointRadius: state.layerEdit.eventPointRadius, + relatedPointColor: state.layerEdit.relatedPointColor, + relatedPointRadius: state.layerEdit.relatedPointRadius, + relationshipLineColor: state.layerEdit.relationshipLineColor, + periodsSettings: state.layerEdit.periodsSettings, + validateLayer: state.layerEdit.validateLayer, + onLayerValidation: state.layerEdit.onLayerValidation, + })) - componentDidMount() { - const { - rows, - startDate, - endDate, - relationshipType, - orgUnits, - setStartDate, - setEndDate, - setOrgUnits, - } = this.props + // -------------------------- + // Local state + // -------------------------- + const [tab, setTab] = useState('data') + const [showRelationshipsChecked, setShowRelationshipsChecked] = + useState(false) + const [trackedEntityTypeError, setTrackedEntityTypeError] = useState(null) + const [orgUnitsError, setOrgUnitsError] = useState(null) + const [periodError, setPeriodError] = useState(null) + // -------------------------- + // Lifecycle: componentDidMount + // -------------------------- + useEffect(() => { const hasDate = startDate !== undefined && endDate !== undefined - // Set default period (last year) + // Set default period if missing if (!hasDate) { const defaultDates = getDefaultDatesInCalendar() - setStartDate(defaultDates.startDate) - setEndDate(defaultDates.endDate) + dispatch(setStartDate(defaultDates.startDate)) + dispatch(setEndDate(defaultDates.endDate)) } if (relationshipType) { - this.setState({ - showRelationshipsChecked: true, - }) + setShowRelationshipsChecked(true) } - // Set org unit tree roots as default - if (!rows && orgUnits.roots) { - setOrgUnits({ - dimension: 'ou', - items: orgUnits.roots, - }) + // Set default org units if missing + if (!rows && orgUnits?.roots) { + dispatch(setOrgUnits({ dimension: 'ou', items: orgUnits.roots })) } - } + }, [dispatch, endDate, orgUnits?.roots, relationshipType, rows, startDate]) - componentDidUpdate(prev) { - const { - validateLayer, - onLayerValidation, - startDate, - endDate, - program, - setPeriodType, - } = this.props - const { periodError } = this.state + // -------------------------- + // Lifecycle: componentDidUpdate + // -------------------------- + useEffect(() => { + if (periodError && startDate && endDate) { + setPeriodError(null) + } + }, [periodError, startDate, endDate]) - if (validateLayer && validateLayer !== prev.validateLayer) { - onLayerValidation(this.validate()) + useEffect(() => { + if (!program) { + dispatch(setPeriodType(LAST_UPDATED_DATES)) } + }, [program, dispatch]) - if ( - periodError && - (startDate !== prev.startDate || endDate !== prev.endDate) - ) { - this.setErrorState('periodError', null, 'period') + // -------------------------- + // Validation + // -------------------------- + const setErrorState = useCallback((key, message, tabName) => { + switch (key) { + case 'trackedEntityTypeError': + setTrackedEntityTypeError(message) + break + case 'orgUnitsError': + setOrgUnitsError(message) + break + case 'periodError': + setPeriodError(message) + break + } + setTab(tabName) + return false + }, []) + + const validate = useCallback(() => { + if (!trackedEntityType) { + return setErrorState( + 'trackedEntityTypeError', + i18n.t('Tracked Entity Type is required'), + 'data' + ) + } + + const error = getStartEndDateError(startDate, endDate) + if (error) { + return setErrorState('periodError', error, 'period') } - if (!program && prev.program !== program) { - setPeriodType(LAST_UPDATED_DATES) + if (!getOrgUnitsFromRows(rows).length) { + return setErrorState( + 'orgUnitsError', + i18n.t('No organisation units are selected.'), + 'orgunits' + ) } - } - render() { - const { - eventPointColor, - eventPointRadius, - followUp, - periodType, - program, - programStatus, - trackedEntityType, - relationshipType, - relatedPointColor, - relatedPointRadius, - relationshipLineColor, - periodsSettings, - } = this.props + return true + }, [trackedEntityType, startDate, endDate, rows, setErrorState]) - const { - setTrackedEntityType, - setPeriodType, - setProgram, - setProgramStatus, - setFollowUpStatus, - setTrackedEntityRelationshipType, - setEventPointColor, - setEventPointRadius, - setRelatedPointColor, - setRelatedPointRadius, - setRelationshipLineColor, - } = this.props + // -------------------------- + // Lifecycle: componentDidUpdate + // -------------------------- + useEffect(() => { + if (validateLayer) { + onLayerValidation(validate()) + } + }, [validateLayer, validate, onLayerValidation]) - const { tab, trackedEntityTypeError, orgUnitsError, periodError } = - this.state + // -------------------------- + // Render + // -------------------------- + return ( +
+ + {i18n.t('Data')} + {i18n.t('Relationships')} + {i18n.t('Period')} + {i18n.t('Org Units')} + {i18n.t('Style')} + - return ( -
- this.setState({ tab })}> - {i18n.t('Data')} - {i18n.t('Relationships')} - {i18n.t('Period')} - {i18n.t('Org Units')} - {i18n.t('Style')} - -
- {tab === 'data' && ( -
- + {tab === 'data' && ( +
+ + dispatch(setTrackedEntityType(val)) + } + className={styles.select} + errorText={trackedEntityTypeError} + /> + {trackedEntityType && ( + dispatch(setProgram(val))} className={styles.select} - errorText={trackedEntityTypeError} /> - {trackedEntityType && ( - - )} - {program && ( - - )} - {program && ( - + )} + {program && ( + + dispatch(setProgramStatus(val)) + } + className={styles.select} + /> + )} + {program && ( + + dispatch(setFollowUpStatus(val)) + } + /> + )} +
+ )} + + {tab === 'relationships' && + (!trackedEntityType ? ( +
+ {i18n.t( + 'Please select a Tracked Entity Type before selecting a Relationship Type' )}
- )} - {tab === 'relationships' && - (!this.props.trackedEntityType ? ( -
- {i18n.t( - 'Please select a Tracked Entity Type before selecting a Relationship Type' - )} -
- ) : ( -
-
- - {i18n.t( - 'Displaying tracked entity relationships in Maps is an experimental feature' - )} - -
- +
+ + {i18n.t( + 'Displaying tracked entity relationships in Maps is an experimental feature' )} - checked={ - this.state.showRelationshipsChecked - } - onChange={(checked) => { - if (!checked) { + +
+ { + if (!checked) { + dispatch( setTrackedEntityRelationshipType( null ) - } - this.setState({ - showRelationshipsChecked: checked, - }) - }} - style={{ - marginBottom: 0, - }} - /> - {this.state.showRelationshipsChecked && ( - - - - )} -
- ))} - {tab === 'period' && ( -
- - - {periodError && ( -
- - {periodError} -
+ {showRelationshipsChecked && ( + + dispatch( + setTrackedEntityRelationshipType( + val + ) + ) + } + className={cx(styles.select, styles.indent)} + /> )}
- )} - {tab === 'orgunits' && ( - - )} - {tab === 'style' && ( -
-
-
- {i18n.t('Tracked entity style')}: -
-
- - -
- + ))} - {relationshipType ? ( - -
- {i18n.t('Related entity style')}: -
-
- - - -
-
- ) : null} + {tab === 'period' && ( +
+ dispatch(setPeriodType(val))} + /> + + dispatch(setStartDate(val)) + } + onSelectEndDate={(val) => dispatch(setEndDate(val))} + periodsSettings={periodsSettings} + /> + {periodError && ( +
+ + {periodError}
-
-
- )} -
-
- ) - } - - // TODO: Add to parent class? - setErrorState(key, message, tab) { - this.setState({ - [key]: message, - tab, - }) + )} +
+ )} - return false - } - - validate() { - const { trackedEntityType, rows, startDate, endDate } = this.props - - if (!trackedEntityType) { - return this.setErrorState( - 'trackedEntityTypeError', - i18n.t('Tracked Entity Type is required'), - 'data' - ) - } - - const error = getStartEndDateError(startDate, endDate) - if (error) { - return this.setErrorState('periodError', error, 'period') - } + {tab === 'orgunits' && ( + + )} - if (!getOrgUnitsFromRows(rows).length) { - return this.setErrorState( - 'orgUnitsError', - i18n.t('No organisation units are selected.'), - 'orgunits' - ) - } + {tab === 'style' && ( +
+
+
+ {i18n.t('Tracked entity style')}: +
+
+ + dispatch(setEventPointColor(val)) + } + className={styles.flexInnerColumn} + /> + + dispatch(setEventPointRadius(val)) + } + className={styles.flexInnerColumn} + /> +
+ - return true - } + {relationshipType && ( + +
+ {i18n.t('Related entity style')}: +
+
+ + dispatch( + setRelatedPointColor(val) + ) + } + className={styles.flexInnerColumn} + /> + + dispatch( + setRelatedPointRadius(val) + ) + } + className={styles.flexInnerColumn} + /> + + dispatch( + setRelationshipLineColor( + val + ) + ) + } + className={styles.flexInnerColumn} + /> +
+
+ )} +
+
+ )} +
+
+ ) } -export default connect( - null, - { - setTrackedEntityType, - setProgram, - setProgramStatus, - setFollowUpStatus, - setTrackedEntityRelationshipType, - setTrackedEntityRelationshipOutsideProgram, - setPeriodType, - setStartDate, - setEndDate, - setOrgUnits, - setEventPointColor, - setEventPointRadius, - setRelatedPointColor, - setRelatedPointRadius, - setRelationshipLineColor, - setStyleDataItem, - }, - null, - { - forwardRef: true, - } -)(TrackedEntityDialog) +export default TrackedEntityDialog