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: [DHIS-11419] display assigned users on events in enrollment overview page #3453

Merged
merged 11 commits into from
Dec 18, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const query = {
id: ({ id }) => id,
params: {
fields:
['programStages[id,repeatable,hideDueDate,programStageDataElements[displayInReports,dataElement[id,valueType,displayName,displayFormName,optionSet[options[code,name]]]'],
['programStages[id,repeatable,hideDueDate,enableUserAssignment,programStageDataElements[displayInReports,dataElement[id,valueType,displayName,displayFormName,optionSet[options[code,name]]]'],
Copy link
Member

Choose a reason for hiding this comment

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

Do you know the reason why useProgramMetadata is retrieving data from the api instead of using the IndexedDB cache? If not, can you change it to use useIndexedDBQuery?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure, but it could perhaps be related to translations, i.e. displayName needs to come from the server?

Copy link
Member

Choose a reason for hiding this comment

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

We have a ticket to fix the displayName cache problem. Great if you change this one to use useIndexedDBQuery

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JoakimSM programStageDataElements in IndexedDB doesn't contain the dataelement subvalues like valueType and optionSet. Do you think we can solve that with local data as well?

Copy link
Member

Choose a reason for hiding this comment

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

If I understand you correctly, there is a separate store/table for data elements. Should be retrieved from there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed the metadata source in this case from the server to IndexedDB. The code got rather complicated the way I did it though. Got any suggestions for making it less complicated?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it looks too bad. But, what is the advantage of using your custom useQueryStyleEvaluation instead of useIndexedDBQuery like we do in useProgramFromIndexedDB?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's just how the caching is done. I think useIndexedDBQuery generally uses time to determine when to clear out a cache entry, which in this case doesn't feel very natural to me. useQueryStyleEvaluation does the caching like useMemo would.

Copy link
Member

Choose a reason for hiding this comment

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

It would be beneficial to have one approach for this and currently that is useIndexedDBQuery. If we don't like the approach there, we can look into what we don't like and change it for all queries to IndexedDB. But let's start by using useIndexedDBQuery.

},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const useProgramStages = (program: Program, programStages?: Array<apiProg
if (program && programStages) {
program.stages.forEach((item) => {
const { id, name, icon, stageForm } = item;
const { hideDueDate, programStageDataElements, repeatable } = programStages.find(p => p.id === id) || {};
const { hideDueDate, programStageDataElements, repeatable, enableUserAssignment } = programStages.find(p => p.id === id) || {};
if (!programStageDataElements) {
log.error(errorCreator(i18n.t('Program stage not found'))(id));
} else {
Expand All @@ -21,6 +21,7 @@ export const useProgramStages = (program: Program, programStages?: Array<apiProg
icon,
hideDueDate,
repeatable,
enableUserAssignment,
description: stageForm.description,
dataElements: programStageDataElements?.reduce((acc, currentStageData) => {
const { displayInReports, dataElement } = currentStageData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const hideProgramStage = (ruleEffects, stageId) => (

export const StagePlain = ({ stage, events, classes, className, onCreateNew, ruleEffects, ...passOnProps }: Props) => {
const [open, setOpenStatus] = useState(true);
const { id, name, icon, description, dataElements, hideDueDate, repeatable } = stage;
const { id, name, icon, description, dataElements, hideDueDate, repeatable, enableUserAssignment } = stage;
const hiddenProgramStage = hideProgramStage(ruleEffects, id);

return (
Expand All @@ -57,6 +57,7 @@ export const StagePlain = ({ stage, events, classes, className, onCreateNew, rul
dataElements={dataElements}
hideDueDate={hideDueDate}
repeatable={repeatable}
enableUserAssignment={enableUserAssignment}
onCreateNew={onCreateNew}
hiddenProgramStage={hiddenProgramStage}
{...passOnProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const StageDetailPlain = (props: Props) => {
dataElements,
hideDueDate = false,
repeatable = false,
enableUserAssignment = false,
onEventClick,
onViewAll,
onCreateNew,
Expand All @@ -76,7 +77,7 @@ const StageDetailPlain = (props: Props) => {
sortDirection: SORT_DIRECTION.DESC,
};
const { stage } = getProgramAndStageForProgram(programId, stageId);
const headerColumns = useComputeHeaderColumn(dataElements, hideDueDate, stage?.stageForm);
const headerColumns = useComputeHeaderColumn(dataElements, hideDueDate, enableUserAssignment, stage?.stageForm);
const { loading, value: dataSource, error } = useComputeDataFromEvent(dataElements, events);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ import { SORT_DIRECTION, MULIT_TEXT_WITH_NO_OPTIONS_SET } from './constants';
import { isNotValidOptionSet } from '../../../../../../utils/isNotValidOptionSet';
import { useOrgUnitNames } from '../../../../../../metadataRetrieval/orgUnitName';

const baseKeys = [{ id: 'status' }, { id: 'occurredAt' }, { id: 'orgUnitName' }, { id: 'scheduledAt' }, { id: 'comments' }];
const baseKeys = [{ id: 'status' }, { id: 'occurredAt' }, { id: 'assignedUser' }, { id: 'orgUnitName' }, { id: 'scheduledAt' }, { id: 'comments' }];
const basedFieldTypes = [
{ type: dataElementTypes.STATUS, resolveValue: convertStatusForView },
{ type: dataElementTypes.DATE },
{ type: 'ASSIGNEE' },
{ type: dataElementTypes.TEXT, resolveValue: convertOrgUnitForView },
{ type: dataElementTypes.DATE },
{ type: dataElementTypes.UNKNOWN, resolveValue: convertCommentForView },
];
const getBaseColumnHeaders = props => [
{ header: i18n.t('Status'), sortDirection: SORT_DIRECTION.DEFAULT, isPredefined: true },
{ header: props.formFoundation.getLabel('occurredAt'), sortDirection: SORT_DIRECTION.DEFAULT, isPredefined: true },
{ header: i18n.t('Assigned to'), sortDirection: SORT_DIRECTION.DEFAULT, isPredefined: true },
{ header: i18n.t('Registering unit'), sortDirection: SORT_DIRECTION.DEFAULT, isPredefined: true },
{ header: props.formFoundation.getLabel('scheduledAt'), sortDirection: SORT_DIRECTION.DEFAULT, isPredefined: true },
{ header: '', sortDirection: null, isPredefined: true },
Expand Down Expand Up @@ -118,7 +120,7 @@ const useComputeDataFromEvent = (dataElements: Array<StageDataElement>, events:
};


const useComputeHeaderColumn = (dataElements: Array<StageDataElement>, hideDueDate: boolean, formFoundation: Object) => {
const useComputeHeaderColumn = (dataElements: Array<StageDataElement>, hideDueDate: boolean, enableUserAssignment: boolean, formFoundation: Object) => {
const headerColumns = useMemo(() => {
const dataElementHeaders = dataElements.reduce((acc, currDataElement) => {
const { id, name, formName, type, optionSet } = currDataElement;
Expand All @@ -132,9 +134,10 @@ const useComputeHeaderColumn = (dataElements: Array<StageDataElement>, hideDueDa
return acc;
}, []);
return [
...getBaseColumns({ formFoundation }).filter(col => (hideDueDate ? col.id !== 'scheduledAt' : true)),
...getBaseColumns({ formFoundation })
.filter(col => (enableUserAssignment || col.id !== 'assignedUser') && (!hideDueDate || col.id !== 'scheduledAt')),
...dataElementHeaders];
}, [dataElements, hideDueDate, formFoundation]);
}, [dataElements, hideDueDate, enableUserAssignment, formFoundation]);

return headerColumns;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { StageDataElement, StageCommonProps } from '../../../types/common.t
eventName: string,
hideDueDate?: boolean,
repeatable?: boolean,
enableUserAssignment?: boolean,
stageId: string,
hiddenProgramStage?: boolean,
...CssClasses,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type Stage = {
description?: ?string,
icon?: Icon,
dataElements: Array<StageDataElement>,
enableUserAssignment: boolean,
hideDueDate?: boolean,
repeatable?: boolean
}
Expand Down
10 changes: 10 additions & 0 deletions src/core_modules/capture-core/converters/serverToClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ function convertTime(d2Value: string) {
return parseData.momentTime;
}

const convertAssignedUserToClient = (assignedUser?: ApiAssignedUser) =>
((assignedUser && assignedUser.uid) ? {
id: assignedUser.uid,
name: assignedUser.displayName,
username: assignedUser.username,
firstName: assignedUser.firstName,
surname: assignedUser.surname,
} : null);

const optionSetConvertersForType = {
[dataElementTypes.NUMBER]: parseNumber,
[dataElementTypes.INTEGER]: parseNumber,
Expand Down Expand Up @@ -51,6 +60,7 @@ const valueConvertersForType = {
return { latitude: arr[1], longitude: arr[0] };
},
[dataElementTypes.POLYGON]: () => 'Polygon',
[dataElementTypes.ASSIGNEE]: convertAssignedUserToClient,
};

export function convertValue(value: any, type: $Keys<typeof dataElementTypes>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @flow
import { convertClientToServer } from '../../converters';
import { convertClientToServer, convertAssigneeToServer } from '../../converters';
import { convertMainEvent } from './mainEventConverter';
import { dataElementTypes } from '../../metaData';
import { convertEventAttributeOptions } from '../convertEventAttributeOptions';
Expand All @@ -26,7 +26,7 @@ export function convertMainEventClientToServer(event: Object) {
convertedValue = convertClientToServer(value, dataElementTypes.DATE);
break;
case 'assignee':
convertedValue = value && ({ uid: value.id });
convertedValue = value && convertAssigneeToServer(value);
break;
default:
convertedValue = value;
Expand Down
Loading