From 37c32df3e7839e30b493ff8e8185de769c3e2fd4 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Sun, 17 Nov 2024 02:45:01 +0100 Subject: [PATCH 1/8] fix(translations): sync translations from transifex (master) Automatically merged. --- i18n/es_419.po | 17 +++++++++++------ i18n/pt.po | 23 +++++++++++++---------- i18n/ru.po | 14 ++++++++------ i18n/zh_CN.po | 10 ++++++---- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/i18n/es_419.po b/i18n/es_419.po index c94ef95e6a..f996ede3ef 100644 --- a/i18n/es_419.po +++ b/i18n/es_419.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" +"POT-Creation-Date: 2024-10-25T18:18:11.518Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Jaime Bosque , 2024\n" "Language-Team: Spanish (Latin America) (https://app.transifex.com/hisp-uio/teams/100509/es_419/)\n" @@ -700,8 +700,10 @@ msgid "Add new enrollment for {{teiDisplayName}} in this program." msgstr "" "Agregue una nueva inscripción para {{teiDisplayName}} en este programa." -msgid "No access to program owner." -msgstr "Sin acceso al propietario del programa." +msgid "" +"You do not have permissions to access to this program, registering unit or " +"record, contact your administrator for more information." +msgstr "" msgid "{{teiDisplayName}} is not enrolled in this program." msgstr "{{teiDisplayName}} no está inscrito en este programa." @@ -1098,7 +1100,7 @@ msgstr "" msgid "Assigned to" msgstr "" -msgid "You don't have access to edit this assignee" +msgid "You don't have access to edit the assigned user" msgstr "" msgid "Edit" @@ -1107,7 +1109,7 @@ msgstr "" msgid "No one is assigned to this event" msgstr "No hay nadie asignado a este evento." -msgid "You don't have access to assign an assignee" +msgid "You don't have access to assign a user to this event" msgstr "" msgid "Assign" @@ -1153,6 +1155,9 @@ msgstr "" msgid "Mark incomplete" msgstr "" +msgid "You do not have access to delete this enrollment" +msgstr "" + msgid "Delete enrollment" msgstr "" @@ -1534,7 +1539,7 @@ msgid "Change" msgstr "" msgid "Value" -msgstr "" +msgstr "Valor" msgid "New {{trackedEntityTypeName}} relationship" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 1a05f44f6b..7c9dc3090d 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -11,13 +11,14 @@ # Jason Pickering , 2024 # Helton Dias, 2024 # Shelsea Chumaio, 2024 +# Laurência Luís, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-25T18:18:11.518Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Shelsea Chumaio, 2024\n" +"Last-Translator: Laurência Luís, 2024\n" "Language-Team: Portuguese (https://app.transifex.com/hisp-uio/teams/100509/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -340,7 +341,7 @@ msgstr "" msgid "" "Would you like to complete the enrollment and all active events as well?" -msgstr "" +msgstr "Gostaria de completar a inscrição e todos os eventos activos?" msgid "{{count}} event in {{programStageName}}" msgid_plural "{{count}} event in {{programStageName}}" @@ -349,13 +350,13 @@ msgstr[1] "" msgstr[2] "" msgid "Yes, complete enrollment and events" -msgstr "" +msgstr "Sim, completar a inscrição e os eventos" msgid "Complete enrollment only" -msgstr "" +msgstr "Completar apenas a inscrição" msgid "Would you like to complete the enrollment?" -msgstr "" +msgstr "Gostaria de completar a inscrição?" msgid "Complete enrollment" msgstr "Completar inscrição" @@ -391,7 +392,7 @@ msgid "validation failed" msgstr "falha na validação" msgid "No feedback for this event yet" -msgstr "" +msgstr "Ainda não há feedback para este evento" msgid "No indicator output for this event yet" msgstr "" @@ -701,8 +702,10 @@ msgstr "Não há inscrições ativas." msgid "Add new enrollment for {{teiDisplayName}} in this program." msgstr "Adicione uma nova inscrição para {{teiDisplayName}} neste programa." -msgid "No access to program owner." -msgstr "Sem acesso ao proprietário do programa." +msgid "" +"You do not have permissions to access to this program, registering unit or " +"record, contact your administrator for more information." +msgstr "" msgid "{{teiDisplayName}} is not enrolled in this program." msgstr "{{teiDisplayName}} não está inscrito neste programa." @@ -1107,7 +1110,7 @@ msgstr "" msgid "Assigned to" msgstr "Atribuído a" -msgid "You don't have access to edit this assignee" +msgid "You don't have access to edit the assigned user" msgstr "" msgid "Edit" @@ -1116,7 +1119,7 @@ msgstr "Editar" msgid "No one is assigned to this event" msgstr "Ninguém está atribuído a este evento" -msgid "You don't have access to assign an assignee" +msgid "You don't have access to assign a user to this event" msgstr "" msgid "Assign" diff --git a/i18n/ru.po b/i18n/ru.po index 23d7e47cd1..0d16b78d76 100644 --- a/i18n/ru.po +++ b/i18n/ru.po @@ -701,8 +701,10 @@ msgstr "" "Создать новую регистрационную запись для объекта {{teiDisplayName}} в данной" " программе." -msgid "No access to program owner." -msgstr "Нет доступа к владельцу программы." +msgid "" +"You do not have permissions to access to this program, registering unit or " +"record, contact your administrator for more information." +msgstr "" msgid "{{teiDisplayName}} is not enrolled in this program." msgstr "Объект {{teiDisplayName}} не зарегистрирован в данной программе." @@ -1106,8 +1108,8 @@ msgstr "Нет доступных отслеживаемых типов объе msgid "Assigned to" msgstr "Назначено пользователю" -msgid "You don't have access to edit this assignee" -msgstr "У вас нет доступа к редактированию данного назначенного пользователя" +msgid "You don't have access to edit the assigned user" +msgstr "" msgid "Edit" msgstr "Редактировать" @@ -1115,8 +1117,8 @@ msgstr "Редактировать" msgid "No one is assigned to this event" msgstr "Никто не назначен для данного события" -msgid "You don't have access to assign an assignee" -msgstr "У вас нет доступа к назначению пользователя" +msgid "You don't have access to assign a user to this event" +msgstr "" msgid "Assign" msgstr "Назначить" diff --git a/i18n/zh_CN.po b/i18n/zh_CN.po index 8d01178792..6a0a80ee90 100644 --- a/i18n/zh_CN.po +++ b/i18n/zh_CN.po @@ -667,8 +667,10 @@ msgstr "没有活跃的注册。" msgid "Add new enrollment for {{teiDisplayName}} in this program." msgstr "在此计划中为 {{teiDisplayName}} 添加新注册。" -msgid "No access to program owner." -msgstr "无法访问程序所有者。" +msgid "" +"You do not have permissions to access to this program, registering unit or " +"record, contact your administrator for more information." +msgstr "" msgid "{{teiDisplayName}} is not enrolled in this program." msgstr "该项目未报名{{teiDisplayName}} 。" @@ -1055,7 +1057,7 @@ msgstr "" msgid "Assigned to" msgstr "指派到" -msgid "You don't have access to edit this assignee" +msgid "You don't have access to edit the assigned user" msgstr "" msgid "Edit" @@ -1064,7 +1066,7 @@ msgstr "编辑" msgid "No one is assigned to this event" msgstr "没有人被分配到的该事件" -msgid "You don't have access to assign an assignee" +msgid "You don't have access to assign a user to this event" msgstr "" msgid "Assign" From e51806412be1a03e61e6e2745d87f97f89d6e3db Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Sun, 17 Nov 2024 01:49:05 +0000 Subject: [PATCH 2/8] chore(release): cut 101.16.1 [skip release] ## [101.16.1](https://github.com/dhis2/capture-app/compare/v101.16.0...v101.16.1) (2024-11-17) ### Bug Fixes * **translations:** sync translations from transifex (master) ([37c32df](https://github.com/dhis2/capture-app/commit/37c32df3e7839e30b493ff8e8185de769c3e2fd4)) --- 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 8ab035123e..91bede54a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [101.16.1](https://github.com/dhis2/capture-app/compare/v101.16.0...v101.16.1) (2024-11-17) + + +### Bug Fixes + +* **translations:** sync translations from transifex (master) ([37c32df](https://github.com/dhis2/capture-app/commit/37c32df3e7839e30b493ff8e8185de769c3e2fd4)) + # [101.16.0](https://github.com/dhis2/capture-app/compare/v101.15.0...v101.16.0) (2024-11-13) diff --git a/package.json b/package.json index 07eb53096a..b54a12f078 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.16.0", + "version": "101.16.1", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.16.0", + "@dhis2/rules-engine-javascript": "101.16.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 d0b7bd5ae9..4526bc13fe 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.16.0", + "version": "101.16.1", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": { From 932721045126e02379f56a85af4f6586b836b4c0 Mon Sep 17 00:00:00 2001 From: henrikmv <110386561+henrikmv@users.noreply.github.com> Date: Tue, 19 Nov 2024 08:56:32 +0100 Subject: [PATCH 3/8] fix: [DHIS2-16994] Image and File DE and TEA not Displayed in Changelog (#3837) * feat: temp * fix: revert clienttolist changes * fix: code clean up * feat: temp * fix: wrong else statement * feat: add link for image and file data element * fix: image and file for tea * fix: show only latest image and file * fix: revert change * fix: update islatestvalue to check for fieldid * feat: temp * fix: caching * fix: ensure text utilizes space without overflow * fix: add try catch to all query calls * fix: use storagestatus to find latest value * fix: string improvement * Revert "fix: string improvement" This reverts commit d8a92bf7498f3e7e6a7ff7b6068a5f50f314f9ed. * Revert "fix: use storagestatus to find latest value" This reverts commit 877d48933879eadde14f74bf2eb480ffa6b3f42a. * feat: temp * feat: compare with event data to find latest value * feat: image and file for event and tracked entity * fix: performance * fix: review comments * fix: review comments * fix: latest value not shown * fix: missing question mark --- i18n/en.pot | 10 +- .../EventDetailsSection.component.js | 3 + .../EventDetailsSection.container.js | 1 + .../EventChangelogWrapper.component.js | 3 +- .../EventChangelogWrapper.types.js | 1 + .../WidgetEventEdit.container.js | 1 + .../OverflowMenu/OverflowMenu.component.js | 2 + .../OverflowMenu/OverflowMenu.container.js | 2 + .../OverflowMenu/OverflowMenu.types.js | 2 + ...TrackedEntityChangelogWrapper.component.js | 8 +- .../TrackedEntityChangelogWrapper.types.js | 1 + .../WidgetProfile/WidgetProfile.component.js | 1 + .../WidgetEventChangelog.js | 3 + .../WidgetTrackedEntityChangelog.js | 3 + .../common/Changelog/Changelog.container.js | 30 ++- .../ChangelogCells/ChangelogValueCell.js | 31 ++- .../ChangelogTable/ChangelogTableRow.js | 4 +- .../WidgetsChangelog/common/hooks/index.js | 1 + .../common/hooks/useChangelogData.js | 92 ++------- .../common/hooks/useListDataValues.js | 177 ++++++++++++++++++ .../utils/getSubValueForChangelogData.js | 148 +++++++++++++++ 21 files changed, 414 insertions(+), 110 deletions(-) create mode 100644 src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js create mode 100644 src/core_modules/capture-core/components/WidgetsChangelog/common/utils/getSubValueForChangelogData.js diff --git a/i18n/en.pot b/i18n/en.pot index 9abb22fa99..11c24b0478 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-11-04T18:45:47.626Z\n" -"PO-Revision-Date: 2024-11-04T18:45:47.626Z\n" +"POT-Creation-Date: 2024-11-07T11:57:59.094Z\n" +"PO-Revision-Date: 2024-11-07T11:57:59.094Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1547,6 +1547,12 @@ msgstr "Change" msgid "Value" msgstr "Value" +msgid "File" +msgstr "File" + +msgid "Image" +msgstr "Image" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "New {{trackedEntityTypeName}} relationship" diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.js b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.js index 70ee627719..30af8fca67 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.js +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.js @@ -56,6 +56,7 @@ const getStyles = () => ({ type Props = { showEditEvent: ?boolean, eventId: string, + eventData: Object, onOpenEditEvent: (orgUnit: Object, programCategory: ?ProgramCategory) => void, programStage: ProgramStage, eventAccess: { read: boolean, write: boolean }, @@ -76,6 +77,7 @@ const EventDetailsSectionPlain = (props: Props) => { const { classes, eventId, + eventData, onOpenEditEvent, showEditEvent, programStage, @@ -200,6 +202,7 @@ const EventDetailsSectionPlain = (props: Props) => { diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.js b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.js index e57972d6bc..46c97da75d 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.js +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.js @@ -10,6 +10,7 @@ import type { ProgramCategory } from '../../../WidgetEventSchedule/CategoryOptio const mapStateToProps = (state: ReduxState) => ({ showEditEvent: state.viewEventPage.eventDetailsSection && state.viewEventPage.eventDetailsSection.showEditEvent, eventId: state.viewEventPage.eventId, + eventData: state.viewEventPage.loadedValues?.eventContainer?.values || {}, programId: state.currentSelections.programId, }); 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 d9f20c2e66..e7041bdd74 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 @@ -5,7 +5,7 @@ import { dataElementTypes } from '../../../metaData'; import type { Props } from './EventChangelogWrapper.types'; import { WidgetEventChangelog } from '../../WidgetsChangelog'; -export const EventChangelogWrapper = ({ formFoundation, eventId, ...passOnProps }: Props) => { +export const EventChangelogWrapper = ({ formFoundation, eventId, eventData, ...passOnProps }: Props) => { const dataItemDefinitions = useMemo(() => { const elements = formFoundation.getElements(); const contextLabels = formFoundation.getLabels(); @@ -52,6 +52,7 @@ export const EventChangelogWrapper = ({ formFoundation, eventId, ...passOnProps ); diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.js b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.js index 274891017e..58cb1d981f 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.js +++ b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.js @@ -10,4 +10,5 @@ type PassOnProps = {| export type Props = { ...PassOnProps, formFoundation: RenderFoundation, + eventData: Object, }; diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js index 075855e6cd..2a6cb8be80 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js @@ -236,6 +236,7 @@ export const WidgetEventEditPlain = ({ isOpen setIsOpen={setChangeLogIsOpen} eventId={loadedValues.eventContainer.id} + eventData={loadedValues.eventContainer.values} formFoundation={formFoundation} /> )} diff --git a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.component.js b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.component.js index e711f1a1f7..faf83dfe91 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.component.js +++ b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.component.js @@ -9,6 +9,7 @@ import { TrackedEntityChangelogWrapper } from './TrackedEntityChangelogWrapper'; export const OverflowMenuComponent = ({ trackedEntity, + trackedEntityData, trackedEntityTypeName, canWriteData, canCascadeDeleteTei, @@ -68,6 +69,7 @@ export const OverflowMenuComponent = ({ programAPI={programAPI} isOpen={changelogIsOpen} setIsOpen={setChangelogIsOpen} + trackedEntityData={trackedEntityData} /> )} diff --git a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.container.js b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.container.js index 031f7fd462..38d17ad0c0 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.container.js +++ b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.container.js @@ -8,6 +8,7 @@ export const OverflowMenu = ({ trackedEntityTypeName, canWriteData, trackedEntity, + trackedEntityData, onDeleteSuccess, displayChangelog, teiId, @@ -21,6 +22,7 @@ export const OverflowMenu = ({ canWriteData={canWriteData} canCascadeDeleteTei={hasAuthority} trackedEntity={trackedEntity} + trackedEntityData={trackedEntityData} onDeleteSuccess={onDeleteSuccess} displayChangelog={displayChangelog} teiId={teiId} diff --git a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.types.js b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.types.js index ad3cc687d4..84ea57e86a 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.types.js +++ b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/OverflowMenu.types.js @@ -3,6 +3,7 @@ export type Props = {| trackedEntity: { trackedEntity: string }, trackedEntityTypeName: string, + trackedEntityData: Object, canWriteData: boolean, onDeleteSuccess?: () => void, displayChangelog: boolean, @@ -13,6 +14,7 @@ export type Props = {| export type PlainProps = {| trackedEntity: { trackedEntity: string }, trackedEntityTypeName: string, + trackedEntityData: Object, canWriteData: boolean, canCascadeDeleteTei: boolean, onDeleteSuccess?: () => void, diff --git a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.js b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.js index 1dc3172af6..27db89634d 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.js +++ b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.js @@ -5,9 +5,14 @@ import { useFormFoundation } from '../../DataEntry/hooks'; import { WidgetTrackedEntityChangelog } from '../../../WidgetsChangelog'; import type { Props } from './TrackedEntityChangelogWrapper.types'; -export const TrackedEntityChangelogWrapper = ({ programAPI, teiId, setIsOpen, ...passOnProps }: Props) => { +export const TrackedEntityChangelogWrapper = ({ programAPI, teiId, setIsOpen, trackedEntityData, ...passOnProps }: Props) => { const formFoundation: RenderFoundation = useFormFoundation(programAPI); + const transformedTrackedEntityData = trackedEntityData.reduce((acc, item) => { + acc[item.attribute] = item.value; + return acc; + }, {}); + const dataItemDefinitions = useMemo(() => { if (!Object.keys(formFoundation)?.length) return {}; const elements = formFoundation.getElements(); @@ -58,6 +63,7 @@ export const TrackedEntityChangelogWrapper = ({ programAPI, teiId, setIsOpen, .. close={() => setIsOpen(false)} programId={programAPI.id} dataItemDefinitions={dataItemDefinitions} + trackedEntityData={transformedTrackedEntityData} /> ); }; diff --git a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.types.js b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.types.js index e0bb62461f..c2c5d726f4 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.types.js +++ b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.types.js @@ -5,6 +5,7 @@ type PassOnProps = {| teiId: string, isOpen: boolean, setIsOpen: (boolean | boolean => boolean) => void, + trackedEntityData: Object, |} export type Props = { diff --git a/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js b/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js index 0204ac19a7..f63800dd43 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js +++ b/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.js @@ -162,6 +162,7 @@ const WidgetProfilePlain = ({ trackedEntity={trackedEntity} onDeleteSuccess={onDeleteSuccess} displayChangelog={displayChangelog} + trackedEntityData={clientAttributesWithSubvalues} teiId={teiId} programAPI={program} /> diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.js b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.js index 86137d295e..aabb240e4a 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.js @@ -5,6 +5,7 @@ import { Changelog, CHANGELOG_ENTITY_TYPES } from '../common/Changelog'; type Props = { eventId: string, + eventData: Object, dataItemDefinitions: ItemDefinitions, isOpen: boolean, setIsOpen: (boolean | boolean => boolean) => void, @@ -12,6 +13,7 @@ type Props = { export const WidgetEventChangelog = ({ eventId, + eventData, setIsOpen, ...passOnProps }: Props) => ( @@ -19,6 +21,7 @@ export const WidgetEventChangelog = ({ {...passOnProps} close={() => setIsOpen(false)} entityId={eventId} + entityData={eventData} entityType={CHANGELOG_ENTITY_TYPES.EVENT} /> ); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.js b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.js index 86e941142c..a28370ea35 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.js @@ -9,17 +9,20 @@ type Props = { dataItemDefinitions: ItemDefinitions, isOpen: boolean, close: () => void, + trackedEntityData: Object, } export const WidgetTrackedEntityChangelog = ({ teiId, programId, close, + trackedEntityData, ...passOnProps }: Props) => ( , isOpen: boolean, close: () => void, dataItemDefinitions: ItemDefinitions, programId?: string, -} +}; export const Changelog = ({ entityId, + entityData, entityType, programId, isOpen, @@ -25,9 +27,11 @@ export const Changelog = ({ dataItemDefinitions, }: Props) => { const { - records, + rawRecords, pager, - isLoading, + isLoading: isChangelogLoading, + page, + pageSize, setPage, setPageSize, sortDirection, @@ -36,10 +40,24 @@ export const Changelog = ({ entityId, entityType, programId, + }); + + const { + processedRecords, + isLoading: isProcessingLoading, + } = useListDataValues({ + rawRecords, dataItemDefinitions, + entityId, + entityData, + entityType, + programId, + sortDirection, + page, + pageSize, }); - if (isLoading) { + if (isChangelogLoading || isProcessingLoading) { return ( @@ -51,7 +69,7 @@ export const Changelog = ({ (
- {previousValue} - - {currentValue} +
{previousValue}
+
+
{currentValue}
); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogTableRow.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogTableRow.js index e93571cbf2..3169194fc1 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogTableRow.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/ChangelogTable/ChangelogTableRow.js @@ -13,13 +13,11 @@ type Props = { }, classes: { dataItemColumn: string, - valueColumn: string, }, }; const styles = { dataItemColumn: { wordWrap: 'break-word', hyphens: 'auto' }, - valueColumn: { wordWrap: 'break-word' }, }; const ChangelogTableRowPlain = ({ record, classes }: Props) => ( @@ -28,7 +26,7 @@ const ChangelogTableRowPlain = ({ record, classes }: Props) => ( {record.user} {record.dataItemLabel} - + ); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js index 1cc9b74cc8..7d2911cd6d 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.js @@ -1,3 +1,4 @@ // @flow export { useChangelogData } from './useChangelogData'; +export { useListDataValues } from './useListDataValues'; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.js index bcf1c82a54..f9562cf4ac 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.js +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.js @@ -1,48 +1,31 @@ // @flow -import moment from 'moment'; -import { v4 as uuid } from 'uuid'; -import log from 'loglevel'; -import { errorCreator } from 'capture-core-utils'; -import { useMemo, useState } from 'react'; -import { useTimeZoneConversion } from '@dhis2/app-runtime'; +import { useState } from 'react'; import { useApiDataQuery } from '../../../../utils/reactQueryHelpers'; -import { CHANGELOG_ENTITY_TYPES, QUERY_KEYS_BY_ENTITY_TYPE } from '../Changelog/Changelog.constants'; -import type { Change, ChangelogRecord, ItemDefinitions, SortDirection } from '../Changelog/Changelog.types'; -import { convertServerToClient } from '../../../../converters'; -import { convert } from '../../../../converters/clientToList'; +import { + CHANGELOG_ENTITY_TYPES, + QUERY_KEYS_BY_ENTITY_TYPE, +} from '../Changelog/Changelog.constants'; +import type { + SortDirection, +} from '../Changelog/Changelog.types'; type Props = { entityId: string, programId?: string, entityType: $Values, - dataItemDefinitions: ItemDefinitions, -} +}; const DEFAULT_PAGE_SIZE = 10; const DEFAULT_SORT_DIRECTION = 'default'; -const getMetadataItemDefinition = ( - elementKey: string, - change: Change, - dataItemDefinitions: ItemDefinitions, -) => { - const { dataElement, attribute } = change; - const fieldId = dataElement ?? attribute; - const metadataElement = fieldId ? dataItemDefinitions[fieldId] : dataItemDefinitions[elementKey]; - - return { metadataElement, fieldId }; -}; - export const useChangelogData = ({ entityId, entityType, programId, - dataItemDefinitions, }: Props) => { + const [sortDirection, setSortDirection] = useState(DEFAULT_SORT_DIRECTION); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE); - const [sortDirection, setSortDirection] = useState(DEFAULT_SORT_DIRECTION); - const { fromServerDate } = useTimeZoneConversion(); const handleChangePageSize = (newPageSize: number) => { setPage(1); @@ -50,7 +33,7 @@ export const useChangelogData = ({ }; const { data, isLoading, isError } = useApiDataQuery( - ['changelog', entityType, entityId, { sortDirection, page, pageSize, programId }], + ['changelog', entityType, entityId, 'rawData', { sortDirection, page, pageSize, programId }], { resource: `tracker/${QUERY_KEYS_BY_ENTITY_TYPE[entityType]}/${entityId}/changeLogs`, params: { @@ -67,62 +50,15 @@ export const useChangelogData = ({ }, ); - const records: ?Array = useMemo(() => { - if (!data) return undefined; - - return data.changeLogs.map((changelog) => { - const { change: apiChange, createdAt, createdBy } = changelog; - const elementKey = Object.keys(apiChange)[0]; - const change = apiChange[elementKey]; - - const { metadataElement, fieldId } = getMetadataItemDefinition( - elementKey, - change, - dataItemDefinitions, - ); - - if (!metadataElement) { - log.error(errorCreator('Could not find metadata for element')({ - ...changelog, - })); - return null; - } - - const { firstName, surname, username } = createdBy; - const { options } = metadataElement; - - const previousValue = convert( - convertServerToClient(change.previousValue, metadataElement.type), - metadataElement.type, - options, - ); - - const currentValue = convert( - convertServerToClient(change.currentValue, metadataElement.type), - metadataElement.type, - options, - ); - - return { - reactKey: uuid(), - date: moment(fromServerDate(createdAt)).format('YYYY-MM-DD HH:mm'), - user: `${firstName} ${surname} (${username})`, - dataItemId: fieldId, - changeType: changelog.type, - dataItemLabel: metadataElement.name, - previousValue, - currentValue, - }; - }).filter(Boolean); - }, [data, dataItemDefinitions, fromServerDate]); - return { - records, + rawRecords: data, pager: data?.pager, setPage, setPageSize: handleChangePageSize, sortDirection, setSortDirection, + page, + pageSize, isLoading, isError, }; 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 new file mode 100644 index 0000000000..e94bf94a88 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.js @@ -0,0 +1,177 @@ +// @flow +import { useMemo } from 'react'; +import log from 'loglevel'; +import { useTimeZoneConversion, useConfig, useDataEngine } from '@dhis2/app-runtime'; +import { useQuery } from 'react-query'; +import { errorCreator, buildUrl, pipe } from 'capture-core-utils'; +import { ReactQueryAppNamespace } from 'capture-core/utils/reactQueryHelpers'; +import { dataElementTypes } from '../../../../metaData'; +import { CHANGELOG_ENTITY_TYPES } from '../Changelog/Changelog.constants'; +import type { Change, ItemDefinitions, SortDirection } from '../Changelog/Changelog.types'; +import { convertServerToClient } from '../../../../converters'; +import { convert as convertClientToList } from '../../../../converters/clientToList'; +import { RECORD_TYPE, subValueGetterByElementType } from '../utils/getSubValueForChangelogData'; +import { makeQuerySingleResource } from '../../../../utils/api'; +import { attributeOptionsKey } from '../../../DataEntryDhis2Helpers'; + + +type Props = { + rawRecords: Object, + dataItemDefinitions: ItemDefinitions, + entityId: string, + entityData: Object, + entityType: $Values, + programId?: string, + sortDirection: SortDirection, + page: number, + pageSize: number, +}; + +const fetchFormattedValues = async ({ + rawRecords, + dataItemDefinitions, + entityId, + entityData, + entityType, + programId, + absoluteApiPath, + querySingleResource, + fromServerDate, +}) => { + if (!rawRecords) return []; + + const getMetadataItemDefinition = ( + elementKey: string, + change: Change, + ) => { + const fieldId = change.dataElement || change.attribute; + if (!fieldId) { + log.error('Could not find fieldId in change:', change); + return { metadataElement: null, fieldId: null }; + } + const metadataElement = dataItemDefinitions[fieldId]; + return { metadataElement, fieldId }; + }; + + + const fetchedRecords = await Promise.all( + rawRecords.changeLogs.map(async (changelog) => { + const { change: apiChange, createdAt, createdBy, type } = changelog; + const elementKey = Object.keys(apiChange)[0]; + const change = apiChange[elementKey]; + + const { metadataElement, fieldId } = getMetadataItemDefinition( + elementKey, + change, + ); + + if (!metadataElement) { + log.error( + errorCreator('Could not find metadata for element')({ ...changelog }), + ); + return null; + } + + const getSubValue = subValueGetterByElementType[RECORD_TYPE[entityType]]?.[metadataElement.type]; + + const getValue = async (value, latestValue) => { + if (!getSubValue) { + return convertServerToClient(value, metadataElement.type); + } + if (entityType === RECORD_TYPE.trackedEntity) { + return getSubValue({ + trackedEntity: { teiId: entityId, value }, + programId, + attributeId: fieldId, + absoluteApiPath, + querySingleResource, + latestValue, + }); + } + if (entityType === RECORD_TYPE.event) { + return getSubValue({ + dataElement: { id: fieldId, value }, + eventId: entityId, + absoluteApiPath, + querySingleResource, + latestValue, + }); + } + return null; + }; + + const [previousValueClient, currentValueClient] = await Promise.all([ + change.previousValue ? getValue(change.previousValue, false) : null, + getValue(change.currentValue, entityData?.[change.attribute ?? change.dataElement]?.value === change.currentValue), + ]); + + const { firstName, surname, username } = createdBy; + const { options } = metadataElement; + + const previousValue = convertClientToList(previousValueClient, metadataElement.type, options); + const currentValue = convertClientToList(currentValueClient, metadataElement.type, options); + + return { + reactKey: fieldId ? `${createdAt}-${fieldId}` : attributeOptionsKey, + date: pipe(convertServerToClient, convertClientToList)(fromServerDate(createdAt), dataElementTypes.DATETIME), + user: `${firstName} ${surname} (${username})`, + changeType: type, + dataItemLabel: metadataElement.name, + previousValue, + currentValue, + }; + }), + ); + + return fetchedRecords.filter(Boolean); +}; + +export const useListDataValues = ({ + rawRecords, + dataItemDefinitions, + entityId, + entityData, + entityType, + programId, + sortDirection, + page, + pageSize, +}: Props) => { + const dataEngine = useDataEngine(); + const { baseUrl, apiVersion } = useConfig(); + const { fromServerDate } = useTimeZoneConversion(); + const absoluteApiPath = buildUrl(baseUrl, `api/${apiVersion}`); + + const querySingleResource = useMemo( + () => makeQuerySingleResource(dataEngine.query.bind(dataEngine)), + [dataEngine], + ); + + const queryKey = [ReactQueryAppNamespace, 'changelog', entityType, entityId, 'formattedData', { sortDirection, page, pageSize, programId }]; + + const { data: processedRecords, isError, isLoading } = useQuery( + queryKey, + () => fetchFormattedValues({ + rawRecords, + dataItemDefinitions, + entityId, + entityData, + entityType, + programId, + absoluteApiPath, + querySingleResource, + fromServerDate, + }), + { + enabled: !!rawRecords && !!dataItemDefinitions && !!entityId && !!entityType, + staleTime: Infinity, + cacheTime: Infinity, + }, + ); + + return { + processedRecords, + isError, + isLoading, + }; +}; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/utils/getSubValueForChangelogData.js b/src/core_modules/capture-core/components/WidgetsChangelog/common/utils/getSubValueForChangelogData.js new file mode 100644 index 0000000000..85b13724e2 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/utils/getSubValueForChangelogData.js @@ -0,0 +1,148 @@ +// @flow +import log from 'loglevel'; +import i18n from '@dhis2/d2-i18n'; +import { errorCreator } from 'capture-core-utils'; +import { dataElementTypes } from '../../../../metaData'; +import type { QuerySingleResource } from '../../../../utils/api'; + +type SubValueTEAProps = { + trackedEntity: Object, + attributeId: string, + programId: string, + absoluteApiPath: string, + querySingleResource: QuerySingleResource, + latestValue?: boolean, +}; + +type SubValuesDataElementProps = { + dataElement: Object, + querySingleResource: QuerySingleResource, + eventId: string, + absoluteApiPath: string, + latestValue?: boolean, +}; + +const buildTEAUrlByElementType: {| +[string]: Function, +|} = { + [dataElementTypes.FILE_RESOURCE]: async ({ + trackedEntity, + attributeId, + programId, + absoluteApiPath, + querySingleResource, + latestValue, + }: SubValueTEAProps) => { + const { teiId, value } = trackedEntity; + if (!value) return null; + try { + if (!latestValue) { + return i18n.t('File'); + } + + const { id, displayName: name } = await querySingleResource({ resource: `fileResources/${value}` }); + + return { + id, + name, + url: `${absoluteApiPath}/tracker/trackedEntities/${teiId}/attributes/${attributeId}/file?program=${programId}`, + }; + } catch (error) { + log.error( + errorCreator('Error fetching file resource')({ error }), + ); + return null; + } + }, + [dataElementTypes.IMAGE]: async ({ + trackedEntity, + attributeId, + programId, + absoluteApiPath, + latestValue, + querySingleResource, + }: SubValueTEAProps) => { + const { teiId, value } = trackedEntity; + if (!value) return null; + try { + if (!latestValue) { + return i18n.t('Image'); + } + const { id, displayName: name } = await querySingleResource({ resource: `fileResources/${value}` }); + + return { + id, + name, + url: `${absoluteApiPath}/tracker/trackedEntities/${teiId}/attributes/${attributeId}/image?program=${programId}`, + previewUrl: `${absoluteApiPath}/tracker/trackedEntities/${teiId}/attributes/${attributeId}/image?program=${programId}&dimension=small`, + }; + } catch (error) { + log.error( + errorCreator('Error fetching image resource')({ error }), + ); + return null; + } + }, +}; + +const buildDataElementUrlByElementType: {| +[string]: Function, +|} = { + [dataElementTypes.FILE_RESOURCE]: async ({ dataElement, querySingleResource, eventId, absoluteApiPath, latestValue }: SubValuesDataElementProps) => { + const { id: dataElementId, value } = dataElement; + if (!value) return null; + + try { + if (!latestValue) { + return i18n.t('File'); + } + + const { id, displayName: name } = await querySingleResource({ resource: `fileResources/${value}` }); + + return { + id, + name, + url: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${dataElementId}/file`, + }; + } catch (error) { + log.error( + errorCreator('Error fetching file resource')({ error }), + ); + return null; + } + }, + [dataElementTypes.IMAGE]: async ({ dataElement, querySingleResource, eventId, absoluteApiPath, latestValue }: SubValuesDataElementProps) => { + const { id: dataElementId, value } = dataElement; + if (!value) return null; + + try { + if (!latestValue) { + return i18n.t('Image'); + } + + const { id, displayName: name } = await querySingleResource({ resource: `fileResources/${value}` }); + + return { + id, + name, + url: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${dataElementId}/image`, + previewUrl: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${dataElementId}/image?dimension=small`, + }; + } catch (error) { + log.error( + errorCreator('Error fetching image resource')({ error }), + ); + return null; + } + }, +}; + +export const RECORD_TYPE = Object.freeze({ + event: 'event', + trackedEntity: 'trackedEntity', +}); + +export const subValueGetterByElementType = Object.freeze({ + [RECORD_TYPE.trackedEntity]: buildTEAUrlByElementType, + [RECORD_TYPE.event]: buildDataElementUrlByElementType, +}); From 1e37cf4745254e62dda5b99c777e939cb4f42d93 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Tue, 19 Nov 2024 08:00:35 +0000 Subject: [PATCH 4/8] chore(release): cut 101.16.2 [skip release] ## [101.16.2](https://github.com/dhis2/capture-app/compare/v101.16.1...v101.16.2) (2024-11-19) ### Bug Fixes * [DHIS2-16994] Image and File DE and TEA not Displayed in Changelog ([#3837](https://github.com/dhis2/capture-app/issues/3837)) ([9327210](https://github.com/dhis2/capture-app/commit/932721045126e02379f56a85af4f6586b836b4c0)) --- 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 91bede54a0..bb83bc17af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [101.16.2](https://github.com/dhis2/capture-app/compare/v101.16.1...v101.16.2) (2024-11-19) + + +### Bug Fixes + +* [DHIS2-16994] Image and File DE and TEA not Displayed in Changelog ([#3837](https://github.com/dhis2/capture-app/issues/3837)) ([9327210](https://github.com/dhis2/capture-app/commit/932721045126e02379f56a85af4f6586b836b4c0)) + ## [101.16.1](https://github.com/dhis2/capture-app/compare/v101.16.0...v101.16.1) (2024-11-17) diff --git a/package.json b/package.json index b54a12f078..3af3dfcf5c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.16.1", + "version": "101.16.2", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.16.1", + "@dhis2/rules-engine-javascript": "101.16.2", "@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 4526bc13fe..f2b60b8805 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.16.1", + "version": "101.16.2", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": { From 5b5b477ed3a26c7eb04c4966802769fe973e1631 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Wed, 20 Nov 2024 10:35:20 +0100 Subject: [PATCH 5/8] fix: [DHIS2-18444] stabilize possible duplicate modal cypress test (#3886) --- cypress/e2e/NewPage/NewPage.js | 8 ++++---- .../TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/NewPage/NewPage.js b/cypress/e2e/NewPage/NewPage.js index 9ef6ab3440..a4a7579656 100644 --- a/cypress/e2e/NewPage/NewPage.js +++ b/cypress/e2e/NewPage/NewPage.js @@ -469,14 +469,14 @@ And('you fill the WHO RMNCH program registration form with its required unique v }); And('you fill the WHO RMNCH program registration form with its required values', () => { - cy.get('[data-test="capture-ui-input"]') - .eq(2) - .type('Ava'); - cy.get('[data-test="capture-ui-input"]') .eq(3) .type('Didriksson'); + cy.get('[data-test="capture-ui-input"]') + .eq(2) + .type('Ava'); + cy.get('[data-test="capture-ui-input"]') .eq(9) .type('1985-10-01') diff --git a/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature index 06eb0a2fb1..f79f4faf87 100644 --- a/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature +++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiBulkActions/TeiBulkActions.feature @@ -51,6 +51,8 @@ Feature: User facing tests for bulk actions on Tracked Entity working lists When you confirm 3 active enrollments successfully Then the bulk complete enrollments modal should close +#DHIS2-18447 +@skip Scenario: the user should be able to bulk complete enrollments without completing events Given you open the main page with Ngelehun and Malaria Case diagnosis context And you select row number 1 @@ -61,6 +63,8 @@ Feature: User facing tests for bulk actions on Tracked Entity working lists When you confirm 1 active enrollment without completing events successfully Then the bulk complete enrollments modal should close +#DHIS2-18447 +@skip Scenario: the user should be able to bulk delete enrollments Given you open the main page with Ngelehun and Malaria Case diagnosis context And you select the first 3 rows @@ -69,6 +73,8 @@ Feature: User facing tests for bulk actions on Tracked Entity working lists When you confirm deleting 3 enrollments Then the bulk delete enrollments modal should close +#DHIS2-18447 +@skip Scenario: the user should be able to bulk delete only active enrollments Given you open the main page with Ngelehun and Malaria Case diagnosis context And you select the first 3 rows From f7bed5249c2a1e43964da918121b1533c4dce651 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Wed, 20 Nov 2024 09:39:39 +0000 Subject: [PATCH 6/8] chore(release): cut 101.16.3 [skip release] ## [101.16.3](https://github.com/dhis2/capture-app/compare/v101.16.2...v101.16.3) (2024-11-20) ### Bug Fixes * [DHIS2-18444] stabilize possible duplicate modal cypress test ([#3886](https://github.com/dhis2/capture-app/issues/3886)) ([5b5b477](https://github.com/dhis2/capture-app/commit/5b5b477ed3a26c7eb04c4966802769fe973e1631)) --- 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 bb83bc17af..6d288debbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [101.16.3](https://github.com/dhis2/capture-app/compare/v101.16.2...v101.16.3) (2024-11-20) + + +### Bug Fixes + +* [DHIS2-18444] stabilize possible duplicate modal cypress test ([#3886](https://github.com/dhis2/capture-app/issues/3886)) ([5b5b477](https://github.com/dhis2/capture-app/commit/5b5b477ed3a26c7eb04c4966802769fe973e1631)) + ## [101.16.2](https://github.com/dhis2/capture-app/compare/v101.16.1...v101.16.2) (2024-11-19) diff --git a/package.json b/package.json index 3af3dfcf5c..ddb1f4697e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.16.2", + "version": "101.16.3", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.16.2", + "@dhis2/rules-engine-javascript": "101.16.3", "@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 f2b60b8805..245c8abb46 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.16.2", + "version": "101.16.3", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": { From 7ea2240b68408a0c4e8db624093c058f2b416584 Mon Sep 17 00:00:00 2001 From: Simona Domnisoru Date: Wed, 20 Nov 2024 15:19:25 +0100 Subject: [PATCH 7/8] fix: [DHIS2-18019] related stages UI tweaks (#3872) --- .../EnrollmentEditEventPageForm.js | 6 +- .../EnrollmentEditEventPageNavigation.js | 5 +- cypress/e2e/TopBarActions/TopBarActions.js | 2 +- .../WidgetAssignee/index.js | 3 +- .../WidgetEventNote/index.js | 4 +- .../WidgetsForEnrollmentAddEventPage.js | 4 +- .../WidgetsForEnrollmentDashboard.js | 4 +- i18n/en.pot | 20 +- .../PageLayout/DefaultPageLayout.constants.js | 4 - .../DefaultEnrollmentLayout.types.js | 1 - .../WidgetEventEditWrapper.js | 24 +- .../WidgetEventEdit.container.js | 227 +++++++----------- .../WidgetHeader/WidgetHeader.container.js | 129 ++++++++++ .../WidgetHeader/WidgetHeader.types.js | 16 ++ .../WidgetEventEdit/WidgetHeader/index.js | 2 + .../WidgetHeader/WidgetHeader.container.js | 76 ++++++ .../WidgetHeader/WidgetHeader.types.js | 17 ++ .../WidgetHeader/index.js | 2 + .../WidgetTwoEventWorkspace.container.js | 129 +++------- .../WidgetTwoEventWorkspace.types.js | 12 +- .../WidgetTwoEventWorkspaceWrapper.const.js | 5 + .../WidgetWrapper/WidgetWrapper.container.js | 74 ++++++ .../WidgetWrapper/WidgetWrapper.types.js | 15 ++ .../WidgetWrapper/index.js | 2 + .../WidgetTwoEventWorkspace/index.js | 1 + 25 files changed, 504 insertions(+), 280 deletions(-) create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.container.js create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.types.js create mode 100644 src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/index.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/index.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspaceWrapper.const.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.container.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.types.js create mode 100644 src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/index.js diff --git a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js index 3d94481898..93ab1beea1 100644 --- a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js +++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/EnrollmentEditEventPageForm.js @@ -61,8 +61,7 @@ Given(/^you land on the enrollment event page with selected (.*) by having typed When(/^the user clicks on the edit button/, () => cy .get('[data-test="widget-enrollment-event"]') - .find('[data-test="dhis2-uicore-button"]') - .eq(1) + .find('[data-test="widget-enrollment-event-edit-button"]') .click(), ); @@ -208,8 +207,7 @@ And('you open the Birth stage event', () => { Then('the edit button should be disabled', () => { cy.get('[data-test="widget-enrollment-event"]') - .find('[data-test="dhis2-uicore-button"]') - .eq(1) + .find('[data-test="widget-enrollment-event-edit-button"]') .should('be.disabled'); }); diff --git a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js index 3664b4c338..d97e01569c 100644 --- a/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js +++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/EnrollmentEditEventPageNavigation.js @@ -21,10 +21,7 @@ When('the user clicks the first second antenatal care visit event', () => { }); When(/^the user clicks the "Back to all stages and events" button/, () => - cy - .get('[data-test="widget-enrollment-event"]') - .find('[data-test="dhis2-uicore-button"]') - .eq(0) + cy.get('[data-test="enrollment-edit-event-back-button"]') .click(), ); diff --git a/cypress/e2e/TopBarActions/TopBarActions.js b/cypress/e2e/TopBarActions/TopBarActions.js index 6fb563ed38..624ca1ac82 100644 --- a/cypress/e2e/TopBarActions/TopBarActions.js +++ b/cypress/e2e/TopBarActions/TopBarActions.js @@ -6,7 +6,7 @@ Given(/^you land on a enrollment page domain by having typed (.*)$/, (url) => { }); When(/^the user clicks on the edit button/, () => - cy.get('[data-test="widget-enrollment-event"]').find('[data-test="dhis2-uicore-button"]').eq(1).click(), + cy.get('[data-test="widget-enrollment-event"]').find('[data-test="widget-enrollment-event-edit-button"]').click(), ); When('the user clicks the arrow button to see the dropdown', () => { diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js index 9f45c31e2f..07403f7566 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetAssignee/index.js @@ -12,8 +12,7 @@ When('you assign the user Geetha in the view mode', () => { When('you assign the user Tracker demo User in the edit mode', () => { cy .get('[data-test="widget-enrollment-event"]') - .find('[data-test="dhis2-uicore-button"]') - .eq(1) + .find('[data-test="widget-enrollment-event-edit-button"]') .click(); cy.get('[data-test="widget-assignee"]').within(() => { diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js index 8b0ae0e38e..aec7cc2c19 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventNote/index.js @@ -5,7 +5,9 @@ Then('the enrollment widget should be loaded', () => { }); When('you click edit mode', () => { - cy.contains('[data-test="dhis2-uicore-button"]', 'Edit event') + cy + .get('[data-test="widget-enrollment-event"]') + .find('[data-test="widget-enrollment-event-edit-button"]') .click(); cy.get('[data-test="widget-enrollment-event-edit"]').should('exist'); }); diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js index ebb0f1293a..24f07d7593 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/WidgetsForEnrollmentAddEventPage.js @@ -16,9 +16,7 @@ Then('you can assign a user when scheduling the event', () => { }); When(/^the user clicks the "Back to all stages and events" button/, () => - cy - .get('[data-test="widget-enrollment-event"]') - .contains('Back to all stages and events') + cy.get('[data-test="enrollment-edit-event-back-button"]') .click(), ); diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js index 81034e2507..96a583bd38 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/WidgetsForEnrollmentDashboard.js @@ -51,9 +51,7 @@ Then(/^the scope selector list contains the text (.*)$/, (name) => { }); When(/^the user clicks the "Back to all stages and events" button/, () => - cy - .get('[data-test="widget-enrollment-event"]') - .contains('Back to all stages and events') + cy.get('[data-test="enrollment-edit-event-back-button"]') .click(), ); diff --git a/i18n/en.pot b/i18n/en.pot index 11c24b0478..39bf445b7a 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -948,6 +948,9 @@ msgstr "Loading" msgid "An error occurred while loading the form" msgstr "An error occurred while loading the form" +msgid "Back to all stages and events" +msgstr "Back to all stages and events" + msgid "Possible duplicates found" msgstr "Possible duplicates found" @@ -1306,9 +1309,6 @@ msgstr "Event completed" msgid "The event cannot be edited after it has been completed" msgstr "The event cannot be edited after it has been completed" -msgid "Back to all stages and events" -msgstr "Back to all stages and events" - msgid "Notes about this event" msgstr "Notes about this event" @@ -1508,11 +1508,21 @@ msgstr "{{ scheduledEvents }} scheduled" msgid "Stages and Events" msgstr "Stages and Events" +msgid "View linked event" +msgstr "View linked event" + msgid "An error occurred while loading the widget." msgstr "An error occurred while loading the widget." -msgid "View linked event" -msgstr "View linked event" +msgid "Linked event" +msgstr "Linked event" + +msgid "" +"This {{stageName}} event is linked to a {{linkedStageName}} event. Review " +"the linked event details before entering data below" +msgstr "" +"This {{stageName}} event is linked to a {{linkedStageName}} event. Review " +"the linked event details before entering data below" msgid "Scheduled" msgstr "Scheduled" diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js index e4c0feb93d..0a8065a405 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/PageLayout/DefaultPageLayout.constants.js @@ -22,10 +22,6 @@ export const WidgetsForEnrollmentEventEdit: $ReadOnly<{ [key: string]: WidgetCon export const DefaultPageLayout: PageLayoutConfig = { leftColumn: [ - { - type: WidgetTypes.COMPONENT, - name: 'TwoEventWorkspace', - }, { type: WidgetTypes.COMPONENT, name: 'EditEventWorkspace', diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js index 922a2071fc..cbb1bea87a 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.types.js @@ -8,7 +8,6 @@ type DefaultComponents = 'QuickActions' | 'AssigneeWidget' | 'NewEventWorkspace' | 'EditEventWorkspace' - | 'TwoEventWorkspace' | 'EnrollmentNote' | 'EventNote' | 'TrackedEntityRelationship' diff --git a/src/core_modules/capture-core/components/Pages/common/WidgetEventEditWrapper/WidgetEventEditWrapper.js b/src/core_modules/capture-core/components/Pages/common/WidgetEventEditWrapper/WidgetEventEditWrapper.js index 2968d8b1ef..aa99aae4e4 100644 --- a/src/core_modules/capture-core/components/Pages/common/WidgetEventEditWrapper/WidgetEventEditWrapper.js +++ b/src/core_modules/capture-core/components/Pages/common/WidgetEventEditWrapper/WidgetEventEditWrapper.js @@ -1,5 +1,6 @@ // @flow import React from 'react'; +import { IconArrowLeft24, Button } from '@dhis2/ui'; import i18n from '@dhis2/d2-i18n'; import { pageStatuses } from '../../EnrollmentEditEvent/EnrollmentEditEventPage.constants'; import { IncompleteSelectionsMessage } from '../../../IncompleteSelectionsMessage'; @@ -16,6 +17,7 @@ export const WidgetEventEditWrapper = ({ pageStatus, ...passOnProps }: WidgetPro const { programId, stageId, + onGoBack, } = passOnProps; const { @@ -56,12 +58,20 @@ export const WidgetEventEditWrapper = ({ pageStatus, ...passOnProps }: WidgetPro } return ( - + <> +
+ +
+ + ); }; diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js index 2a6cb8be80..21b2e372ea 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js @@ -1,40 +1,41 @@ // @flow import React, { type ComponentType, useEffect, useState } from 'react'; import { dataEntryIds, dataEntryKeys } from 'capture-core/constants'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { - Button, + spacersNum, colors, - FlyoutMenu, - IconArrowLeft24, - IconEdit24, - IconMore16, - MenuItem, spacers, - spacersNum, } from '@dhis2/ui'; import { withStyles } from '@material-ui/core'; -import i18n from '@dhis2/d2-i18n'; -import { ConditionalTooltip } from 'capture-core/components/Tooltips/ConditionalTooltip'; -import { useAvailableProgramStages, useEnrollmentEditEventPageMode } from 'capture-core/hooks'; +import { useEnrollmentEditEventPageMode, useAvailableProgramStages } from 'capture-core/hooks'; import { useCoreOrgUnit } from 'capture-core/metadataRetrieval/coreOrgUnit'; -import type { ComponentProps, PlainProps } from './widgetEventEdit.types'; -import { startShowEditEventDataEntry } from './WidgetEventEdit.actions'; +import type { PlainProps, ComponentProps } from './widgetEventEdit.types'; import { Widget } from '../Widget'; import { EditEventDataEntry } from './EditEventDataEntry/'; import { ViewEventDataEntry } from './ViewEventDataEntry/'; import { LoadingMaskElementCenter } from '../LoadingMasks'; -import { NonBundledDhis2Icon } from '../NonBundledDhis2Icon'; -import { getProgramEventAccess } from '../../metaData'; -import { useCategoryCombinations } from '../DataEntryDhis2Helpers/AOC/useCategoryCombinations'; -import { OverflowButton } from '../Buttons'; import { EventChangelogWrapper } from './EventChangelogWrapper'; import { FEATURES, useFeature } from '../../../capture-core-utils'; import { inMemoryFileStore } from '../DataEntry/file/inMemoryFileStore'; -import { eventStatuses } from './constants/status.const'; -import { useAuthorities } from '../../utils/authority/useAuthorities'; +import { WidgetHeader } from './WidgetHeader'; +import { WidgetTwoEventWorkspace, WidgetTwoEventWorkspaceWrapperTypes } from '../WidgetTwoEventWorkspace'; const styles = { + container: { + backgroundColor: 'white', + borderRadius: 3, + borderStyle: 'solid', + borderColor: colors.grey400, + borderWidth: 1, + '& > div:nth-child(2)': { + margin: spacersNum.dp16, + borderRadius: 3, + borderStyle: 'solid', + borderColor: colors.grey400, + borderWidth: 1, + }, + }, header: { display: 'flex', alignItems: 'center', @@ -73,7 +74,6 @@ export const WidgetEventEditPlain = ({ initialScheduleDate, stage, formFoundation, - onGoBack, onCancelEditEvent, onHandleScheduleSave, onSaveExternal, @@ -90,146 +90,91 @@ export const WidgetEventEditPlain = ({ classes, }: PlainProps) => { useEffect(() => inMemoryFileStore.clear, []); - const dispatch = useDispatch(); const supportsChangelog = useFeature(FEATURES.changelogs); const { currentPageMode } = useEnrollmentEditEventPageMode(eventStatus); const { orgUnit, error } = useCoreOrgUnit(orgUnitId); const [changeLogIsOpen, setChangeLogIsOpen] = useState(false); - const [actionsIsOpen, setActionsIsOpen] = useState(false); // "Edit event"-button depends on loadedValues. Delay rendering component until loadedValues has been initialized. const loadedValues = useSelector(({ viewEventPage }) => viewEventPage.loadedValues); - const eventAccess = getProgramEventAccess(programId, stageId); - const { hasAuthority } = useAuthorities({ authorities: ['F_UNCOMPLETE_EVENT'] }); - const blockEntryForm = stage.blockEntryForm && !hasAuthority && eventStatus === eventStatuses.COMPLETED; - const disableEdit = !eventAccess?.write || blockEntryForm; - - const tooltipContent = blockEntryForm ? - i18n.t('The event cannot be edited after it has been completed') : - i18n.t('You don\'t have access to edit this event'); - const availableProgramStages = useAvailableProgramStages(stage, teiId, enrollmentId, programId); - const { programCategory } = useCategoryCombinations(programId); if (error) { return error.errorComponent; } - const { icon, name } = stage; return orgUnit && loadedValues ? ( -
-
- - - {currentPageMode === dataEntryKeys.VIEW && ( -
- - - - - {supportsChangelog && ( - setActionsIsOpen(prev => !prev)} - icon={} - small - secondary - dataTest={'widget-event-edit-overflow-button'} - component={( - - { - setChangeLogIsOpen(true); - setActionsIsOpen(false); - }} - /> - - )} - /> - )} -
- )} -
- - {icon && ( -
- +
+ ) : ( +
+
)} - {name} -
- } - noncollapsible - > - {currentPageMode === dataEntryKeys.VIEW ? ( -
-
- ) : ( -
- -
- )} - + + {supportsChangelog && changeLogIsOpen && ( { + useEffect(() => inMemoryFileStore.clear, []); + const dispatch = useDispatch(); + + const supportsChangelog = useFeature(FEATURES.changelogs); + const { currentPageMode } = useEnrollmentEditEventPageMode(eventStatus); + const [actionsIsOpen, setActionsIsOpen] = useState(false); + + const eventAccess = getProgramEventAccess(programId, stage.id); + const { hasAuthority } = useAuthorities({ authorities: ['F_UNCOMPLETE_EVENT'] }); + const blockEntryForm = stage.blockEntryForm && !hasAuthority && eventStatus === eventStatuses.COMPLETED; + const disableEdit = !eventAccess?.write || blockEntryForm; + + const tooltipContent = blockEntryForm + ? i18n.t('The event cannot be edited after it has been completed') + : i18n.t("You don't have access to edit this event"); + + const { programCategory } = useCategoryCombinations(programId); + + const { icon, name } = stage; + + return ( + <> + {icon && ( +
+ +
+ )} + {name} +
+ {currentPageMode === dataEntryKeys.VIEW && ( +
+ + + + + {supportsChangelog && ( + setActionsIsOpen(prev => !prev)} + icon={} + small + secondary + dataTest={'widget-event-edit-overflow-button'} + component={ + + { + setChangeLogIsOpen(true); + setActionsIsOpen(false); + }} + /> + + } + /> + )} +
+ )} +
+ + ); +}; + +export const WidgetHeader: ComponentType = withStyles(styles)(WidgetHeaderPlain); diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.types.js b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.types.js new file mode 100644 index 0000000000..df70f89e0e --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/WidgetHeader.types.js @@ -0,0 +1,16 @@ +// @flow +import type { OrgUnit } from '@dhis2/rules-engine-javascript'; +import { ProgramStage } from '../../../metaData'; + +export type PlainProps = {| + eventStatus?: string, + stage: ProgramStage, + programId: string, + orgUnit: OrgUnit, + setChangeLogIsOpen: (toggle: boolean) => void, +|}; + +export type Props = {| + ...PlainProps, + ...CssClasses, +|}; diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/index.js b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/index.js new file mode 100644 index 0000000000..efff17d3ce --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetHeader/index.js @@ -0,0 +1,2 @@ +// @flow +export { WidgetHeader } from './WidgetHeader.container'; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js new file mode 100644 index 0000000000..129dedd9ec --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.container.js @@ -0,0 +1,76 @@ +// @flow +import React, { type ComponentType, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { FlyoutMenu, IconMore16, MenuItem, spacersNum } from '@dhis2/ui'; +import i18n from '@dhis2/d2-i18n'; +import { withStyles } from '@material-ui/core/'; +import { OverflowButton } from '../../Buttons'; +import { buildUrlQueryString } from '../../../utils/routing'; +import { EnrollmentPageKeys } + from '../../Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.constants'; +import { NonBundledDhis2Icon } from '../../NonBundledDhis2Icon'; +import type { PlainProps, Props } from './WidgetHeader.types'; + +const styles = { + menu: { + marginLeft: 'auto', + }, + icon: { + marginRight: spacersNum.dp8, + }, +}; + + +const WidgetHeaderPlain = ({ linkedStage, linkedEvent, orgUnitId, currentPage, classes }: Props) => { + const [actionsIsOpen, setActionsIsOpen] = useState(false); + const { push } = useHistory(); + const { icon } = linkedStage; + return ( + <> + {icon && ( +
+ +
+ )} + {linkedStage.name} + {currentPage === EnrollmentPageKeys.VIEW_EVENT && ( +
+ setActionsIsOpen(prev => !prev)} + icon={} + small + secondary + dataTest={'widget-event-navigate-to-linked-event'} + component={ + + { + push( + `/enrollmentEventEdit?${buildUrlQueryString({ + eventId: linkedEvent.event, + orgUnitId, + })}`, + ); + setActionsIsOpen(false); + }} + /> + + } + /> +
+ )} + + ); +}; + +export const WidgetHeader: ComponentType = withStyles(styles)(WidgetHeaderPlain); + diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js new file mode 100644 index 0000000000..9a8b8d460f --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/WidgetHeader.types.js @@ -0,0 +1,17 @@ +// @flow +import { EnrollmentPageKeys } + from '../../Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.constants'; +import type { ProgramStage } from '../../../metaData'; + +export type PlainProps = {| + orgUnitId: string, + linkedEvent: { event: string }, + linkedStage: ProgramStage, + currentPage: $Values | string, +|}; + +export type Props = {| + ...PlainProps, + ...CssClasses, +|}; + diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/index.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/index.js new file mode 100644 index 0000000000..efff17d3ce --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetHeader/index.js @@ -0,0 +1,2 @@ +// @flow +export { WidgetHeader } from './WidgetHeader.container'; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js index a889aace6b..645f84064a 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.container.js @@ -1,55 +1,23 @@ // @flow -import React, { useState } from 'react'; -import { useHistory } from 'react-router-dom'; -import { colors, FlyoutMenu, IconMore16, MenuItem, spacersNum } from '@dhis2/ui'; +import React from 'react'; import i18n from '@dhis2/d2-i18n'; -import { withStyles } from '@material-ui/core/'; import type { Props } from './WidgetTwoEventWorkspace.types'; import { useMetadataForProgramStage } from '../DataEntries/common/ProgramStage/useMetadataForProgramStage'; import { Widget } from '../Widget'; import { useLinkedEventByOriginId } from './hooks/useLinkedEventByOriginId'; import { WidgetTwoEventWorkspaceComponent } from './WidgetTwoEventWorkspace.component'; -import { OverflowButton } from '../Buttons'; -import { buildUrlQueryString } from '../../utils/routing'; -import { - EnrollmentPageKeys, -} from '../Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.constants'; -import { NonBundledDhis2Icon } from '../NonBundledDhis2Icon'; import { useClientDataValues } from './hooks/useClientDataValues'; +import { WidgetWrapper } from './WidgetWrapper'; +import { WidgetHeader } from './WidgetHeader'; -const styles = { - menu: { - display: 'flex', - alignItems: 'center', - padding: spacersNum.dp8, - justifyContent: 'end', - background: colors.white, - borderTopLeftRadius: 3, - borderTopRightRadius: 3, - borderStyle: 'solid', - borderColor: colors.grey400, - borderWidth: 1, - borderBottomWidth: 0, - }, - header: { - display: 'flex', - alignItems: 'center', - padding: spacersNum.dp8, - }, - icon: { - marginRight: spacersNum.dp8, - }, -}; - -const WidgetTwoEventWorkspacePlain = ({ +export const WidgetTwoEventWorkspace = ({ eventId, programId, orgUnitId, currentPage, - classes, + stage, + type, }: Props) => { - const [actionsIsOpen, setActionsIsOpen] = useState(false); - const { push } = useHistory(); const { linkedEvent, dataValues, @@ -59,7 +27,7 @@ const WidgetTwoEventWorkspacePlain = ({ const { formFoundation, - stage, + stage: linkedStage, isLoading: isLoadingMetadata, isError: isMetadataError, } = useMetadataForProgramStage({ @@ -89,69 +57,34 @@ const WidgetTwoEventWorkspacePlain = ({ ); } - if (!linkedEvent || !formFoundation || !stage) { + if (!linkedEvent || !formFoundation || !linkedStage) { return null; } return ( -
- {currentPage === EnrollmentPageKeys.VIEW_EVENT && ( -
- setActionsIsOpen(prev => !prev)} - icon={} - small - secondary - dataTest={'widget-event-navigate-to-linked-event'} - component={( - - { - push(`/enrollmentEventEdit?${buildUrlQueryString({ - eventId: linkedEvent.event, - orgUnitId, - })}`); - setActionsIsOpen(false); - }} - /> - - )} + + } + noncollapsible + > + -
- )} - - - {stage.icon && ( -
- -
- )} - {stage.name} -
- } - noncollapsible - > - - - + + } + /> ); }; - -export const WidgetTwoEventWorkspace = withStyles( - styles, -)(WidgetTwoEventWorkspacePlain); diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js index 52aff39ded..c79a68c5d5 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspace.types.js @@ -2,13 +2,17 @@ import { EnrollmentPageKeys, } from '../Pages/common/EnrollmentOverviewDomain/EnrollmentPageLayout/DefaultEnrollmentLayout.constants'; +import { WidgetTwoEventWorkspaceWrapperTypes } from './index'; +import type { ProgramStage } from '../../metaData'; -type PlainProps = {| +export type Props = {| programId: string, eventId: string, orgUnitId: string, stageId: string, - currentPage: $Values, + currentPage: $Values | string, + stage?: ProgramStage, + type?: $Values, |} export type LinkedEvent = {| @@ -18,7 +22,3 @@ export type LinkedEvent = {| orgUnit: string, |} -export type Props = {| - ...PlainProps, - ...CssClasses, -|} diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspaceWrapper.const.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspaceWrapper.const.js new file mode 100644 index 0000000000..421d827ca5 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetTwoEventWorkspaceWrapper.const.js @@ -0,0 +1,5 @@ +// @flow + +export const WidgetTwoEventWorkspaceWrapperTypes = { + EDIT_EVENT: 'editEvent', +}; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.container.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.container.js new file mode 100644 index 0000000000..dcc0defdc9 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.container.js @@ -0,0 +1,74 @@ +// @flow +import React from 'react'; +import { colors, spacersNum, IconLink16 } from '@dhis2/ui'; +import i18n from '@dhis2/d2-i18n'; +import { withStyles } from '@material-ui/core/'; +import type { Props } from './WidgetWrapper.types'; +import { WidgetTwoEventWorkspaceWrapperTypes } from '../index'; + +const styles = { + container: { + width: 'fit-content', + marginBottom: '16px', + margin: '16px', + }, + header: { + display: 'flex', + alignItems: 'center', + paddingBottom: spacersNum.dp16, + fontWeight: 500, + fontSize: 16, + color: colors.grey800, + }, + referalResponse: { + padding: spacersNum.dp16, + backgroundColor: colors.blue100, + borderRadius: '3px', + }, + linkedEvent: { + color: colors.blue900, + verticalAlign: 'middle', + display: 'flex', + fontSize: '16px', + fontWeight: '500', + }, + icon: { + marginRight: spacersNum.dp8, + }, + decription: { + margin: `${spacersNum.dp8}px 0`, + }, +}; + +const WidgetWrapperPlain = ({ widget, type, stage, linkedStage, classes }: Props) => { + if (type === WidgetTwoEventWorkspaceWrapperTypes.EDIT_EVENT) { + return ( +
+
{stage?.name}
+
+
+ + + +
{i18n.t('Linked event')}
+
+
+ {i18n.t( + 'This {{stageName}} event is linked to a {{linkedStageName}} event. Review the linked event details before entering data below', + { + linkedStageName: linkedStage?.name, + stageName: stage?.name, + interpolation: { escapeValue: false }, + }, + )} +
+ {widget} +
+
+ ); + } + + return <>{widget}; +}; + +export const WidgetWrapper = withStyles(styles)(WidgetWrapperPlain); diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.types.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.types.js new file mode 100644 index 0000000000..ae72f04aa3 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/WidgetWrapper.types.js @@ -0,0 +1,15 @@ +// @flow +import { WidgetTwoEventWorkspaceWrapperTypes } from '../index'; +import type { ProgramStage } from '../../../metaData'; + +type PlainProps = {| + widget: any, + linkedStage: ProgramStage, + stage?: ProgramStage, + type?: $Values, +|}; + +export type Props = {| + ...PlainProps, + ...CssClasses, +|}; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/index.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/index.js new file mode 100644 index 0000000000..adb87f9498 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/WidgetWrapper/index.js @@ -0,0 +1,2 @@ +// @flow +export { WidgetWrapper } from './WidgetWrapper.container'; diff --git a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js index 27320f9e8c..b057d60b99 100644 --- a/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js +++ b/src/core_modules/capture-core/components/WidgetTwoEventWorkspace/index.js @@ -1,3 +1,4 @@ // @flow export { WidgetTwoEventWorkspace } from './WidgetTwoEventWorkspace.container'; +export { WidgetTwoEventWorkspaceWrapperTypes } from './WidgetTwoEventWorkspaceWrapper.const'; From 40431841d02ca250e3d3ff7ec16c6fa64789a5b8 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Wed, 20 Nov 2024 14:23:31 +0000 Subject: [PATCH 8/8] chore(release): cut 101.16.4 [skip release] ## [101.16.4](https://github.com/dhis2/capture-app/compare/v101.16.3...v101.16.4) (2024-11-20) ### Bug Fixes * [DHIS2-18019] related stages UI tweaks ([#3872](https://github.com/dhis2/capture-app/issues/3872)) ([7ea2240](https://github.com/dhis2/capture-app/commit/7ea2240b68408a0c4e8db624093c058f2b416584)) --- 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 6d288debbb..c43ded6dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [101.16.4](https://github.com/dhis2/capture-app/compare/v101.16.3...v101.16.4) (2024-11-20) + + +### Bug Fixes + +* [DHIS2-18019] related stages UI tweaks ([#3872](https://github.com/dhis2/capture-app/issues/3872)) ([7ea2240](https://github.com/dhis2/capture-app/commit/7ea2240b68408a0c4e8db624093c058f2b416584)) + ## [101.16.3](https://github.com/dhis2/capture-app/compare/v101.16.2...v101.16.3) (2024-11-20) diff --git a/package.json b/package.json index ddb1f4697e..110c81af35 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.16.3", + "version": "101.16.4", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.16.3", + "@dhis2/rules-engine-javascript": "101.16.4", "@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 245c8abb46..847005fb6f 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.16.3", + "version": "101.16.4", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": {