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-17770] Org unit contextualization in self contained widgets #3720

Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
25a3c5a
feat: org unit context in Stages and Events widget
henrikmv Jul 19, 2024
07f80b0
Merge remote-tracking branch 'origin/master' into hv/feat/DHIS2-17771…
henrikmv Jul 28, 2024
3dde56b
feat: orgunit in enrollment widget completed
henrikmv Jul 31, 2024
c201f96
fix: lint
henrikmv Jul 31, 2024
0f0f3c8
feat: profile widget org unit tooltip completed
henrikmv Jul 31, 2024
4cd6a58
fix: remove unused component
henrikmv Jul 31, 2024
13eb68d
fix: merge conflict
henrikmv Jul 31, 2024
06ef38f
fix: error in getOrgUnitNames
henrikmv Aug 1, 2024
d453275
fix: cache
henrikmv Aug 1, 2024
efbd81d
fix: disabled value
henrikmv Aug 3, 2024
8be30ca
fix: merge conflict
henrikmv Aug 12, 2024
039c1ac
fix: review changes
henrikmv Aug 12, 2024
763b495
fix: restructure cache
henrikmv Aug 15, 2024
57db7da
fix: set back to right cache
henrikmv Aug 15, 2024
2b43005
fix: cache structure for useorgunitnames
henrikmv Aug 15, 2024
940f45c
feat: follow cache standard for all functions
henrikmv Aug 16, 2024
712d78b
fix: move full path hook
henrikmv Aug 16, 2024
5b671b8
fix: change cache name
henrikmv Aug 16, 2024
dc7ce33
fix: undefined value for ancestor
henrikmv Aug 16, 2024
57eab52
fix: remove level
henrikmv Aug 19, 2024
fbf8410
feat: use recursion
henrikmv Aug 19, 2024
d788114
fix: remove console log
henrikmv Aug 19, 2024
d6c8a91
fix: changes on recursion
henrikmv Aug 20, 2024
707e1a9
fix: remove unnecessary function from recursion
henrikmv Aug 21, 2024
0ff202f
fix: code clean up
henrikmv Aug 21, 2024
b7a18ff
feat: change to clienttolist for widgetenrollment
henrikmv Aug 22, 2024
a66b0a2
fix: missing orgunitname in chip component
henrikmv Aug 22, 2024
b7f1a36
fix: change to orgunitname
henrikmv Aug 22, 2024
b1d1c06
fix: change to clienttolist in widgetprofile
henrikmv Aug 22, 2024
6f132f0
fix: set back to name
henrikmv Aug 23, 2024
deb46b5
feat: change from orgunitname to name
henrikmv Aug 23, 2024
8af08ee
fix: set back to cleint to view
henrikmv Aug 26, 2024
5f9e2a6
fix: review changes for orgunitname file
henrikmv Aug 26, 2024
8cc389a
feat: remove id from ancestors
henrikmv Aug 26, 2024
649107f
feat: change tooltip component
henrikmv Aug 26, 2024
9c3d38e
feat: clean up for tooltip
henrikmv Aug 26, 2024
4a598ee
fix: merge with master
henrikmv Aug 26, 2024
c1e0f57
Merge branch 'master' into hv/feat/DHIS2-17771_OrgUnitContextualizati…
henrikmv Aug 26, 2024
9622a4b
fix: after review changes
henrikmv Sep 2, 2024
1193a08
fix: cy test
henrikmv Sep 2, 2024
efe949d
Merge branch 'master' into hv/feat/DHIS2-17771_OrgUnitContextualizati…
henrikmv Sep 12, 2024
8477c66
Merge branch 'master' of https://github.com/dhis2/capture-app into hv…
henrikmv Sep 23, 2024
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
12 changes: 6 additions & 6 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1275,18 +1275,18 @@ msgstr "Enrollment date"
msgid "Incident date"
msgstr "Incident date"

msgid "Enrollment widget could not be loaded. Please try again later"
msgstr "Enrollment widget could not be loaded. Please try again later"

