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-18328] Handle log entries for occurredAt, scheduledAt and geometry #3887

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
22 changes: 20 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-11-07T11:57:59.094Z\n"
"PO-Revision-Date: 2024-11-07T11:57:59.094Z\n"
"POT-Creation-Date: 2024-12-15T15:25:38.375Z\n"
"PO-Revision-Date: 2024-12-15T15:25:38.375Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -101,6 +101,24 @@ msgstr "Date of enrollment"
msgid "Last updated"
msgstr "Last updated"

msgid "Lat"
msgstr "Lat"

msgid "Long"
msgstr "Long"

msgid "lat"
msgstr "lat"

msgid "long"
msgstr "long"

msgid "Show less"
msgstr "Show less"

msgid "Show more"
msgstr "Show more"

msgid "error encountered during field validation"
msgstr "error encountered during field validation"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import React from 'react';
import i18n from '@dhis2/d2-i18n';

type Props = $ReadOnly<{|
latitude: number | string,
Expand All @@ -8,9 +9,10 @@ type Props = $ReadOnly<{|

const toSixDecimal = value => (parseFloat(value) ? parseFloat(value).toFixed(6) : null);

export const MinimalCoordinates = ({ latitude, longitude }: Props) =>
(<div>
lat: {toSixDecimal(latitude)} <br />
long: {toSixDecimal(longitude)}
</div>);
export const MinimalCoordinates = ({ latitude, longitude }: Props) => (
<div>
{i18n.t('Lat')}: {toSixDecimal(latitude)}<br />
{i18n.t('Long')}: {toSixDecimal(longitude)}
</div>
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// @flow
import React, { useState } from 'react';
import i18n from '@dhis2/d2-i18n';
import { withStyles } from '@material-ui/core/styles';
import { IconChevronUp16, IconChevronDown16, colors, spacers } from '@dhis2/ui';

type Props = $ReadOnly<{|
coordinates: Array<Array<number>>,
classes: {
buttonContainer: string,
viewButton: string,
},
|}>;

const styles = {
buttonContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
viewButton: {
background: 'none',
border: 'none',
cursor: 'pointer',
color: colors.grey800,
marginTop: spacers.dp8,
display: 'flex',
alignItems: 'center',
'&:hover': {
textDecoration: 'underline',
color: 'black',
},
},
};

const PolygonCoordinatesPlain = ({ coordinates, classes }: Props) => {
const [showMore, setShowMore] = useState(false);
return (
<>
<div>
{coordinates.slice(0, showMore ? coordinates.length : 1).map((coordinatePair, index) => (
// eslint-disable-next-line react/no-array-index-key
<div key={index}>
{`${i18n.t('lat')}: ${coordinatePair[1]}`}<br />
{`${i18n.t('long')}: ${coordinatePair[0]}`}
</div>
))}
</div>
<div className={classes.buttonContainer}>
<button className={classes.viewButton} onClick={() => setShowMore(!showMore)}>
{showMore ? i18n.t('Show less') : i18n.t('Show more')}
{showMore ? <IconChevronUp16 /> : <IconChevronDown16 />}
</button>
</div>
</>
);
};

export const PolygonCoordinates = withStyles(styles)(PolygonCoordinatesPlain);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { PolygonCoordinates } from './PolygonCoordinates';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow
export { MinimalCoordinates } from './MinimalCoordinates';
export { PolygonCoordinates } from './PolygonCoordinates';
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function getTrackerProgram(suggestedProgramId: string) {
log.error(
errorCreator('tracker program for id not found')({ suggestedProgramId, error }),
);
throw Error(i18n('Metadata error. see log for details'));
throw Error(i18n.t('Metadata error. see log for details'));
}
return trackerProgram;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function getTrackerProgram(suggestedProgramId: string) {
log.error(
errorCreator('tracker program for id not found')({ suggestedProgramId, error }),
);
throw Error(i18n('Metadata error. see log for details'));
throw Error(i18n.t('Metadata error. see log for details'));
}
return trackerProgram;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import React, { useMemo } from 'react';
import i18n from '@dhis2/d2-i18n';
import type { DataElement } from '../../../metaData';
import { dataElementTypes } from '../../../metaData';
import type { Props } from './EventChangelogWrapper.types';
Expand Down Expand Up @@ -42,9 +43,19 @@ export const EventChangelogWrapper = ({ formFoundation, eventId, eventData, ...p
return acc;
}, {});

const additionalFields = formFoundation.featureType !== 'None' ? {
geometry: {
id: 'geometry',
name: formFoundation.featureType === 'Polygon' ? i18n.t('Area') : i18n.t('Coordinate'),
type: formFoundation.featureType === 'Polygon' ?
dataElementTypes.POLYGON : dataElementTypes.COORDINATE,
},
} : null;

return {
...fieldElementsById,
...fieldElementsContext,
...additionalFields,
};
}, [formFoundation]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ type CreatedChange = {|
type: typeof CHANGE_TYPES.CREATED,
dataElement?: string,
attribute?: string,
field?: string,
currentValue: any,
|}

type UpdatedChange = {|
type: typeof CHANGE_TYPES.UPDATED,
dataElement?: string,
attribute?: string,
field?: string,
previousValue: any,
currentValue: any,
|}
Expand All @@ -21,6 +23,7 @@ type DeletedChange = {|
type: typeof CHANGE_TYPES.DELETED,
dataElement?: string,
attribute?: string,
field?: string,
previousValue: any,
|}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,23 @@ const styles = {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
whiteSpace: 'normal',
height: '100%',
},
buttonContainer: {
display: 'flex',
justifyContent: 'center',
},
previousValue: {
color: colors.grey700,
wordBreak: 'break-word',
},
currentValue: {
color: colors.grey900,
wordBreak: 'break-word',
maxWidth: '82%',
},
arrow: {
margin: `0 ${spacers.dp4}`,
margin: spacers.dp4,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ const fetchFormattedValues = async ({
elementKey: string,
change: Change,
) => {
const fieldId = change.dataElement || change.attribute;
const { dataElement, attribute, field } = change;
const fieldId = dataElement ?? attribute ?? field;
if (!fieldId) {
log.error('Could not find fieldId in change:', change);
return { metadataElement: null, fieldId: null };
Expand Down Expand Up @@ -115,6 +116,7 @@ const fetchFormattedValues = async ({
reactKey: fieldId ? `${createdAt}-${fieldId}` : attributeOptionsKey,
date: pipe(convertServerToClient, convertClientToList)(fromServerDate(createdAt), dataElementTypes.DATETIME),
user: `${firstName} ${surname} (${username})`,
dataItemId: fieldId,
changeType: type,
dataItemLabel: metadataElement.name,
previousValue,
Expand Down
44 changes: 24 additions & 20 deletions src/core_modules/capture-core/converters/clientToList.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { PreviewImage } from 'capture-ui';
import { dataElementTypes, type DataElement } from '../metaData';
import { convertMomentToDateFormatString } from '../utils/converters/date';
import { stringifyNumber } from './common/stringifyNumber';
import { MinimalCoordinates } from '../components/MinimalCoordinates';
import { MinimalCoordinates, PolygonCoordinates } from '../components/Coordinates';
import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';

function convertDateForListDisplay(rawValue: string): string {
Expand Down Expand Up @@ -87,41 +87,45 @@ function convertStatusForDisplay(clientValue: Object) {
);
}

function convertOrgUnitForDisplay(clientValue: string | {id: string}) {
function convertOrgUnitForDisplay(clientValue: string | { id: string }) {
const orgUnitId = typeof clientValue === 'string' ? clientValue : clientValue.id;
return (
<TooltipOrgUnit orgUnitId={orgUnitId} />
);
}

function convertPolygonForDisplay(clientValue: Object) {
return <PolygonCoordinates coordinates={clientValue} />;
}

const valueConvertersForType = {
[dataElementTypes.NUMBER]: stringifyNumber,
[dataElementTypes.INTEGER]: stringifyNumber,
[dataElementTypes.INTEGER_POSITIVE]: stringifyNumber,
[dataElementTypes.INTEGER_ZERO_OR_POSITIVE]: stringifyNumber,
[dataElementTypes.INTEGER_NEGATIVE]: stringifyNumber,
[dataElementTypes.INTEGER_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.INTEGER_POSITIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.INTEGER_ZERO_OR_POSITIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.INTEGER_NEGATIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.PERCENTAGE]: (value: number) => `${stringifyNumber(value)} %`,
[dataElementTypes.AGE]: convertDateForListDisplay,
[dataElementTypes.ASSIGNEE]: (rawValue: Object) => `${rawValue.name} (${rawValue.username})`,
[dataElementTypes.BOOLEAN]: (rawValue: boolean) => (rawValue ? i18n.t('Yes') : i18n.t('No')),
[dataElementTypes.COORDINATE]: MinimalCoordinates,
[dataElementTypes.DATE]: convertDateForListDisplay,
[dataElementTypes.DATE_RANGE]: value => convertRangeForDisplay(convertDateForListDisplay, value),
[dataElementTypes.DATETIME]: convertDateTimeForListDisplay,
[dataElementTypes.DATETIME_RANGE]: value => convertRangeForDisplay(convertDateTimeForListDisplay, value),
[dataElementTypes.TIME]: convertTimeForListDisplay,
[dataElementTypes.TIME_RANGE]: value => convertRangeForDisplay(convertTimeForListDisplay, value),
[dataElementTypes.TRUE_ONLY]: () => i18n.t('Yes'),
[dataElementTypes.BOOLEAN]: (rawValue: boolean) => (rawValue ? i18n.t('Yes') : i18n.t('No')),
[dataElementTypes.COORDINATE]: MinimalCoordinates,
[dataElementTypes.AGE]: convertDateForListDisplay,
[dataElementTypes.FILE_RESOURCE]: convertFileForDisplay,
[dataElementTypes.IMAGE]: convertImageForDisplay,
[dataElementTypes.ORGANISATION_UNIT]: convertOrgUnitForDisplay,
[dataElementTypes.ASSIGNEE]: (rawValue: Object) => `${rawValue.name} (${rawValue.username})`,
[dataElementTypes.INTEGER]: stringifyNumber,
[dataElementTypes.INTEGER_NEGATIVE]: stringifyNumber,
[dataElementTypes.INTEGER_NEGATIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.INTEGER_POSITIVE]: stringifyNumber,
[dataElementTypes.INTEGER_POSITIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.INTEGER_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.INTEGER_ZERO_OR_POSITIVE]: stringifyNumber,
[dataElementTypes.INTEGER_ZERO_OR_POSITIVE_RANGE]: value => convertRangeForDisplay(stringifyNumber, value),
[dataElementTypes.NUMBER]: stringifyNumber,
[dataElementTypes.NUMBER_RANGE]: convertNumberRangeForDisplay,
[dataElementTypes.ORGANISATION_UNIT]: convertOrgUnitForDisplay,
[dataElementTypes.PERCENTAGE]: (value: number) => `${stringifyNumber(value)} %`,
[dataElementTypes.POLYGON]: convertPolygonForDisplay,
[dataElementTypes.STATUS]: convertStatusForDisplay,
[dataElementTypes.TIME]: convertTimeForListDisplay,
[dataElementTypes.TIME_RANGE]: value => convertRangeForDisplay(convertTimeForListDisplay, value),
[dataElementTypes.TRUE_ONLY]: () => i18n.t('Yes'),
};

export function convertValue(value: any, type: $Keys<typeof dataElementTypes>, dataElement?: ?DataElement) {
Expand Down
2 changes: 1 addition & 1 deletion src/core_modules/capture-core/converters/clientToView.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PreviewImage } from 'capture-ui';
import { dataElementTypes, type DataElement } from '../metaData';
import { convertMomentToDateFormatString } from '../utils/converters/date';
import { stringifyNumber } from './common/stringifyNumber';
import { MinimalCoordinates } from '../components/MinimalCoordinates';
import { MinimalCoordinates } from '../components/Coordinates';
import { TooltipOrgUnit } from '../components/Tooltips/TooltipOrgUnit';


Expand Down
25 changes: 20 additions & 5 deletions src/core_modules/capture-core/converters/serverToClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ export function convertOptionSetValue(value: any, type: $Keys<typeof dataElement
return optionSetConvertersForType[type] ? optionSetConvertersForType[type](value) : value;
}

function convertCoordinateToClient(value: any) {
const coordinates = typeof value === 'string' ?
value.replace(/[\(\)\[\]\s]/g, '').split(',').map(Number) : value;

return { latitude: coordinates[1], longitude: coordinates[0] };
}

function convertPolygonToClient(value: any) {
if (typeof value === 'string') {
const coordinates = value.replace(/[()]/g, '').split(',').map(Number);
const coordinatesArray = [];
for (let i = 0; i < coordinates.length; i += 2) {
coordinatesArray.push([coordinates[i], coordinates[i + 1]]);
}
return coordinatesArray;
}
return value;
}

const valueConvertersForType = {
[dataElementTypes.NUMBER]: parseNumber,
Expand All @@ -55,11 +73,8 @@ const valueConvertersForType = {
[dataElementTypes.DATETIME]: (d2Value: string) => moment(d2Value).toISOString(),
[dataElementTypes.TRUE_ONLY]: (d2Value: string) => ((d2Value === 'true') || null),
[dataElementTypes.BOOLEAN]: (d2Value: string) => (d2Value === 'true'),
[dataElementTypes.COORDINATE]: (d2Value: string | Array<string>) => {
const arr = typeof d2Value === 'string' ? JSON.parse(d2Value) : d2Value;
return { latitude: arr[1], longitude: arr[0] };
},
[dataElementTypes.POLYGON]: (d2Value: Array<number>) => d2Value,
[dataElementTypes.COORDINATE]: (d2Value: string | Array<string>) => convertCoordinateToClient(d2Value),
[dataElementTypes.POLYGON]: (d2Value: string | Array<Array<number>>) => convertPolygonToClient(d2Value),
[dataElementTypes.ASSIGNEE]: convertAssignedUserToClient,
};

Expand Down
Loading