Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [DHIS2-16123] Add inheritable TEAs to Relationships #3464

Merged
merged 5 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { RenderFoundation } from '../../../../metaData';
import { convertClientToForm, convertServerToClient } from '../../../../converters';
import { subValueGetterByElementType } from './getSubValueForTei';
import type { QuerySingleResource } from '../../../../utils/api/api.types';
import { dataElementTypes } from '../../../../metaData';

type InputProgramData = {
attributes: Array<{
Expand All @@ -32,7 +33,7 @@ export type InputAttribute = {
displayName: string,
lastUpdated: string,
value: string,
valueType: string,
valueType: $Keys<typeof dataElementTypes>,
};

type InputForm = {
Expand All @@ -50,7 +51,7 @@ type StaticPatternValues = {

const useClientAttributesWithSubvalues = (program: InputProgramData, attributes: Array<InputAttribute>) => {
const dataEngine = useDataEngine();
const [listAttributes, setListAttributes] = useState([]);
const [listAttributes, setListAttributes] = useState(null);

const getListAttributes = useCallback(async () => {
if (program && attributes) {
Expand Down Expand Up @@ -139,8 +140,6 @@ export const useFormValues = ({ program, trackedEntityInstanceAttributes, orgUni
const formValuesReadyRef = useRef<any>(false);
const [formValues, setFormValues] = useState<any>({});
const [clientValues, setClientValues] = useState<any>({});
const areAttributesWithSubvaluesReady =
(teiId && clientAttributesWithSubvalues.length > 0) || (!teiId && clientAttributesWithSubvalues.length === 0);

useEffect(() => {
formValuesReadyRef.current = false;
Expand All @@ -152,7 +151,7 @@ export const useFormValues = ({ program, trackedEntityInstanceAttributes, orgUni
formFoundation &&
Object.entries(formFoundation).length > 0 &&
formValuesReadyRef.current === false &&
areAttributesWithSubvaluesReady
!!clientAttributesWithSubvalues
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! NIT: Can omit !!

) {
const staticPatternValues = { orgUnitCode: orgUnit.code };
const querySingleResource = makeQuerySingleResource(dataEngine.query.bind(dataEngine));
Expand All @@ -172,7 +171,6 @@ export const useFormValues = ({ program, trackedEntityInstanceAttributes, orgUni
clientAttributesWithSubvalues,
formValuesReadyRef,
orgUnit,
areAttributesWithSubvaluesReady,
searchTerms,
dataEngine,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,37 @@ import { useFormValuesFromSearchTerms } from './hooks/useFormValuesFromSearchTer
import { dataEntryHasChanges } from '../../DataEntry/common/dataEntryHasChanges';
import { useMetadataForRegistrationForm } from '../common/TEIAndEnrollment/useMetadataForRegistrationForm';
import { useBuildTeiPayload } from './hooks/useBuildTeiPayload';
import type { InputAttribute } from '../EnrollmentRegistrationEntry/hooks/useFormValues';

const useInitialiseTeiRegistration = (selectedScopeId, dataEntryId, orgUnitId) => {
type Props = {
selectedScopeId: string,
dataEntryId: string,
orgUnitId: string,
inheritedAttributes: ?Array<InputAttribute>,
}
const useInitialiseTeiRegistration = ({
selectedScopeId,
dataEntryId,
orgUnitId,
inheritedAttributes,
}: Props) => {
const dispatch = useDispatch();
const { scopeType, trackedEntityName } = useScopeInfo(selectedScopeId);
const { formId, formFoundation } = useMetadataForRegistrationForm({ selectedScopeId });
const formValues = useFormValuesFromSearchTerms();
const formValues = useFormValuesFromSearchTerms({ inheritedAttributes });
const registrationFormReady = !!formId;

useEffect(() => {
if (registrationFormReady && scopeType === scopeTypes.TRACKED_ENTITY_TYPE) {
dispatch(
startNewTeiDataEntryInitialisation(
{ selectedOrgUnitId: orgUnitId, selectedScopeId, dataEntryId, formFoundation, formValues },
{
selectedOrgUnitId: orgUnitId,
selectedScopeId,
dataEntryId,
formFoundation,
formValues,
},
));
}
}, [
Expand All @@ -43,8 +61,20 @@ const useInitialiseTeiRegistration = (selectedScopeId, dataEntryId, orgUnitId) =
};


export const TeiRegistrationEntry: ComponentType<OwnProps> = ({ selectedScopeId, id, orgUnitId, onSave, ...rest }) => {
const { trackedEntityName } = useInitialiseTeiRegistration(selectedScopeId, id, orgUnitId);
export const TeiRegistrationEntry: ComponentType<OwnProps> = ({
selectedScopeId,
id,
orgUnitId,
onSave,
inheritedAttributes,
...rest
}) => {
const { trackedEntityName } = useInitialiseTeiRegistration({
selectedScopeId,
dataEntryId: id,
orgUnitId,
inheritedAttributes,
});
const ready = useSelector(({ dataEntries }) => (!!dataEntries[id]));
const dataEntry = useSelector(({ dataEntries }) => (dataEntries[id]));
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMess
import type {
TeiPayload,
} from '../../Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types';
import type { InputAttribute } from '../EnrollmentRegistrationEntry/hooks/useFormValues';

export type OwnProps = $ReadOnly<{|
id: string,
Expand All @@ -16,6 +17,7 @@ export type OwnProps = $ReadOnly<{|
onSave: (TeiPayload) => void,
duplicatesReviewPageSize: number,
isSavingInProgress?: boolean,
inheritedAttributes?: Array<InputAttribute>,
renderDuplicatesCardActions?: RenderCustomCardActions,
renderDuplicatesDialogActions?: (onCancel: () => void, onSave: (TeiPayload) => void) => Node,
ExistingUniqueValueDialogActions: ExistingUniqueValueDialogActionsComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
// @flow
import { useEffect, useState } from 'react';
import { useMemo } from 'react';
import { useSelector } from 'react-redux';
import { convertClientToForm } from '../../../../converters';
import type { InputAttribute } from '../../EnrollmentRegistrationEntry/hooks/useFormValues';

type Props = {
inheritedAttributes: ?Array<InputAttribute>,
};

export const useFormValuesFromSearchTerms = () => {
export const useFormValuesFromSearchTerms = ({ inheritedAttributes }: Props) => {
const searchTerms = useSelector(({ searchDomain }) => searchDomain.currentSearchInfo.currentSearchTerms);
const [formValues, setFormValues] = useState();
useEffect(() => {

return useMemo(() => {
superskip marked this conversation as resolved.
Show resolved Hide resolved
if (inheritedAttributes) {
return inheritedAttributes
?.reduce((acc, item) => {
acc[item.attribute] = convertClientToForm(item.value, item.valueType);
return acc;
}, {});
}
if (searchTerms) {
const searchFormValues = searchTerms
?.reduce((acc, item) => ({ ...acc, [item.id]: convertClientToForm(item.value, item.type) }), {});
setFormValues(searchFormValues);
return searchTerms
?.reduce((acc, item) => {
acc[item.id] = convertClientToForm(item.value, item.type);
return acc;
}, {});
}
}, [searchTerms]);

return formValues;
return null;
}, [inheritedAttributes, searchTerms]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const getStyles = () => ({

type OrgUnitValue = {
id: string,
name: string,
displayName: string,
path: string,
}

Expand Down Expand Up @@ -44,15 +44,15 @@ class SingleOrgUnitSelectFieldPlain extends React.Component<Props, State> {
const { classes } = this.props;
return (
<div className={classes.selectedOrgUnitContainer}>
<Chip onRemove={this.onDeselectOrgUnit}>{selectedOrgUnit.name}</Chip>
<Chip onRemove={this.onDeselectOrgUnit}>{selectedOrgUnit.displayName}</Chip>
</div>
);
}

onSelectOrgUnit = (orgUnit: Object) => {
this.props.onBlur({
id: orgUnit.id,
name: orgUnit.displayName,
superskip marked this conversation as resolved.
Show resolved Hide resolved
displayName: orgUnit.displayName,
path: orgUnit.path,
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import { typeof newPageStatuses } from './NewPage.constants';
import { dataElementTypes } from '../../../metaData';

type ProgramCategories = Array<{|name: string, id: string|}>

Expand All @@ -10,7 +11,7 @@ type InputAttribute = {
displayName: string,
lastUpdated: string,
value: string,
valueType: string,
valueType: $Keys<typeof dataElementTypes>,
};

export type ContainerProps = $ReadOnly<{|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// @flow

import { dataElementTypes } from '../../../../metaData';

type InputAttribute = {
attribute: string,
code: string,
created: string,
displayName: string,
lastUpdated: string,
value: string,
valueType: string,
valueType: $Keys<typeof dataElementTypes>,
};

export type OwnProps = $ReadOnly<{|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const NewEnrollmentRelationshipPlain =
theme,
onSave,
programId,
inheritedAttributes,
orgUnitId,
duplicatesReviewPageSize,
renderDuplicatesDialogActions,
Expand All @@ -35,6 +36,7 @@ const NewEnrollmentRelationshipPlain =
renderDuplicatesDialogActions={renderDuplicatesDialogActions}
renderDuplicatesCardActions={renderDuplicatesCardActions}
ExistingUniqueValueDialogActions={ExistingUniqueValueDialogActions}
trackedEntityInstanceAttributes={inheritedAttributes}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import type {
SaveForEnrollmentAndTeiRegistration,
ExistingUniqueValueDialogActionsComponent,
} from '../../../../../../DataEntries';
import type { InputAttribute } from '../../../../../../DataEntries/EnrollmentRegistrationEntry/hooks/useFormValues';

export type Props = {|
theme: Theme,
programId: string,
orgUnitId: string,
inheritedAttributes: Array<InputAttribute>,
enrollmentMetadata?: Enrollment,
onSave: SaveForEnrollmentAndTeiRegistration,
duplicatesReviewPageSize: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ type Props = {

export class RegisterTeiDataEntryComponent extends React.Component<Props> {
render() {
const { showDataEntry, programId, onSaveWithoutEnrollment, onSaveWithEnrollment, ...passOnProps } = this.props;
const {
showDataEntry,
programId,
onSaveWithoutEnrollment,
onSaveWithEnrollment,
...passOnProps
} = this.props;

if (!showDataEntry) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const RelationshipTrackedEntityInstancePlain =
theme,
onSave,
trackedEntityTypeId,
inheritedAttributes,
duplicatesReviewPageSize,
renderDuplicatesDialogActions,
renderDuplicatesCardActions,
Expand All @@ -36,6 +37,7 @@ const RelationshipTrackedEntityInstancePlain =
orgUnitId={orgUnitId}
teiRegistrationMetadata={teiRegistrationMetadata}
selectedScopeId={teiRegistrationMetadata.form.id}
inheritedAttributes={inheritedAttributes}
saveButtonText={i18n.t('Save new {{trackedEntityTypeName}} and link', {
trackedEntityTypeName: trackedEntityTypeNameLC, interpolation: { escapeValue: false },
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { RenderCustomCardActions } from '../../../../../../CardList';
import type {
ExistingUniqueValueDialogActionsComponent,
} from '../../../../../../DataEntries';
import type { InputAttribute } from '../../../../../../DataEntries/EnrollmentRegistrationEntry/hooks/useFormValues';

export type TeiPayload = {|
trackedEntity: string,
Expand All @@ -23,6 +24,7 @@ export type Props = {|
trackedEntityTypeId: string,
onSave: TeiPayload => void,
teiRegistrationMetadata?: TeiRegistration,
inheritedAttributes: Array<InputAttribute>,
duplicatesReviewPageSize: number,
renderDuplicatesCardActions?: RenderCustomCardActions,
renderDuplicatesDialogActions?: (onCancel: () => void, onSave: (TeiPayload) => void) => Node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const RegisterTeiPlain = ({
trackedEntityName,
trackedEntityTypeId,
selectedScopeId,
inheritedAttributes,
classes,
}: ComponentProps) => {
const { resultsPageSize } = useContext(ResultsPageSizeContext);
Expand Down Expand Up @@ -110,6 +111,7 @@ const RegisterTeiPlain = ({
renderDuplicatesDialogActions={renderDuplicatesDialogActions}
renderDuplicatesCardActions={renderDuplicatesCardActions}
ExistingUniqueValueDialogActions={ExistingUniqueValueDialogActions}
inheritedAttributes={inheritedAttributes}
/>
</div>
<DataEntryWidgetOutput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@ import { useSelector } from 'react-redux';
import { RegisterTeiComponent } from './RegisterTei.component';
import type { ContainerProps } from './RegisterTei.types';
import { useScopeInfo } from '../../../../../hooks';
import { useInheritedAttributeValues } from '../useInheritedAttributeValues';

export const RegisterTei = ({
onLink,
onSave,
onGetUnsavedAttributeValues,
teiId,
trackedEntityTypeId,
suggestedProgramId,
}: ContainerProps) => {
const dataEntryId = 'relationship';
const error = useSelector(({ newRelationshipRegisterTei }) => (newRelationshipRegisterTei.error));
const selectedScopeId = suggestedProgramId || trackedEntityTypeId;
const { trackedEntityName } = useScopeInfo(selectedScopeId);
const { inheritedAttributes, isLoading: isLoadingAttributes } = useInheritedAttributeValues({
teiId,
trackedEntityTypeId,
programId: suggestedProgramId,
});

if (isLoadingAttributes) {
return null;
}

return (
<RegisterTeiComponent
Expand All @@ -28,6 +39,7 @@ export const RegisterTei = ({
selectedScopeId={selectedScopeId}
error={error}
trackedEntityTypeId={trackedEntityTypeId}
inheritedAttributes={inheritedAttributes}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// @flow
import type { InputAttribute } from '../../../../DataEntries/EnrollmentRegistrationEntry/hooks/useFormValues';

export type SharedProps = {|
onLink: (teiId: string, values: Object) => void,
onGetUnsavedAttributeValues?: ?Function,
Expand All @@ -7,6 +9,7 @@ export type SharedProps = {|

export type ContainerProps = {|
suggestedProgramId: string,
teiId: string,
onSave: (teiPayload: Object) => void,
...SharedProps,
|};
Expand All @@ -16,6 +19,7 @@ export type ComponentProps = {|
error: string,
dataEntryId: string,
trackedEntityName: ?string,
inheritedAttributes: Array<InputAttribute>,
onSaveWithEnrollment: () => void,
onSaveWithoutEnrollment: () => void,
...SharedProps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const TrackedEntityRelationshipsWrapper = ({
suggestedProgramId={suggestedProgramId}
onLink={onLinkToTrackedEntityFromSearch}
onSave={onLinkToTrackedEntityFromRegistration}
teiId={teiId}
onGetUnsavedAttributeValues={() => console.log('get unsaved')}
trackedEntityTypeId={selectedTrackedEntityTypeId}
/>
Expand Down
Loading
Loading