msgid "Follow-up"
msgstr "Follow-up"

msgid "Started at {{orgUnitName}}"
msgstr "Started at {{orgUnitName}}"

msgid "Owned by {{ownerOrgUnit}}"
msgstr "Owned by {{ownerOrgUnit}}"

msgid "Enrollment widget could not be loaded. Please try again later"
msgstr "Enrollment widget could not be loaded. Please try again later"

msgid "Follow-up"
msgstr "Follow-up"

msgid "Cancelled"
msgstr "Cancelled"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { searchScopes } from '../SearchBox';
import { enrollmentTypes } from './CardList.constants';
import { ListEntry } from './ListEntry.component';
import { dataElementTypes, getTrackerProgramThrowIfNotFound } from '../../metaData';
import { useOrgUnitName } from '../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../metadataRetrieval/orgUnitName';
import type { ListItem, RenderCustomCardActions } from './CardList.types';

type OwnProps = $ReadOnly<{|
Expand Down Expand Up @@ -144,7 +144,7 @@ const CardListItemIndex = ({
const enrollments = item.tei ? item.tei.enrollments : [];
const enrollmentType = deriveEnrollmentType(enrollments, currentProgramId);
const { orgUnitId, enrolledAt } = deriveEnrollmentOrgUnitIdAndDate(enrollments, enrollmentType, currentProgramId);
const { displayName: orgUnitName } = useOrgUnitName(orgUnitId);
const { displayName: orgUnitName } = useOrgUnitNameWithAncestors(orgUnitId);
const program = enrollments && enrollments.length
? deriveProgramFromEnrollment(enrollments, currentSearchScopeType)
: undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useScopeInfo } from '../../../hooks/useScopeInfo';
import { scopeTypes } from '../../../metaData';
import { TrackedEntityInstanceDataEntry } from '../TrackedEntityInstance';
import { useCurrentOrgUnitId } from '../../../hooks/useCurrentOrgUnitId';
import { useOrgUnitName } from '../../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../../metadataRetrieval/orgUnitName';
import type { Props, PlainProps } from './TeiRegistrationEntry.types';
import { DiscardDialog } from '../../Dialogs/DiscardDialog.component';
import { withSaveHandler } from '../../DataEntry';
Expand Down Expand Up @@ -54,7 +54,7 @@ const TeiRegistrationEntryPlain =
const { scopeType } = useScopeInfo(selectedScopeId);
const { formId, formFoundation } = useMetadataForRegistrationForm({ selectedScopeId });
const orgUnitId = useCurrentOrgUnitId();
const { displayName: orgUnitName } = useOrgUnitName(orgUnitId);
const { displayName: orgUnitName } = useOrgUnitNameWithAncestors(orgUnitId);

const handleOnCancel = () => {
if (!isUserInteractionInProgress) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { type ComponentType, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ScopeSelectorComponent } from './ScopeSelector.component';
import type { OwnProps } from './ScopeSelector.types';
import { useOrgUnitName } from '../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../metadataRetrieval/orgUnitName';
import { resetOrgUnitIdFromScopeSelector } from './ScopeSelector.actions';


Expand Down Expand Up @@ -34,7 +34,7 @@ export const ScopeSelector: ComponentType<OwnProps> = ({
}) => {
const dispatch = useDispatch();
const [selectedOrgUnit, setSelectedOrgUnit] = useState({ name: undefined, id: selectedOrgUnitId });
const { displayName, error: ouNameError } = useOrgUnitName(selectedOrgUnit.id);
const { displayName, error: ouNameError } = useOrgUnitNameWithAncestors(selectedOrgUnit.id);

useEffect(() => {
if (displayName && selectedOrgUnit.name !== displayName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @flow
import React from 'react';
henrikmv marked this conversation as resolved.
Show resolved Hide resolved
import { Tooltip } from '@dhis2/ui';

type Props = {
orgUnitName: string,
ancestors?: Array<string>,
tooltip?: string,
};

export const TooltipOrgUnit = ({ orgUnitName, ancestors = [], tooltip }: Props) => {
superskip marked this conversation as resolved.
Show resolved Hide resolved
const fullPath = [...ancestors, orgUnitName].join(' / ');
const [before, after] = tooltip ? tooltip.split(orgUnitName) : [orgUnitName];
superskip marked this conversation as resolved.
Show resolved Hide resolved

const renderTooltip = (
<Tooltip content={fullPath} openDelay={400} maxWidth={900}>
<span style={{ textDecoration: 'underline dotted' }}>{orgUnitName}</span>
</Tooltip>
);

return tooltip ? <span>{before}{renderTooltip}{after}</span> : renderTooltip;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TooltipOrgUnit } from './TooltipOrgUnit.component';
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cx from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import { colors, IconInfo16, IconWarning16 } from '@dhis2/ui';
import i18n from '@dhis2/d2-i18n';
import { useOrgUnitName } from '../../../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../../../metadataRetrieval/orgUnitName';
import { OrgUnitScopes } from '../hooks/useTransferValidation';
import { ProgramAccessLevels } from '../hooks/useProgramAccessLevel';

Expand Down Expand Up @@ -48,8 +48,8 @@ const InfoBoxesPlain = ({
orgUnitScopes,
classes,
}: Props) => {
const { displayName: ownerOrgUnitName } = useOrgUnitName(ownerOrgUnitId);
const { displayName: newOrgUnitName } = useOrgUnitName(validOrgUnitId);
const { displayName: ownerOrgUnitName } = useOrgUnitNameWithAncestors(ownerOrgUnitId);
const { displayName: newOrgUnitName } = useOrgUnitNameWithAncestors(validOrgUnitId);

const showWarning = [ProgramAccessLevels.PROTECTED, ProgramAccessLevels.CLOSED].includes(programAccessLevel)
&& orgUnitScopes.destination === OrgUnitScopes.SEARCH;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { Widget } from '../Widget';
import type { PlainProps } from './enrollment.types';
import { Status } from './Status';
import { dataElementTypes } from '../../metaData';
import { useOrgUnitName } from '../../metadataRetrieval/orgUnitName';
import { convertValue } from '../../converters/clientToView';
import { useOrgUnitNameWithAncestors } from '../../metadataRetrieval/orgUnitName';
import { Date } from './Date';
import { Actions } from './Actions';
import { MiniMap } from './MiniMap';
Expand Down Expand Up @@ -69,11 +70,32 @@ export const WidgetEnrollmentPlain = ({
onUpdateEnrollmentStatusError,
onUpdateEnrollmentStatusSuccess,
onAccessLostFromTransfer,
type = dataElementTypes.ORGANISATION_UNIT,
}: PlainProps) => {
const [open, setOpenStatus] = useState(true);
const { fromServerDate } = useTimeZoneConversion();
const geometryType = getGeometryType(enrollment?.geometry?.type);
const { displayName: orgUnitName } = useOrgUnitName(enrollment?.orgUnit);
const { displayName: orgUnitName, ancestors } = useOrgUnitNameWithAncestors(enrollment?.orgUnit);
const { displayName: ownerOrgUnitName, ancestors: ownerAncestors } = useOrgUnitNameWithAncestors(ownerOrgUnit?.id);

const orgUnitClientValue = {
name: orgUnitName,
ancestors,
tooltip: i18n.t('Started at {{orgUnitName}}', {
orgUnitName,
interpolation: { escapeValue: false },
}),
};

const ownerOrgUnitClientValue = {
name: ownerOrgUnitName,
ancestors: ownerAncestors,
tooltip: i18n.t('Owned by {{ownerOrgUnit}}', {
ownerOrgUnit: ownerOrgUnitName,
interpolation: { escapeValue: false },
}),
};


return (
<div data-test="widget-enrollment">
Expand Down Expand Up @@ -130,19 +152,18 @@ export const WidgetEnrollmentPlain = ({
<span className={classes.icon} data-test="widget-enrollment-icon-orgunit">
<IconDimensionOrgUnit16 color={colors.grey600} />
</span>
{i18n.t('Started at {{orgUnitName}}', {
orgUnitName,
interpolation: { escapeValue: false },
})}
<span>
{convertValue(orgUnitClientValue, type)}
</span>
</div>

<div className={classes.row} data-test="widget-enrollment-owner-orgunit">
<span className={classes.icon} data-test="widget-enrollment-icon-owner-orgunit">
<IconDimensionOrgUnit16 color={colors.grey600} />
</span>
{i18n.t('Owned by {{ownerOrgUnit}}', {
ownerOrgUnit: ownerOrgUnit.displayName,
})}
<span>
{convertValue(ownerOrgUnitClientValue, type)}
</span>
</div>

<div className={classes.row} data-test="widget-enrollment-last-update">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
import { errorCreator } from 'capture-core-utils';
import log from 'loglevel';
import { WidgetEnrollment as WidgetEnrollmentNote } from './WidgetEnrollment.component';
import { useOrgUnitName } from '../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../metadataRetrieval/orgUnitName';
import { useTrackedEntityInstances } from './hooks/useTrackedEntityInstances';
import { useEnrollment } from './hooks/useEnrollment';
import { useProgram } from './hooks/useProgram';
Expand Down Expand Up @@ -68,7 +68,7 @@ export const WidgetEnrollment = ({
enrollments,
refetch: refetchTEI,
} = useTrackedEntityInstances(teiId, programId);
const { error: errorOrgUnit, displayName } = useOrgUnitName(
const { error: errorOrgUnit, displayName } = useOrgUnitNameWithAncestors(
typeof ownerOrgUnit === 'string' ? ownerOrgUnit : undefined,
);
const { error: errorLocale, locale } = useUserLocale();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import i18n from '@dhis2/d2-i18n';
import { useDispatch } from 'react-redux';
import moment from 'moment';
import { getProgramAndStageForProgram, TrackerProgram, getProgramEventAccess, dataElementTypes } from '../../metaData';
import { useOrgUnitName } from '../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../metadataRetrieval/orgUnitName';
import { useLocationQuery } from '../../utils/routing';
import type { ContainerProps } from './widgetEventSchedule.types';
import { WidgetEventScheduleComponent } from './WidgetEventSchedule.component';
Expand Down Expand Up @@ -37,7 +37,7 @@ export const WidgetEventSchedule = ({
}: ContainerProps) => {
const { program, stage } = useMemo(() => getProgramAndStageForProgram(programId, stageId), [programId, stageId]);
const dispatch = useDispatch();
const orgUnit = { id: orgUnitId, name: useOrgUnitName(orgUnitId).displayName };
const orgUnit = { id: orgUnitId, name: useOrgUnitNameWithAncestors(orgUnitId).displayName };
const { programStageScheduleConfig } = useScheduleConfigFromProgramStage(stageId);
const { programConfig } = useScheduleConfigFromProgram(programId);
const suggestedScheduleDate = useDetermineSuggestedScheduleDate({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ const WidgetProfilePlain = ({
const displayInListAttributes = useMemo(() => clientAttributesWithSubvalues
.filter(item => item.displayInList)
.map((clientAttribute) => {
const { attribute, key } = clientAttribute;
const { attribute, key, valueType } = clientAttribute;
const value = convertClientToView(clientAttribute);
return {
attribute, key, value, reactKey: attribute,
attribute, key, value, valueType, reactKey: attribute,
};
}), [clientAttributesWithSubvalues]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,17 @@ const getOrganisationUnitSubvalue = async ({ attribute, querySingleResource }: S
resource: 'organisationUnits',
id: attribute.value,
params: {
fields: 'id,name',
fields: 'id,name,ancestors[displayName]',
},
});
return { ...organisationUnit };

const orgUnitClientValue = {
id: organisationUnit.id,
name: organisationUnit.name,
ancestors: organisationUnit.ancestors.map(ancestor => ancestor.displayName),
};

return orgUnitClientValue;
};

export const subValueGetterByElementType = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow
import React from 'react';
import { useOrgUnitName } from '../../../metadataRetrieval/orgUnitName';
import { useOrgUnitNameWithAncestors } from '../../../metadataRetrieval/orgUnitName';

type Props = {
orgUnitId: string,
Expand All @@ -9,7 +9,7 @@ type Props = {
export const FlatListOrgUnitField = ({
orgUnitId,
}: Props) => {
const { displayName } = useOrgUnitName(orgUnitId);
const { displayName } = useOrgUnitNameWithAncestors(orgUnitId);

return (
<span>
Expand Down
27 changes: 18 additions & 9 deletions src/core_modules/capture-core/converters/clientToView.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,39 @@ import { dataElementTypes, type DataElement } from '../metaData';
import { convertMomentToDateFormatString } from '../utils/converters/date';
import { stringifyNumber } from './common/stringifyNumber';
import { MinimalCoordinates } from '../components/MinimalCoordinates';
import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';


function convertDateForView(rawValue: string): string {
const momentDate = moment(rawValue);
return convertMomentToDateFormatString(momentDate);
}

function convertDateTimeForView(rawValue: string): string {
const momentDate = moment(rawValue);
const dateString = convertMomentToDateFormatString(momentDate);
const timeString = momentDate.format('HH:mm');
return `${dateString} ${timeString}`;
}

function convertTimeForView(rawValue: string): string {
const momentDate = moment(rawValue, 'HH:mm', true);
return momentDate.format('HH:mm');
}

type FileClientValue = {
name: string,
url: string,
value: string,
};

type ImageClientValue = {
...FileClientValue,
previewUrl: string,
};

type OrgUnitClientValue = {
name: string,
ancestors?: Array<string>,
tooltip?: string,
};

function convertFileForDisplay(clientValue: FileClientValue) {
return (
<a
Expand All @@ -49,11 +52,20 @@ function convertFileForDisplay(clientValue: FileClientValue) {
</a>
);
}

function convertImageForDisplay(clientValue: ImageClientValue) {
return <PreviewImage url={clientValue.url} previewUrl={clientValue.previewUrl} alignLeft />;
}

function convertOrgUnitForDisplay(clientValue: OrgUnitClientValue) {
return (
<TooltipOrgUnit
orgUnitName={clientValue.name}
ancestors={clientValue.ancestors}
tooltip={clientValue.tooltip}
/>
);
}

const valueConvertersForType = {
[dataElementTypes.NUMBER]: stringifyNumber,
[dataElementTypes.INTEGER]: stringifyNumber,
Expand All @@ -70,26 +82,23 @@ const valueConvertersForType = {
[dataElementTypes.AGE]: convertDateForView,
[dataElementTypes.FILE_RESOURCE]: convertFileForDisplay,
[dataElementTypes.IMAGE]: convertImageForDisplay,
[dataElementTypes.ORGANISATION_UNIT]: (rawValue: Object) => rawValue.name,
[dataElementTypes.ORGANISATION_UNIT]: convertOrgUnitForDisplay,
[dataElementTypes.POLYGON]: () => 'Polygon',
};

export function convertValue(value: any, type: $Keys<typeof dataElementTypes>, dataElement?: ?DataElement) {
if (!value && value !== 0 && value !== false) {
return value;
}

if (dataElement && dataElement.optionSet) {
if (dataElement.type === dataElementTypes.MULTI_TEXT) {
return dataElement.optionSet.getMultiOptionsText(value);
}
return dataElement.optionSet.getOptionText(value);
}

// $FlowFixMe dataElementTypes flow error
return valueConvertersForType[type] ? valueConvertersForType[type](value) : value;
}

export function convertDateWithTimeForView(rawValue?: ?string): string {
if (!rawValue) { return ''; }
if (!moment(rawValue).hours() && !moment(rawValue).minutes()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow
export {
useOrgUnitName,
useOrgUnitNameWithAncestors,
useOrgUnitNames,
getOrgUnitNames,
getCachedOrgUnitName,
Expand Down
Loading
Loading