From dcf1a957e4504247cd27b69c75cb67128526ed7c Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Tue, 7 Jan 2025 14:39:04 +0100 Subject: [PATCH 1/5] chore: [DHIS2-18736] fix Cypress tests broken due to the change of year (#3933) --- .../StagesAndEventsWidget/StagesAndEventsWidget.feature | 2 +- .../StagesAndEventsWidget/StagesAndEventsWidget.js | 4 ++-- .../e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js | 4 ++-- .../WidgetsForEnrollmentAddEventPage.feature | 2 +- .../WidgetsForEnrollmentDashboard.feature | 2 +- .../WidgetsForEnrollmentEditEvent.feature | 2 +- .../TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature | 2 ++ 7 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature index e944741b2f..a18e595f6b 100644 --- a/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature +++ b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature @@ -71,7 +71,7 @@ Feature: User interacts with Stages and Events Widget @with-restore-deleted-event Scenario: User can delete an event - Given you open the enrollment page by typing #/enrollment?enrollmentId=ikYMpSKXik1&orgUnitId=DiszpKrYNg8&programId=ur1Edk5Oe2n&teiId=Trc1H9T5C6f + Given you open the enrollment page by typing #/enrollment?enrollmentId=ITyaPVATEwc&orgUnitId=DiszpKrYNg8&programId=ur1Edk5Oe2n&teiId=wsk89u7zquT And there is an Active event in the TB visit stage When you click the Delete event overflow button on the Active event And you confirm you want to delete the event diff --git a/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.js b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.js index 35d8996ee7..bf5334da58 100644 --- a/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.js +++ b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.js @@ -3,7 +3,7 @@ import { getCurrentYear } from '../../../support/date'; import '../sharedSteps'; After({ tags: '@with-restore-deleted-event' }, () => { - cy.visit('#/enrollment?enrollmentId=ikYMpSKXik1&orgUnitId=DiszpKrYNg8&programId=ur1Edk5Oe2n&teiId=Trc1H9T5C6f'); + cy.visit('#/enrollment?enrollmentId=ITyaPVATEwc&orgUnitId=DiszpKrYNg8&programId=ur1Edk5Oe2n&teiId=wsk89u7zquT'); cy.get('[data-test="stages-and-events-widget"]') .find('[data-test="widget-contents"]') @@ -215,7 +215,7 @@ When(/you click the (.*) event overflow button on the (.*) event$/, (buttonName, cy.get('[data-test="overflow-menu"]') .contains(buttonName) - .click(); + .click({ force: true }); }); Then('the event should be skipped', () => { diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js index 9f4148abf9..fe8fdfee12 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js @@ -9,10 +9,10 @@ After({ tags: '@with-transfer-ownership-data-cleanup' }, () => { }); const changeEnrollmentAndEventsStatus = () => ( - cy.buildApiUrl('tracker', 'trackedEntities/osF4RF4EiqP?program=IpHINAT79UW&fields=enrollments') + cy.buildApiUrl('tracker', 'trackedEntities/mPLqCVS27AD?program=IpHINAT79UW&fields=enrollments') .then(url => cy.request(url)) .then(({ body }) => { - const enrollment = body.enrollments && body.enrollments.find(e => e.enrollment === 'qyx7tscVpVB'); + const enrollment = body.enrollments && body.enrollments.find(e => e.enrollment === 'YqNTNLKmX4z'); const eventsToUpdate = enrollment.events.map(e => ({ ...e, status: 'ACTIVE', diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature index 0ab586b19f..cd6062c7fe 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.feature @@ -68,7 +68,7 @@ Feature: The user interacts with the widgets on the enrollment add event page Then you can assign a user when scheduling the event Scenario: User can complete the enrollment and the active events - Given you land on the enrollment edit event page by having typed #/enrollmentEventNew?enrollmentId=qyx7tscVpVB&orgUnitId=DiszpKrYNg8&programId=IpHINAT79UW&teiId=osF4RF4EiqP + Given you land on the enrollment edit event page by having typed #/enrollmentEventNew?enrollmentId=YqNTNLKmX4z&orgUnitId=RzgSFJ9E46G&programId=IpHINAT79UW&teiId=mPLqCVS27AD And the enrollment widget should be opened And the user sees the enrollment status and the Baby Postnatal event status is active And the user opens the enrollment actions menu diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.feature index ffd0a56c1c..76b3465297 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.feature @@ -127,7 +127,7 @@ Feature: The user interacts with the widgets on the enrollment dashboard Then the user can see the program rules effect in the indicator widget Scenario: User can complete the enrollment and the active events - Given you land on the enrollment dashboard page by having typed #/enrollment?enrollmentId=qyx7tscVpVB + Given you land on the enrollment dashboard page by having typed #/enrollment?enrollmentId=YqNTNLKmX4z And the enrollment widget should be opened And the user sees the enrollment status and the Baby Postnatal event status is active And the user opens the enrollment actions menu diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature index 39bd30c304..defa075922 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/WidgetsForEnrollmentEditEvent.feature @@ -105,7 +105,7 @@ Feature: The user interacts with the widgets on the enrollment edit event Then the table footer should display page 2 Scenario: User can complete the enrollment and the active events - Given you land on the enrollment edit event page by having typed #/enrollmentEventEdit?eventId=OWpIzQ4xabC&orgUnitId=DiszpKrYNg8 + Given you land on the enrollment edit event page by having typed #/enrollmentEventEdit?eventId=PyXThVzWJzL&orgUnitId=RzgSFJ9E46G And the enrollment widget should be opened And the user sees the enrollment status and the Baby Postnatal event status is active And the user opens the enrollment actions menu diff --git a/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature index f79f4faf87..40b383e7b3 100644 --- a/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature +++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature @@ -31,6 +31,8 @@ Feature: User facing tests for bulk actions on Tracked Entity working lists When you select the first 5 rows Then the filters should be disabled +#DHIS2-18447 +@skip Scenario: The user should see an error message when trying to bulk complete enrollments with errors Given you open the main page with Ngelehun and Malaria focus investigation context And you select the first 3 rows From f670971808f49b75295c1d3c7ef25a747b4888df Mon Sep 17 00:00:00 2001 From: henrikmv <110386561+henrikmv@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:34:37 +0100 Subject: [PATCH 2/5] feat: [DHIS2-18328] Handle log entries for occurredAt, scheduledAt and geometry (#3887) * feat: geometry scheduledat occuredat in changelog * fix: clean up * fix: add check for featuretype * feat: style improvements * fix: convert changelog data in servertoclient * feat: update clienttolist * fix: revert new folder structure * fix: code clean up * fix: show coordinate * feat: style improvements * fix: change from property to fields * fix: review comments * fix: translation * fix: revert type change --- i18n/en.pot | 22 ++++++- .../MinimalCoordinates/MinimalCoordinates.js | 12 ++-- .../MinimalCoordinates/index.js | 0 .../PolygonCoordinates/PolygonCoordinates.js | 59 +++++++++++++++++++ .../Coordinates/PolygonCoordinates/index.js | 2 + .../components/Coordinates/index.js | 3 + .../NewRelationship/RegisterTei/open.epics.js | 2 +- ...TrackedEntityRelationshipsWrapper.epics.js | 2 +- .../EventChangelogWrapper.component.js | 11 ++++ .../common/Changelog/Changelog.types.js | 3 + .../ChangelogCells/ChangelogValueCell.js | 8 ++- .../common/hooks/useListDataValues.js | 4 +- .../capture-core/converters/clientToList.js | 44 +++++++------- .../capture-core/converters/clientToView.js | 2 +- .../capture-core/converters/serverToClient.js | 25 ++++++-- 15 files changed, 161 insertions(+), 38 deletions(-) rename src/core_modules/capture-core/components/{ => Coordinates}/MinimalCoordinates/MinimalCoordinates.js (61%) rename src/core_modules/capture-core/components/{ => Coordinates}/MinimalCoordinates/index.js (100%) create mode 100644 src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/PolygonCoordinates.js create mode 100644 src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/index.js create mode 100644 src/core_modules/capture-core/components/Coordinates/index.js diff --git a/i18n/en.pot b/i18n/en.pot index 2f414dc91a..0e6ac05309 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -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-12-05T11:39:04.447Z\n" -"PO-Revision-Date: 2024-12-05T11:39:04.447Z\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..." @@ -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" diff --git a/src/core_modules/capture-core/components/MinimalCoordinates/MinimalCoordinates.js b/src/core_modules/capture-core/components/Coordinates/MinimalCoordinates/MinimalCoordinates.js similarity index 61% rename from src/core_modules/capture-core/components/MinimalCoordinates/MinimalCoordinates.js rename to src/core_modules/capture-core/components/Coordinates/MinimalCoordinates/MinimalCoordinates.js index 36af51e4f5..39b23b672c 100644 --- a/src/core_modules/capture-core/components/MinimalCoordinates/MinimalCoordinates.js +++ b/src/core_modules/capture-core/components/Coordinates/MinimalCoordinates/MinimalCoordinates.js @@ -1,5 +1,6 @@ // @flow import React from 'react'; +import i18n from '@dhis2/d2-i18n'; type Props = $ReadOnly<{| latitude: number | string, @@ -8,9 +9,10 @@ type Props = $ReadOnly<{| const toSixDecimal = value => (parseFloat(value) ? parseFloat(value).toFixed(6) : null); -export const MinimalCoordinates = ({ latitude, longitude }: Props) => - (
- lat: {toSixDecimal(latitude)}
- long: {toSixDecimal(longitude)} -
); +export const MinimalCoordinates = ({ latitude, longitude }: Props) => ( +
+ {i18n.t('Lat')}: {toSixDecimal(latitude)}
+ {i18n.t('Long')}: {toSixDecimal(longitude)} +
+); diff --git a/src/core_modules/capture-core/components/MinimalCoordinates/index.js b/src/core_modules/capture-core/components/Coordinates/MinimalCoordinates/index.js similarity index 100% rename from src/core_modules/capture-core/components/MinimalCoordinates/index.js rename to src/core_modules/capture-core/components/Coordinates/MinimalCoordinates/index.js diff --git a/src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/PolygonCoordinates.js b/src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/PolygonCoordinates.js new file mode 100644 index 0000000000..c019bdb5fe --- /dev/null +++ b/src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/PolygonCoordinates.js @@ -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>, + 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 ( + <> +
+ {coordinates.slice(0, showMore ? coordinates.length : 1).map((coordinatePair, index) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {`${i18n.t('lat')}: ${coordinatePair[1]}`}
+ {`${i18n.t('long')}: ${coordinatePair[0]}`} +
+ ))} +
+
+ +
+ + ); +}; + +export const PolygonCoordinates = withStyles(styles)(PolygonCoordinatesPlain); diff --git a/src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/index.js b/src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/index.js new file mode 100644 index 0000000000..0c85230d28 --- /dev/null +++ b/src/core_modules/capture-core/components/Coordinates/PolygonCoordinates/index.js @@ -0,0 +1,2 @@ +// @flow +export { PolygonCoordinates } from './PolygonCoordinates'; diff --git a/src/core_modules/capture-core/components/Coordinates/index.js b/src/core_modules/capture-core/components/Coordinates/index.js new file mode 100644 index 0000000000..a7549a63b6 --- /dev/null +++ b/src/core_modules/capture-core/components/Coordinates/index.js @@ -0,0 +1,3 @@ +// @flow +export { MinimalCoordinates } from './MinimalCoordinates'; +export { PolygonCoordinates } from './PolygonCoordinates'; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/open.epics.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/open.epics.js index cfa4280b97..3244faf794 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/open.epics.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/open.epics.js @@ -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; } diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper/TrackedEntityRelationshipsWrapper.epics.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper/TrackedEntityRelationshipsWrapper.epics.js index 488faf8e6f..c752b65d0a 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper/TrackedEntityRelationshipsWrapper.epics.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper/TrackedEntityRelationshipsWrapper.epics.js @@ -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; } diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.js b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.js index e7041bdd74..2d7d294dd0 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.js +++ b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.js @@ -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'; @@ -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]); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/Changelog/Changelog.types.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/Changelog/Changelog.types.js index 56dccc68cd..ece01ab3a9 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/Changelog/Changelog.types.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/Changelog/Changelog.types.js @@ -6,6 +6,7 @@ type CreatedChange = {| type: typeof CHANGE_TYPES.CREATED, dataElement?: string, attribute?: string, + field?: string, currentValue: any, |} @@ -13,6 +14,7 @@ type UpdatedChange = {| type: typeof CHANGE_TYPES.UPDATED, dataElement?: string, attribute?: string, + field?: string, previousValue: any, currentValue: any, |} @@ -21,6 +23,7 @@ type DeletedChange = {| type: typeof CHANGE_TYPES.DELETED, dataElement?: string, attribute?: string, + field?: string, previousValue: any, |} diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogCells/ChangelogValueCell.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogCells/ChangelogValueCell.js index 4d6bd41b3e..3b43a13eba 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogCells/ChangelogValueCell.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogCells/ChangelogValueCell.js @@ -23,9 +23,12 @@ const styles = { display: 'flex', flexDirection: 'row', alignItems: 'center', - whiteSpace: 'normal', height: '100%', }, + buttonContainer: { + display: 'flex', + justifyContent: 'center', + }, previousValue: { color: colors.grey700, wordBreak: 'break-word', @@ -33,9 +36,10 @@ const styles = { currentValue: { color: colors.grey900, wordBreak: 'break-word', + maxWidth: '82%', }, arrow: { - margin: `0 ${spacers.dp4}`, + margin: spacers.dp4, }, }; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js index e94bf94a88..16dcbe87f2 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js @@ -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 }; @@ -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, diff --git a/src/core_modules/capture-core/converters/clientToList.js b/src/core_modules/capture-core/converters/clientToList.js index e72b837179..abbfbf6975 100644 --- a/src/core_modules/capture-core/converters/clientToList.js +++ b/src/core_modules/capture-core/converters/clientToList.js @@ -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 { @@ -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 ( ); } +function convertPolygonForDisplay(clientValue: Object) { + return ; +} 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, dataElement?: ?DataElement) { diff --git a/src/core_modules/capture-core/converters/clientToView.js b/src/core_modules/capture-core/converters/clientToView.js index fb7728f723..826c637456 100644 --- a/src/core_modules/capture-core/converters/clientToView.js +++ b/src/core_modules/capture-core/converters/clientToView.js @@ -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'; diff --git a/src/core_modules/capture-core/converters/serverToClient.js b/src/core_modules/capture-core/converters/serverToClient.js index 7a342c3aff..456e87d6af 100644 --- a/src/core_modules/capture-core/converters/serverToClient.js +++ b/src/core_modules/capture-core/converters/serverToClient.js @@ -43,6 +43,24 @@ export function convertOptionSetValue(value: any, type: $Keys moment(d2Value).toISOString(), [dataElementTypes.TRUE_ONLY]: (d2Value: string) => ((d2Value === 'true') || null), [dataElementTypes.BOOLEAN]: (d2Value: string) => (d2Value === 'true'), - [dataElementTypes.COORDINATE]: (d2Value: string | Array) => { - const arr = typeof d2Value === 'string' ? JSON.parse(d2Value) : d2Value; - return { latitude: arr[1], longitude: arr[0] }; - }, - [dataElementTypes.POLYGON]: (d2Value: Array) => d2Value, + [dataElementTypes.COORDINATE]: (d2Value: string | Array) => convertCoordinateToClient(d2Value), + [dataElementTypes.POLYGON]: (d2Value: string | Array>) => convertPolygonToClient(d2Value), [dataElementTypes.ASSIGNEE]: convertAssignedUserToClient, }; From 8227680c27d4635753ea2f644c502ab46261f15e Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Tue, 7 Jan 2025 22:38:40 +0000 Subject: [PATCH 3/5] chore(release): cut 101.21.0 [skip release] # [101.21.0](https://github.com/dhis2/capture-app/compare/v101.20.3...v101.21.0) (2025-01-07) ### Features * [DHIS2-18328] Handle log entries for occurredAt, scheduledAt and geometry ([#3887](https://github.com/dhis2/capture-app/issues/3887)) ([f670971](https://github.com/dhis2/capture-app/commit/f670971808f49b75295c1d3c7ef25a747b4888df)) --- CHANGELOG.md | 7 +++++++ package.json | 4 ++-- packages/rules-engine/package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f8e81a1bc..b144404cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [101.21.0](https://github.com/dhis2/capture-app/compare/v101.20.3...v101.21.0) (2025-01-07) + + +### Features + +* [DHIS2-18328] Handle log entries for occurredAt, scheduledAt and geometry ([#3887](https://github.com/dhis2/capture-app/issues/3887)) ([f670971](https://github.com/dhis2/capture-app/commit/f670971808f49b75295c1d3c7ef25a747b4888df)) + ## [101.20.3](https://github.com/dhis2/capture-app/compare/v101.20.2...v101.20.3) (2024-12-29) diff --git a/package.json b/package.json index e4caffa666..9e3f58ba03 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.20.3", + "version": "101.21.0", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.20.3", + "@dhis2/rules-engine-javascript": "101.21.0", "@dhis2/app-runtime": "^3.9.3", "@dhis2/d2-i18n": "^1.1.0", "@dhis2/d2-icons": "^1.0.1", diff --git a/packages/rules-engine/package.json b/packages/rules-engine/package.json index 0ae04d0315..df1868e4b6 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.20.3", + "version": "101.21.0", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": { From 15414cc73f7ce74d4d057acad13bf4855d358426 Mon Sep 17 00:00:00 2001 From: henrikmv <110386561+henrikmv@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:57:15 +0100 Subject: [PATCH 4/5] fix: [DHIS2-18632] Sorting stage detail table on orgunit breaks the app (#3917) * fix: converting org unit and sorting * fix: review comment * fix: wrong sort direction for text * fix: remove console log --- .../StageDetail/StageDetail.component.js | 3 +-- .../Stages/Stage/StageDetail/hooks/helpers.js | 3 --- .../{sortFuntions.js => sortFunctions.js} | 19 ++++++++++--------- .../Stage/StageDetail/hooks/useEventList.js | 3 +-- .../metadataRetrieval/orgUnitName/index.js | 1 + .../orgUnitName/orgUnitName.js | 2 ++ 6 files changed, 15 insertions(+), 16 deletions(-) rename src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/{sortFuntions.js => sortFunctions.js} (91%) diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js index e36d5f4b16..4a02ddcd75 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js @@ -19,7 +19,7 @@ import { Tooltip, } from '@dhis2/ui'; import log from 'loglevel'; -import { sortDataFromEvent } from './hooks/sortFuntions'; +import { sortDataFromEvent } from './hooks/sortFunctions'; import { StageCreateNewButton } from '../StageCreateNewButton'; import { useComputeDataFromEvent, useComputeHeaderColumn, formatRowForView } from './hooks/useEventList'; import { DEFAULT_NUMBER_OF_ROW, SORT_DIRECTION } from './hooks/constants'; @@ -115,7 +115,6 @@ const StageDetailPlain = (props: Props) => { const headerColumns = useComputeHeaderColumn(dataElements, hideDueDate, enableUserAssignment, stage?.stageForm); const { loading, value: dataSource, error } = useComputeDataFromEvent(dataElements, events); - const [{ columnName, sortDirection }, setSortInstructions] = useState(defaultSortState); const [displayedRowNumber, setDisplayedRowNumber] = useState(DEFAULT_NUMBER_OF_ROW); diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js index 9d7f31f63f..f80831a9a7 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/helpers.js @@ -9,7 +9,6 @@ import type { StageDataElement } from '../../../../types/common.types'; import { Notes } from '../Notes.component'; import type { QuerySingleResource } from '../../../../../../utils/api/api.types'; import { isEventOverdue } from '../../../../../../utils/isEventOverdue'; -import { TooltipOrgUnit } from '../../../../../Tooltips/TooltipOrgUnit/TooltipOrgUnit.component'; const getEventStatus = (event: ApiEnrollmentEvent) => { const today = moment().startOf('day'); @@ -63,7 +62,6 @@ const convertStatusForView = (event: ApiEnrollmentEvent) => { }; }; -const convertOrgUnitForView = (event: ApiEnrollmentEvent) => ; const convertNoteForView = (event: ApiEnrollmentEvent) => ; @@ -100,7 +98,6 @@ export { isEventOverdue, getEventStatus, convertStatusForView, - convertOrgUnitForView, convertNoteForView, getValueByKeyFromEvent, groupRecordsByType, diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/sortFuntions.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/sortFunctions.js similarity index 91% rename from src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/sortFuntions.js rename to src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/sortFunctions.js index c57e42ed0e..3da3a324fa 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/sortFuntions.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/sortFunctions.js @@ -1,6 +1,7 @@ // @flow import log from 'loglevel'; import { errorCreator } from 'capture-core-utils'; +import { getCachedOrgUnitName } from 'capture-core/metadataRetrieval/orgUnitName'; import moment from 'moment'; import { dataElementTypes } from '../../../../../../metaData'; import { SORT_DIRECTION } from './constants'; @@ -40,7 +41,7 @@ const sortText = (clientValueA: Object, clientValueB: Object, direction: string, if (!clientValueB) return -1; if (clientValueA !== clientValueB) { - return clientValueA.localeCompare(clientValueB); + return clientValueB.localeCompare(clientValueA); } return moment(eventDateB).unix() - moment(eventDateA).unix(); @@ -49,7 +50,7 @@ const sortText = (clientValueA: Object, clientValueB: Object, direction: string, if (!clientValueB) return 1; if (clientValueA !== clientValueB) { - return clientValueB.localeCompare(clientValueA); + return clientValueA.localeCompare(clientValueB); } return moment(eventDateB).unix() - moment(eventDateA).unix(); @@ -78,7 +79,12 @@ const sortTime = (clientValueA: Object, clientValueB: Object, direction: string, return 0; }; -const sortOrgUnit = (clientValueA: Object, clientValueB: Object, direction: string, options: Object) => sortText(clientValueA.name, clientValueB.name, direction, options); +const sortOrgUnit = (clientValueA: string, clientValueB: string, direction: string, options: Object) => { + const orgUnitNameA = getCachedOrgUnitName(clientValueA); + const orgUnitNameB = getCachedOrgUnitName(clientValueB); + + return sortText(orgUnitNameA, orgUnitNameB, direction, options); +}; // desc: Scheduled -> Active -> Completed -> Skipped const sortStatus = (clientValueA: Object, clientValueB: Object, direction: string, options: Object) => { @@ -113,7 +119,7 @@ const sortStatus = (clientValueA: Object, clientValueB: Object, direction: strin return 0; }; -const sortDataFromEvent = ({ dataA, dataB, type, columnName, direction }: Object) => { +export const sortDataFromEvent = ({ dataA, dataB, type, columnName, direction }: Object) => { if (!type) { log.error(errorCreator('Type is not defined')({ dataA, dataB })); } @@ -128,11 +134,6 @@ const sortDataFromEvent = ({ dataA, dataB, type, columnName, direction }: Object return sortForTypes[type](clientValueA, clientValueB, direction, options); }; -export { - sortDataFromEvent, -}; - - const sortForTypes = { [dataElementTypes.EMAIL]: sortText, [dataElementTypes.TEXT]: sortText, diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/useEventList.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/useEventList.js index f160eb00e3..d6fcf10be0 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/useEventList.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/hooks/useEventList.js @@ -11,7 +11,6 @@ import { convertValue as convertClientToList } from '../../../../../../converter import { convertValue as convertServerToClient } from '../../../../../../converters/serverToClient'; import { convertStatusForView, - convertOrgUnitForView, convertNoteForView, getValueByKeyFromEvent, groupRecordsByType, @@ -27,7 +26,7 @@ const basedFieldTypes = [ { type: dataElementTypes.STATUS, resolveValue: convertStatusForView }, { type: dataElementTypes.DATE }, { type: 'ASSIGNEE' }, - { type: dataElementTypes.TEXT, resolveValue: convertOrgUnitForView }, + { type: dataElementTypes.ORGANISATION_UNIT }, { type: dataElementTypes.DATE }, { type: dataElementTypes.UNKNOWN, resolveValue: convertNoteForView }, ]; diff --git a/src/core_modules/capture-core/metadataRetrieval/orgUnitName/index.js b/src/core_modules/capture-core/metadataRetrieval/orgUnitName/index.js index 204c983009..21e8b84212 100644 --- a/src/core_modules/capture-core/metadataRetrieval/orgUnitName/index.js +++ b/src/core_modules/capture-core/metadataRetrieval/orgUnitName/index.js @@ -3,4 +3,5 @@ export { useOrgUnitNameWithAncestors, useOrgUnitNames, getOrgUnitNames, + getCachedOrgUnitName, } from './orgUnitName'; diff --git a/src/core_modules/capture-core/metadataRetrieval/orgUnitName/orgUnitName.js b/src/core_modules/capture-core/metadataRetrieval/orgUnitName/orgUnitName.js index c86c4d7c14..56d222c119 100644 --- a/src/core_modules/capture-core/metadataRetrieval/orgUnitName/orgUnitName.js +++ b/src/core_modules/capture-core/metadataRetrieval/orgUnitName/orgUnitName.js @@ -209,3 +209,5 @@ export const useOrgUnitNameWithAncestors = (orgUnitId: ?string): { return { error }; }; + +export const getCachedOrgUnitName = (orgUnitId: string): ?string => displayNameCache[orgUnitId]?.displayName; From 5da612691342624007789d0d6befca608cca91ba Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Wed, 8 Jan 2025 10:01:38 +0000 Subject: [PATCH 5/5] chore(release): cut 101.21.1 [skip release] ## [101.21.1](https://github.com/dhis2/capture-app/compare/v101.21.0...v101.21.1) (2025-01-08) ### Bug Fixes * [DHIS2-18632] Sorting stage detail table on orgunit breaks the app ([#3917](https://github.com/dhis2/capture-app/issues/3917)) ([15414cc](https://github.com/dhis2/capture-app/commit/15414cc73f7ce74d4d057acad13bf4855d358426)) --- CHANGELOG.md | 7 +++++++ package.json | 4 ++-- packages/rules-engine/package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b144404cd3..9fad9e8b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [101.21.1](https://github.com/dhis2/capture-app/compare/v101.21.0...v101.21.1) (2025-01-08) + + +### Bug Fixes + +* [DHIS2-18632] Sorting stage detail table on orgunit breaks the app ([#3917](https://github.com/dhis2/capture-app/issues/3917)) ([15414cc](https://github.com/dhis2/capture-app/commit/15414cc73f7ce74d4d057acad13bf4855d358426)) + # [101.21.0](https://github.com/dhis2/capture-app/compare/v101.20.3...v101.21.0) (2025-01-07) diff --git a/package.json b/package.json index 9e3f58ba03..e6ed78a410 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.21.0", + "version": "101.21.1", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.21.0", + "@dhis2/rules-engine-javascript": "101.21.1", "@dhis2/app-runtime": "^3.9.3", "@dhis2/d2-i18n": "^1.1.0", "@dhis2/d2-icons": "^1.0.1", diff --git a/packages/rules-engine/package.json b/packages/rules-engine/package.json index df1868e4b6..c3b0df5cf7 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.21.0", + "version": "101.21.1", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": {