diff --git a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js index afb0561e04..7fc5b5c587 100644 --- a/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js +++ b/src/core_modules/capture-core/components/DataEntries/Enrollment/EnrollmentDataEntry.component.js @@ -239,6 +239,7 @@ const getGeometrySettings = () => ({ dialogLabel: i18n.t('Area'), required: false, orientation: getOrientation(props.formHorizontal), + orgUnit: props.orgUnit, }); } @@ -249,6 +250,7 @@ const getGeometrySettings = () => ({ required: false, orientation: getOrientation(props.formHorizontal), shrinkDisabled: props.formHorizontal, + orgUnit: props.orgUnit, }); }, getPropName: () => 'geometry', @@ -371,7 +373,6 @@ class FinalEnrollmentDataEntry extends React.Component { } } - const AOCFieldBuilderHOC = withAOCFieldBuilder(getAOCSettingsFn())( withDataEntryFields( getCategoryOptionsSettingsFn(), @@ -431,7 +432,6 @@ export class EnrollmentDataEntryComponent extends React.Component ({ dialogLabel: i18n.t('Area'), required: false, orientation: getOrientation(props.formHorizontal), + orgUnit: props.orgUnit, }); } @@ -138,6 +139,7 @@ const getStageGeometrySettings = () => ({ required: false, orientation: getOrientation(props.formHorizontal), shrinkDisabled: props.formHorizontal, + orgUnit: props.orgUnit, }); }, getPropName: () => stageMainDataIds.GEOMETRY, diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js index d70da2b4ae..e01a9e4e95 100644 --- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js +++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/DataEntry.component.js @@ -231,6 +231,7 @@ const buildGeometrySettingsFn = () => ({ dialogLabel: i18n.t('Area'), required: false, orientation: getOrientation(props.formHorizontal), + orgUnit: props.orgUnit, }); } @@ -241,6 +242,7 @@ const buildGeometrySettingsFn = () => ({ required: false, orientation: getOrientation(props.formHorizontal), shrinkDisabled: props.formHorizontal, + orgUnit: props.orgUnit, }); }, getPropName: () => 'geometry', @@ -579,6 +581,7 @@ class NewEventDataEntry extends Component { fieldOptions={this.fieldOptions} dataEntrySections={this.dataEntrySections} relationshipsRef={this.setRelationshipsInstance} + orgUnit={orgUnit} {...passOnProps} /> diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/CoordinateField/CoordinateField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/CoordinateField/CoordinateField.component.js index 992b6b069d..5fd044cddd 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/CoordinateField/CoordinateField.component.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/CoordinateField/CoordinateField.component.js @@ -4,6 +4,7 @@ import withStyles from '@material-ui/core/styles/withStyles'; import { CoordinateField as UICoordinateField } from 'capture-ui'; import { Modal, ModalTitle } from '@dhis2/ui'; import { typeof orientations } from '../../../New'; +import { withCenterPoint } from '../../HOC'; const getStyles = (theme: Theme) => ({ inputWrapperFocused: { @@ -93,4 +94,4 @@ class CoordinateFieldPlain extends React.Component { } } -export const CoordinateField = withStyles(getStyles)(CoordinateFieldPlain); +export const CoordinateField = withStyles(getStyles)(withCenterPoint()(CoordinateFieldPlain)); diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/PolygonField/PolygonField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/PolygonField/PolygonField.component.js index 47831a9a47..6fe88545d2 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/PolygonField/PolygonField.component.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/PolygonField/PolygonField.component.js @@ -4,6 +4,7 @@ import withStyles from '@material-ui/core/styles/withStyles'; import { PolygonField as UIPolygonField } from 'capture-ui'; import { Modal, ModalTitle } from '@dhis2/ui'; import { typeof orientations } from '../../../New'; +import { withCenterPoint } from '../../HOC'; const getStyles = () => ({ dialogPaper: { @@ -57,4 +58,4 @@ class PolygonFieldPlain extends React.Component { } } -export const PolygonField = withStyles(getStyles)(PolygonFieldPlain); +export const PolygonField = withStyles(getStyles)(withCenterPoint()(PolygonFieldPlain)); diff --git a/src/core_modules/capture-core/components/FormFields/New/HOC/index.js b/src/core_modules/capture-core/components/FormFields/New/HOC/index.js new file mode 100644 index 0000000000..e92b2ccdf8 --- /dev/null +++ b/src/core_modules/capture-core/components/FormFields/New/HOC/index.js @@ -0,0 +1,2 @@ +// @flow +export { withCenterPoint } from './withCenterPoint'; diff --git a/src/core_modules/capture-core/components/FormFields/New/HOC/withCenterPoint.js b/src/core_modules/capture-core/components/FormFields/New/HOC/withCenterPoint.js new file mode 100644 index 0000000000..0ac9861d47 --- /dev/null +++ b/src/core_modules/capture-core/components/FormFields/New/HOC/withCenterPoint.js @@ -0,0 +1,56 @@ +// @flow +import React, { type ComponentType, useMemo, useState } from 'react'; +import { useApiMetadataQuery } from 'capture-core/utils/reactQueryHelpers'; + +const DEFAULT_CENTER = [51.505, -0.09]; + +const convertToClientCoordinates = ({ coordinates, type }: { coordinates: any[], type: string }) => { + switch (type) { + case 'Point': + return [coordinates[1], coordinates[0]]; + case 'Polygon': + return coordinates[0][0]; + default: + return DEFAULT_CENTER; + } +}; + +const getCenterPoint = (InnerComponent: ComponentType) => (props: Object) => { + const { orgUnit, ...passOnProps } = props; + const [orgUnitKey, setOrgUnitKey] = useState(orgUnit.id); + const [shouldFetch, setShouldFetch] = useState(false); + const queryKey = ['organisationUnit', 'geometry', orgUnitKey]; + const queryFn = { + resource: 'organisationUnits', + id: () => orgUnitKey, + params: { + fields: 'geometry,parent', + }, + }; + const queryOptions = useMemo( + () => ({ enabled: Boolean(orgUnit.id) && shouldFetch }), + [shouldFetch, orgUnit.id], + ); + const { data } = useApiMetadataQuery(queryKey, queryFn, queryOptions); + + const center = useMemo(() => { + if (data) { + const { geometry, parent } = data; + if (geometry) { + return convertToClientCoordinates(geometry); + } else if (parent?.id) { + setOrgUnitKey(parent.id); + } + return DEFAULT_CENTER; + } + return undefined; + }, [data]); + + const onOpenMap = (hasValue) => { + setShouldFetch(!hasValue); + }; + + return ; +}; + +export const withCenterPoint = () => (InnerComponent: ComponentType) => getCenterPoint(InnerComponent); diff --git a/src/core_modules/capture-core/components/FormFields/New/index.js b/src/core_modules/capture-core/components/FormFields/New/index.js index 96563951ac..d1e1f4a89a 100644 --- a/src/core_modules/capture-core/components/FormFields/New/index.js +++ b/src/core_modules/capture-core/components/FormFields/New/index.js @@ -33,6 +33,7 @@ export { withLabel } from './HOC/withLabel'; export { withStyledContainer } from './HOC/withStyledContainer'; export { withOptionsIconElement } from './HOC/withOptionsIconElement'; export { withFocusSaver, withInternalChangeHandler } from 'capture-ui'; +export { withCenterPoint } from './HOC/withCenterPoint'; // OrgUnit HOCs export { diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/Coordinates/Coordinates.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/Coordinates/Coordinates.component.js index cd9940c11a..90f60c51ce 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/Coordinates/Coordinates.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/Coordinates/Coordinates.component.js @@ -48,7 +48,7 @@ const CoordinatesPlain = ({ onSetCoordinates, }: CoordinatesProps) => { const [position, setPosition] = useState(defaultValues); - const [center, setCenter] = useState(initialCenter); + const [center, setCenter] = useState(); const [tempLatitude, setTempLatitude] = useState(position?.[0]); const [tempLongitude, setTempLongitude] = useState(position?.[1]); const [isEditing, setEditing] = useState(!defaultValues); @@ -92,7 +92,7 @@ const CoordinatesPlain = ({ const renderMap = () => ( { if (ref?.leafletElement) { diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/MapModal.container.js b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/MapModal.container.js index b39d141b1d..97513540fc 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/MapModal.container.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/MapModal.container.js @@ -3,17 +3,17 @@ import React, { useCallback } from 'react'; import { useGeometry } from '../hooks/useGeometry'; import type { MapModalProps } from './MapModal.types'; import { MapModal as MapModalComponent } from './MapModal.component'; - -const DEFAULT_CENTER = [51.505, -0.09]; +import { useCenterPoint } from './hooks'; export const MapModal = ({ enrollment, onUpdate, setOpenMap, defaultValues, - center, + center: storedCenter, }: MapModalProps) => { const { geometryType, dataElementType } = useGeometry(enrollment); + const { center } = useCenterPoint(enrollment.orgUnit, storedCenter); const onSetCoordinates = useCallback((coordinates) => { const geometry = coordinates ? { type: geometryType, coordinates } : null; @@ -22,7 +22,7 @@ export const MapModal = ({ return ( { const [polygonArea, setPolygonArea] = useState(defaultValues); - const [center, setCenter] = useState(initialCenter); + const [center, setCenter] = useState(); const [drawingState, setDrawingState] = useState(undefined); const prevDrawingState = useRef(undefined); @@ -91,7 +91,7 @@ const PolygonPlain = ({ const renderMap = () => ( { if (ref?.leafletElement) { diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/hooks/index.js b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/hooks/index.js new file mode 100644 index 0000000000..d1de65205f --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/hooks/index.js @@ -0,0 +1,2 @@ +// @flow +export { useCenterPoint } from './useCenterPoint'; diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/hooks/useCenterPoint.js b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/hooks/useCenterPoint.js new file mode 100644 index 0000000000..b74f3f08e5 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetEnrollment/MapModal/hooks/useCenterPoint.js @@ -0,0 +1,54 @@ +// @flow +import { useMemo, useState } from 'react'; +import { useApiMetadataQuery } from 'capture-core/utils/reactQueryHelpers'; + +const DEFAULT_CENTER = [51.505, -0.09]; + +const convertToClientCoordinates = ({ coordinates, type }: { coordinates: any[], type: string }) => { + switch (type) { + case 'Point': + return [coordinates[1], coordinates[0]]; + case 'Polygon': + return coordinates[0][0]; + default: + return DEFAULT_CENTER; + } +}; + +export const useCenterPoint = (orgUnitId: string, storedCenter: ?[number, number]) => { + const [orgUnitKey, setOrgUnitKey] = useState(orgUnitId); + const queryKey = ['organisationUnit', 'geometry', orgUnitKey]; + const queryFn = { + resource: 'organisationUnits', + id: () => orgUnitKey, + params: { + fields: 'geometry,parent', + }, + }; + const queryOptions = { enabled: !storedCenter && Boolean(orgUnitId) }; + const { data, isLoading } = useApiMetadataQuery(queryKey, queryFn, queryOptions); + + const center = useMemo(() => { + if (data) { + const { geometry, parent } = data; + if (geometry) { + return convertToClientCoordinates(geometry); + } else if (parent?.id) { + setOrgUnitKey(parent.id); + } + return DEFAULT_CENTER; + } + return undefined; + }, [data]); + + if (storedCenter) { + return { + center: storedCenter, + }; + } + + return { + center, + loading: isLoading, + }; +}; diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js index 812c92ec47..d871330dfb 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.component.js @@ -223,6 +223,7 @@ const buildGeometrySettingsFn = () => ({ dialogLabel: i18n.t('Area'), required: false, orientation: getOrientation(props.formHorizontal), + orgUnit: props.orgUnit, }); } @@ -233,6 +234,7 @@ const buildGeometrySettingsFn = () => ({ required: false, orientation: getOrientation(props.formHorizontal), shrinkDisabled: props.formHorizontal, + orgUnit: props.orgUnit, }); }, getPropName: () => 'geometry', diff --git a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.container.js b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.container.js index e45f3262d0..aa16f0c93b 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.container.js +++ b/src/core_modules/capture-core/components/WidgetEnrollmentEventNew/DataEntry/DataEntry.container.js @@ -69,6 +69,7 @@ export const DataEntry = ({ orgUnit, rulesExecutionDependenciesClientFormatted, return ( ({ label: i18n.t('Area'), dialogLabel: i18n.t('Area'), required: false, + orgUnit: props.orgUnit, }); } return createComponentProps(props, { @@ -260,6 +261,7 @@ const buildGeometrySettingsFn = () => ({ label: i18n.t('Coordinate'), dialogLabel: i18n.t('Coordinate'), required: false, + orgUnit: props.orgUnit, }); }, getPropName: () => 'geometry', @@ -519,6 +521,7 @@ class EditEventDataEntryPlain extends Component { onSaveAndCompleteEnrollment={onSaveAndCompleteEnrollment(orgUnit)} fieldOptions={this.fieldOptions} dataEntrySections={this.dataEntrySections} + orgUnit={orgUnit} programId={programId} selectedOrgUnitId={orgUnit?.id} {...passOnProps} diff --git a/src/core_modules/capture-core/components/WidgetProfile/DataEntry/DataEntry.component.js b/src/core_modules/capture-core/components/WidgetProfile/DataEntry/DataEntry.component.js index c3c6909ce9..b9de1f5590 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/DataEntry/DataEntry.component.js +++ b/src/core_modules/capture-core/components/WidgetProfile/DataEntry/DataEntry.component.js @@ -21,6 +21,7 @@ export const DataEntryComponent = ({ onGetValidationContext, errorsMessages, warningsMessages, + orgUnit, }: PlainProps) => ( {i18n.t(`Edit ${trackedEntityName}`)} @@ -36,6 +37,7 @@ export const DataEntryComponent = ({ onUpdateFormField={onUpdateFormField} onUpdateFormFieldAsync={onUpdateFormFieldAsync} onGetValidationContext={onGetValidationContext} + orgUnit={orgUnit} /> ) ); diff --git a/src/core_modules/capture-core/components/WidgetProfile/DataEntry/dataEntry.types.js b/src/core_modules/capture-core/components/WidgetProfile/DataEntry/dataEntry.types.js index d491c53f0e..b067cac252 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/DataEntry/dataEntry.types.js +++ b/src/core_modules/capture-core/components/WidgetProfile/DataEntry/dataEntry.types.js @@ -16,6 +16,8 @@ export type PlainProps = {| modalState: string, errorsMessages: Array<{ id: string, message: string }>, warningsMessages: Array<{ id: string, message: string }>, + center?: ?Array, + orgUnit: { id: string }, |}; export type Props = {| diff --git a/src/core_modules/capture-ui/CoordinateField/CoordinateField.component.js b/src/core_modules/capture-ui/CoordinateField/CoordinateField.component.js index 568cb0cedc..39bb978211 100644 --- a/src/core_modules/capture-ui/CoordinateField/CoordinateField.component.js +++ b/src/core_modules/capture-ui/CoordinateField/CoordinateField.component.js @@ -20,8 +20,9 @@ type Coordinate = { type Props = { onBlur: (value: any) => void, + onOpenMap: (hasValue: boolean) => void, orientation: $Values, - mapCenter: Array, + center?: ?Array, onChange?: ?(value: any) => void, value?: ?Coordinate, shrinkDisabled?: ?boolean, @@ -43,10 +44,6 @@ const coordinateKeys = { export class CoordinateField extends React.Component { mapInstance: ?any; - static defaultProps = { - mapCenter: [51.505, -0.09], - }; - constructor(props: Props) { super(props); @@ -96,6 +93,7 @@ export class CoordinateField extends React.Component { } openMap = () => { + this.props.onOpenMap(Boolean(this.props.value)); this.setState({ showMap: true, position: this.getPosition() }); } @@ -171,7 +169,7 @@ export class CoordinateField extends React.Component { renderMap = () => { const { position, zoom } = this.state; - const center = position || this.props.mapCenter; + const center = position || this.props.center; return (
{ ); renderLatitude = () => { - const { mapCenter, onBlur, onChange, value, orientation, shrinkDisabled, classes, mapDialog, disabled, ...passOnProps } = this.props; + const { center, onBlur, onChange, value, orientation, shrinkDisabled, classes, mapDialog, disabled, ...passOnProps } = this.props; const { mapIconContainer: mapIconContainerCustomClass, mapIcon: mapIconCustomClass, ...passOnClasses } = classes || {}; return ( // $FlowFixMe[cannot-spread-inexact] automated comment @@ -230,7 +228,7 @@ export class CoordinateField extends React.Component { } renderLongitude = () => { - const { mapCenter, onBlur, onChange, value, orientation, shrinkDisabled, classes, mapDialog, disabled, ...passOnProps } = this.props; + const { center, onBlur, onChange, value, orientation, shrinkDisabled, classes, mapDialog, disabled, ...passOnProps } = this.props; const { mapIconContainer: mapIconContainerCustomClass, mapIcon: mapIconCustomClass, ...passOnClasses } = classes || {}; return ( // $FlowFixMe[cannot-spread-inexact] automated comment diff --git a/src/core_modules/capture-ui/PolygonField/PolygonField.component.js b/src/core_modules/capture-ui/PolygonField/PolygonField.component.js index 7ef585a16c..0290a8613b 100644 --- a/src/core_modules/capture-ui/PolygonField/PolygonField.component.js +++ b/src/core_modules/capture-ui/PolygonField/PolygonField.component.js @@ -14,8 +14,9 @@ const WrappedLeafletSearch = withLeaflet(ReactLeafletSearch); type Props = { onBlur: (value: any) => void, + onOpenMap: (hasValue: boolean) => void, value?: ?any, - mapCenter: Array, + center?: ?Array, mapDialog?: ?React.Element, }; @@ -64,10 +65,6 @@ function coordsToFeatureCollection(coordinates): ?FeatureCollection { export class PolygonField extends React.Component { mapInstance: ?any; - static defaultProps = { - mapCenter: [51.505, -0.09], - } - constructor(props: Props) { super(props); @@ -107,7 +104,7 @@ export class PolygonField extends React.Component { getCenter = (featureCollection: ?FeatureCollection) => { if (!featureCollection) { - return this.props.mapCenter; + return this.props.center; } const coordinates = featureCollection.features[0].geometry.coordinates[0]; const { lat, lng } = L.latLngBounds(coordinates.map(c => ([c[0], c[1]]))).getCenter(); @@ -122,6 +119,7 @@ export class PolygonField extends React.Component { } openMap = () => { + this.props.onOpenMap(Boolean(this.props.value)); this.setState({ showMap: true, mapCoordinates: this.props.value }); }