diff --git a/CHANGELOG.md b/CHANGELOG.md index 6549a1ce03..4ec9917046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,46 @@ +## [101.14.4](https://github.com/dhis2/capture-app/compare/v101.14.3...v101.14.4) (2024-10-29) + + +### Bug Fixes + +* [DHIS2-18228] Image Deleted on Update ([#3857](https://github.com/dhis2/capture-app/issues/3857)) ([4656864](https://github.com/dhis2/capture-app/commit/46568640482a3d6a610533573b1132c160329296)) + +## [101.14.3](https://github.com/dhis2/capture-app/compare/v101.14.2...v101.14.3) (2024-10-29) + + +### Bug Fixes + +* [DHIS2-17843] Disable delete enrollment button when user does not have authority ([#3859](https://github.com/dhis2/capture-app/issues/3859)) ([edee6d3](https://github.com/dhis2/capture-app/commit/edee6d350fb2672a3470ef4c282460b34fa726eb)) + +## [101.14.2](https://github.com/dhis2/capture-app/compare/v101.14.1...v101.14.2) (2024-10-27) + + +### Bug Fixes + +* **translations:** sync translations from transifex (master) ([57a7b83](https://github.com/dhis2/capture-app/commit/57a7b8300c841d043da84735ed32dfce0b64e25b)) + +## [101.14.1](https://github.com/dhis2/capture-app/compare/v101.14.0...v101.14.1) (2024-10-22) + + +### Bug Fixes + +* [DHIS2-16010] app crashes on invalid programid ([#3765](https://github.com/dhis2/capture-app/issues/3765)) ([9133a63](https://github.com/dhis2/capture-app/commit/9133a63e138517c633a192b5d51f39a43c57e327)) + +# [101.14.0](https://github.com/dhis2/capture-app/compare/v101.13.0...v101.14.0) (2024-10-21) + + +### Features + +* [DHIS2-17792] Org unit context in forms ([#3828](https://github.com/dhis2/capture-app/issues/3828)) ([c8ccf63](https://github.com/dhis2/capture-app/commit/c8ccf63e0cde97a190cec6248dc25a4a4d747646)) + +# [101.13.0](https://github.com/dhis2/capture-app/compare/v101.12.2...v101.13.0) (2024-10-21) + + +### Features + +* [DHIS2-16992] Fixed size for changelog modal and columns ([#3834](https://github.com/dhis2/capture-app/issues/3834)) ([3b021cf](https://github.com/dhis2/capture-app/commit/3b021cf5569338db5038d8ed231a810a0fa909f3)) +* [DHIS2-17991] Show orgUnit selector in Enter details now ([#3824](https://github.com/dhis2/capture-app/issues/3824)) ([bf2f1ca](https://github.com/dhis2/capture-app/commit/bf2f1cabebe4ddf100cb8054be0a4af2fbec3965)) + ## [101.12.2](https://github.com/dhis2/capture-app/compare/v101.12.1...v101.12.2) (2024-10-20) diff --git a/cypress/e2e/ScopeSelector/ScopeSelector.feature b/cypress/e2e/ScopeSelector/ScopeSelector.feature index a53da4790d..ec80a5d2b0 100644 --- a/cypress/e2e/ScopeSelector/ScopeSelector.feature +++ b/cypress/e2e/ScopeSelector/ScopeSelector.feature @@ -28,8 +28,6 @@ Feature: User uses the ScopeSelector to navigate When you select both org unit and program Malaria case registration Then you should see the table - # DHIS2-16010 - App crashes on invalid program id - @skip Scenario: Main page > Url with invalid program id Given you land on a main page with an invalid program id Then you should see error message diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js index ca5bef0b08..fb01545e49 100644 --- a/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js @@ -66,7 +66,7 @@ Then('the user sees the enrollment organisation unit', () => { cy.get('[data-test="widget-enrollment"]').within(() => { cy.get('[data-test="widget-enrollment-icon-orgunit"]').should('exist'); cy.get('[data-test="widget-enrollment-orgunit"]') - .contains('Started at: Ngelehun CHC') + .contains('Started at:Ngelehun CHC') .should('exist'); }); }); @@ -77,7 +77,7 @@ Then('the user sees the owner organisation unit', () => { 'exist', ); cy.get('[data-test="widget-enrollment-owner-orgunit"]') - .contains('Owned by: Ngelehun CHC') + .contains('Owned by:Ngelehun CHC') .should('exist'); }); }); @@ -232,7 +232,7 @@ Then(/^the user successfully transfers the enrollment/, () => { cy.get('[data-test="widget-enrollment"]').within(() => { cy.get('[data-test="widget-enrollment-owner-orgunit"]') - .contains('Owned by: Njandama MCHP') + .contains('Owned by:Njandama MCHP') .should('exist'); }); }); @@ -246,7 +246,7 @@ Then(/^the user types in (.*)/, (orgunit) => { Given(/^the enrollment owner organisation unit is (.*)/, (orgunit) => { cy.get('[data-test="widget-enrollment"]').within(() => { cy.get('[data-test="widget-enrollment-owner-orgunit"]') - .contains(`Owned by: ${orgunit}`) + .contains(`Owned by:${orgunit}`) .should('exist'); }); }); diff --git a/docs/developer/configure-a-capture-plugin.mdx b/docs/developer/configure-a-capture-plugin.mdx index dbf597972a..7f7ab36e39 100644 --- a/docs/developer/configure-a-capture-plugin.mdx +++ b/docs/developer/configure-a-capture-plugin.mdx @@ -1,5 +1,4 @@ import Logo from './resources/tracker-plugin-configurator-logo.png' -import TPCFormFieldDemoImg from './resources/tpc-form-field-demo.png' # Configuration @@ -11,7 +10,7 @@ This application is called __Tracker Plugin Configurator__ and is available in t The app is also installable from the App Management app in your DHIS2 instance. -Tracker Plugin Configurator +Tracker Plugin Configurator ### How to use the Tracker Plugin Configurator @@ -25,5 +24,5 @@ The app is also installable from the App Management app in your DHIS2 instance.
-Form Field Plugin +![Form field plugin](resources/tpc-form-field-demo.png) diff --git a/docs/developer/develop-a-capture-plugin.md b/docs/developer/develop-a-capture-plugin.md index 1205c186b5..6f109ce37f 100644 --- a/docs/developer/develop-a-capture-plugin.md +++ b/docs/developer/develop-a-capture-plugin.md @@ -5,7 +5,7 @@ description: Learn how to develop a plugin for the DHIS2 Capture app. id: develop-a-capture-plugin --- -To develop your own capture plugin, you need to follow these steps: +Developing a capture plugin is almost the same as developing a regular DHIS2 custom app. However, there are some differences in how you structure your plugin and how you configure it to work with the DHIS2 Capture app. These are outlined in the few steps below. ## Step 1: Create a new DHIS2 custom app @@ -52,6 +52,6 @@ To build your plugin, you can use the provided build script from `d2-app-scripts Run `yarn build` or (`d2-app-scripts build`) to build a production version of your plugin. After building your plugin, you can deploy it to your DHIS2 instance. -You can do this by uploading the ZIP file to the _App management_ app in your DHIS2 instance, or publishing it to the app hub. +You can do this by uploading the ZIP file to the _App management_ app in your DHIS2 instance, or publishing it to the App Hub. -See [Configuring a Capture plugin](/docs/capture-plugins/developer/configure-a-capture-plugin) for more information on how to configure your plugin in the DHIS2 Capture app. +See [Configuring a Capture plugin](configure-a-capture-plugin) for more information on how to configure your plugin in the DHIS2 Capture app. diff --git a/docs/developer/enrollment-plugins/manual-setup.mdx b/docs/developer/enrollment-plugins/manual-setup.mdx index bae6afbe0f..36c4443d5a 100644 --- a/docs/developer/enrollment-plugins/manual-setup.mdx +++ b/docs/developer/enrollment-plugins/manual-setup.mdx @@ -19,7 +19,7 @@ When booting the capture application, we will look in the dataStore for these ke We assume that you will already have these three keys under the `capture` namespace in the datastore. If you do not have them, please create them before proceeding. | Page | Key | -|-----------------------|-----------------------------| +| --------------------- | --------------------------- | | Enrollment Dashboard | `enrollmentOverviewLayout` | | Enrollment Add Event | `enrollmentEventNewLayout` | | Enrollment Edit Event | `enrollmentEventEditLayout` | @@ -37,10 +37,8 @@ You can also have different layouts for the three different enrollment pages. label={'Enrollment Dashboard'} default > - - {`{ +```json +{ "IpHINAT79UW": { "title": "Child Programme dashboard", "leftColumn": [ @@ -94,17 +92,15 @@ You can also have different layouts for the three different enrollment pages. } ] } -}`} - +} +``` - - {`{ +```json +{ "IpHINAT79UW": { "title": "Child Programme: Add Event", "leftColumn": [ @@ -150,17 +146,15 @@ You can also have different layouts for the three different enrollment pages. } ] } -}`} - +} +``` - - {`{ +```json +{ "IpHINAT79UW": { "title": "Child Programme: Edit Event", "leftColumn": [ @@ -215,7 +209,7 @@ You can also have different layouts for the three different enrollment pages. ] } }`} - +``` @@ -268,11 +262,8 @@ You can also extend the configurations to render your own custom plugins. You ca You would place this inside either the `leftColumn` or `rightColumn` array. - -{`"leftColumn": [ +```json title="capture/enrollmentEventEditLayout" +"leftColumn": [ { "type": "component", "name": "EditEventWorkspace" @@ -284,6 +275,5 @@ You would place this inside either the `leftColumn` or `rightColumn` array. } // highlight-end ], -`} - +``` diff --git a/docs/developer/form-field-plugins/developer-details.md b/docs/developer/form-field-plugins/developer-details.md index 86b30531d9..cce32947ce 100644 --- a/docs/developer/form-field-plugins/developer-details.md +++ b/docs/developer/form-field-plugins/developer-details.md @@ -4,11 +4,9 @@ sidebar_label: Developer details id: developer-details --- -Here are some details for developers who want to develop a form field plugin for the DHIS2 Capture app. - A form field plugin runs in a sandboxed environment, meaning it can only read and write data that it's been configured to have access to. -If the plugin is given access to a field, we provide any relevant values, metadata, and rules engine output for that field. -We also provide some context about the application state, mainly if the plugin is in view or edit mode, or if the form has been attempted submitted. +If the plugin is given access to a field, all relevant values, metadata, and rules engine output for that field is provided. +Also the context about the application state is provided, mainly if the plugin is in view or edit mode, or if the form has been attempted to be submitted. If a plugin tries to update a field that it has not been given access to, an appropriate error message will be displayed in the console. diff --git a/docs/developer/getting-started.md b/docs/developer/getting-started.md index 6fcd7ead9f..e25b294434 100644 --- a/docs/developer/getting-started.md +++ b/docs/developer/getting-started.md @@ -6,6 +6,10 @@ The plugin framework is currently an experimental feature and is subject to chan We are working on improving both the technology and the way you use it. We will provide more documentation and examples as we progress. ::: +:::info Supported versions +The plugin framework is supported in **DHIS2 version 40.5 and later**. +::: + ## What are plugins? Plugins are a way to extend the functionality of our core applications within the DHIS2 ecosystem. These plugins will act and feel like native widgets / components, and allow you to write custom code that will be injected into the core applications, diff --git a/i18n/cs.po b/i18n/cs.po index a280598f67..496af4cba9 100644 --- a/i18n/cs.po +++ b/i18n/cs.po @@ -719,9 +719,6 @@ msgstr "Zobrazit pracovní seznam v tomto programu." msgid "Page is missing required values from URL" msgstr "Na stránce chybí požadované hodnoty z adresy URL" -msgid "Program is not valid" -msgstr "Program není platný" - msgid "Org unit is not valid with current program" msgstr "Organizační jednotka není platná pro aktuální program" @@ -1238,6 +1235,12 @@ msgstr "Widget pro zápis nelze načíst. Prosím zkuste to znovu později" msgid "Follow-up" msgstr "Následovat" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Zrušeno" @@ -1442,13 +1445,6 @@ msgstr "{{ linkableStageLabel }} nemá žádné propojitelné události" msgid "Ambiguous relationships, contact system administrator" msgstr "Nejednoznačné vztahy, kontaktujte správce systému" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" -"V dalším kroku po vyplnění této stránky {{currentStageLabel}} zadejte údaje " -"{{linkableStageLabel}}." - msgid "Enter details now" msgstr "Zadejte nyní podrobnosti" @@ -1533,6 +1529,9 @@ msgstr "Seznam změn" msgid "No changes to display" msgstr "" +msgid "Updated" +msgstr "Aktualizováno" + msgid "Created" msgstr "Vytvořeno" @@ -1551,6 +1550,9 @@ msgstr "Datová položka" msgid "Change" msgstr "" +msgid "Value" +msgstr "" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "" diff --git a/i18n/en.pot b/i18n/en.pot index f502445e5e..138a2722ee 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -705,9 +705,6 @@ msgstr "View working list in this program." msgid "Page is missing required values from URL" msgstr "Page is missing required values from URL" -msgid "Program is not valid" -msgstr "Program is not valid" - msgid "Org unit is not valid with current program" msgstr "Org unit is not valid with current program" @@ -1136,6 +1133,9 @@ msgstr "Mark as cancelled" msgid "Mark incomplete" msgstr "Mark incomplete" +msgid "You do not have access to delete this enrollment" +msgstr "You do not have access to delete this enrollment" + msgid "Delete enrollment" msgstr "Delete enrollment" @@ -1224,6 +1224,12 @@ msgstr "Enrollment widget could not be loaded. Please try again later" msgid "Follow-up" msgstr "Follow-up" +msgid "Started at{{escape}}" +msgstr "Started at{{escape}}" + +msgid "Owned by{{escape}}" +msgstr "Owned by{{escape}}" + msgid "Cancelled" msgstr "Cancelled" diff --git a/i18n/es.po b/i18n/es.po index bfd046a3bf..62ee25e726 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -14,13 +14,14 @@ # Viktor Varland , 2024 # Marta Vila , 2024 # Juan M Alcantara Acosta , 2024 +# Manuel Silva , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Juan M Alcantara Acosta , 2024\n" +"Last-Translator: Manuel Silva , 2024\n" "Language-Team: Spanish (https://app.transifex.com/hisp-uio/teams/100509/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -743,9 +744,6 @@ msgstr "Ver lista de trabajo en este programa." msgid "Page is missing required values from URL" msgstr "A la página le faltan los valores obligatorios de la URL" -msgid "Program is not valid" -msgstr "El programa no es válido" - msgid "Org unit is not valid with current program" msgstr "La unidad organizativa no es válida con el programa actual" @@ -1276,6 +1274,12 @@ msgstr "" msgid "Follow-up" msgstr "Seguimiento" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Cancelar" @@ -1489,13 +1493,6 @@ msgid "Ambiguous relationships, contact system administrator" msgstr "" "Relaciones ambiguas, póngase en contacto con el administrador del sistema" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" -"Introduzca los detalles de {{linkableStageLabel}} en el siguiente paso " -"después de completar este {{currentStageLabel}}." - msgid "Enter details now" msgstr "Introducir datos ahora" @@ -1583,6 +1580,9 @@ msgstr "Registro de cambios" msgid "No changes to display" msgstr "No hay cambios para mostrar" +msgid "Updated" +msgstr "Actualizado" + msgid "Created" msgstr "Creado" @@ -1601,6 +1601,9 @@ msgstr "Elemento de datos" msgid "Change" msgstr "Cambio" +msgid "Value" +msgstr "" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "Nueva relación de {{trackedEntityTypeName}}" diff --git a/i18n/es_419.po b/i18n/es_419.po index f5afa49b7d..c94ef95e6a 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-09-02T11:08:16.281Z\n" +"POT-Creation-Date: 2024-10-14T14:53:34.553Z\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" @@ -635,54 +635,6 @@ msgstr "Aviso" msgid "Close the notice" msgstr "cerrar el aviso" -msgid "Use new Enrollment dashboard for {{programName}}" -msgstr "Usar el nuevo panel de inscripción para {{programName}}" - -msgid "Opt in for {{programName}}" -msgstr "Opt in para el programa {{programName}}" - -msgid "" -"By clicking opt-in below, you will start using the new enrollment dashboard " -"in the Capture app for this Tracker program. At the moment, there is certain" -" functionality from Tracker Capture that has not yet been added, including " -"relationship and referral functionality. The work on including this Tracker " -"functionality in Capture is ongoing and will be added in upcoming app " -"releases." -msgstr "" -"Al hacer clic en registrarse, comenzará a utilizar el nuevo panel de " -"inscripción en Capture para este programa. Por el momento, hay ciertas " -"funciones de la Tracker Capture que aún no se han agregado, incluida la " -"función de relación y referencia. El trabajo para incluir esta funcionalidad" -" Tracker en Capture está en curso y se agregará en próximos lanzamientos de " -"la aplicación." - -msgid "" -"The core team appreciates any feedback on this new functionality which is " -"currently being beta tested, please report any issues and feedback in the " -"DHIS2 JIRA project." -msgstr "" -"El equipo de desarrollo agradece cualquier comentario sobre esta nueva " -"funcionalidad que actualmente se está probando en versión beta. Informe " -"cualquier problema y comentario en el proyecto de JIRA de DHIS2." - -msgid "" -"Click the button below to opt-in to the new enrollment dashboard " -"functionality in the Capture app (beta) for this Tracker program for all " -"users." -msgstr "" -"Haga clic en el botón de abajo para optar por la nueva funcionalidad del " -"panel de inscripción en la aplicación Capture (beta) para este programa " -"Tracker para todos los usuarios." - -msgid "Yes, opt in" -msgstr "Sí, opt in" - -msgid "Stop using new Enrollment dashboard for {{programName}}" -msgstr "Deje de usar el nuevo panel de inscripción para {{programName}}" - -msgid "Opt out for {{programName}}" -msgstr "Dejar de probar la nueva app con el programa \"{{programName}}\"" - msgid "Enrollment with id \"{{enrollmentId}}\" does not exist" msgstr "La inscripción con id \"{{enrollmentId}}\" no existe" @@ -780,9 +732,6 @@ msgstr "Ver lista de trabajo en este programa." msgid "Page is missing required values from URL" msgstr "A la página le faltan los valores obligatorios de la URL" -msgid "Program is not valid" -msgstr "El programa no es válido" - msgid "Org unit is not valid with current program" msgstr "La unidad organizativa no es válida con el programa actual" @@ -1284,6 +1233,12 @@ msgstr "" msgid "Follow-up" msgstr "" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Cancelar" @@ -1474,11 +1429,6 @@ msgstr "" msgid "Ambiguous relationships, contact system administrator" msgstr "" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" - msgid "Enter details now" msgstr "" @@ -1562,6 +1512,9 @@ msgstr "" msgid "No changes to display" msgstr "" +msgid "Updated" +msgstr "" + msgid "Created" msgstr "" @@ -1580,6 +1533,9 @@ msgstr "" msgid "Change" msgstr "" +msgid "Value" +msgstr "" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 277a314448..3effbc2fc8 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -10,13 +10,14 @@ # Viktor Varland , 2024 # Yayra Gomado , 2024 # Bram Piot , 2024 +# Jason Pickering , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Bram Piot , 2024\n" +"Last-Translator: Jason Pickering , 2024\n" "Language-Team: French (https://app.transifex.com/hisp-uio/teams/100509/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -739,9 +740,6 @@ msgstr "Afficher la liste de travail dans ce programme." msgid "Page is missing required values from URL" msgstr "Des valeurs obligatoires ne figurent pas dans l'URL de la page" -msgid "Program is not valid" -msgstr "Le programme n'est pas valide" - msgid "Org unit is not valid with current program" msgstr "L'unité d'organisation n'est pas valide avec le programme actuel" @@ -1256,6 +1254,12 @@ msgstr "" msgid "Follow-up" msgstr "Suivi" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Annulé" @@ -1465,11 +1469,6 @@ msgstr "" msgid "Ambiguous relationships, contact system administrator" msgstr "" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" - msgid "Enter details now" msgstr "Saisir les détails maintenant" @@ -1554,6 +1553,9 @@ msgstr "" msgid "No changes to display" msgstr "" +msgid "Updated" +msgstr "Mis-à-jour" + msgid "Created" msgstr "Créé" @@ -1572,6 +1574,9 @@ msgstr "Item de données" msgid "Change" msgstr "Modifier" +msgid "Value" +msgstr "" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "" diff --git a/i18n/id.po b/i18n/id.po index c4fca90018..1cf72fa6a2 100644 --- a/i18n/id.po +++ b/i18n/id.po @@ -8,15 +8,15 @@ # ratih syabrina, 2024 # Yusuf Setiawan , 2024 # Guardian Sanjaya , 2024 -# Viktor Varland , 2024 # Aprisa Chrysantina , 2024 +# Viktor Varland , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Aprisa Chrysantina , 2024\n" +"Last-Translator: Viktor Varland , 2024\n" "Language-Team: Indonesian (https://app.transifex.com/hisp-uio/teams/100509/id/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -722,9 +722,6 @@ msgstr "Lihat daftar kerja dalam program ini." msgid "Page is missing required values from URL" msgstr "Halaman kehilangan nilai yang diutuhkan dari URL" -msgid "Program is not valid" -msgstr "Program tidak valid" - msgid "Org unit is not valid with current program" msgstr "" @@ -1227,6 +1224,12 @@ msgstr "Widget pendaftaran tidak dapat dimuat. Silakan coba lagi nanti" msgid "Follow-up" msgstr "Mengikuti" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Dibatalkan" @@ -1413,11 +1416,6 @@ msgstr "" msgid "Ambiguous relationships, contact system administrator" msgstr "" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" - msgid "Enter details now" msgstr "" @@ -1499,6 +1497,9 @@ msgstr "" msgid "No changes to display" msgstr "" +msgid "Updated" +msgstr "Diperbarui" + msgid "Created" msgstr "Dibuat" @@ -1517,6 +1518,9 @@ msgstr "Data item" msgid "Change" msgstr "" +msgid "Value" +msgstr "Nilai" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "" diff --git a/i18n/lo.po b/i18n/lo.po index 2f880d5d1f..4decb60044 100644 --- a/i18n/lo.po +++ b/i18n/lo.po @@ -1,18 +1,18 @@ # # Translators: # Philip Larsen Donnelly, 2022 -# Saysamone Sibounma, 2023 # Somkhit Bouavong , 2024 # Thuy Nguyen , 2024 # Viktor Varland , 2024 # Namwan Chanthavisouk, 2024 +# Saysamone Sibounma, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Namwan Chanthavisouk, 2024\n" +"Last-Translator: Saysamone Sibounma, 2024\n" "Language-Team: Lao (https://app.transifex.com/hisp-uio/teams/100509/lo/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -713,9 +713,6 @@ msgstr "ເບິ່ງລາຍຊື່ເຮັດວຽກຢູ່ໃນໂ msgid "Page is missing required values from URL" msgstr "ບໍ່ພົບໜ້ານີ້ ຕ້ອງການຄ່າຈາກ URL" -msgid "Program is not valid" -msgstr "ໂປຣແກຼມບໍ່ຖືກຕ້ອງ" - msgid "Org unit is not valid with current program" msgstr "ຫົວໜ່ວຍການຈັດຕັ້ງບໍ່ຖືກຕ້ອງກັບໂປແກຼມນີ້" @@ -1221,6 +1218,12 @@ msgstr "ບໍ່ສາມາດໂຫຼດລາຍການການລົງ msgid "Follow-up" msgstr "ຕິດຕາມ" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "ຍົກເລີກແລ້ວ" @@ -1414,13 +1417,6 @@ msgstr "{{ linkableStageLabel }} ບໍ່ມີເຫດການທີ່ສ msgid "Ambiguous relationships, contact system administrator" msgstr "ການເຊື່ອມໂຍງບໍ່ຊັດເຈນ, ຕິດຕໍ່ຜູ້ດູແລລະບົບ" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" -"ໃສ່ລາຍລະອຽດ {{linkableStageLabel}} ໃນຂັ້ນຕອນຕໍ່ໄປຫຼັງຈາກເຮັດສໍາເລັດ " -"{{currentStageLabel}} ນີ້." - msgid "Enter details now" msgstr "ປ້ອນລາຍລະອຽດ" @@ -1504,6 +1500,9 @@ msgstr "ບັນທຶກການປ່ຽນແປງ" msgid "No changes to display" msgstr "ບໍ່ມີການປ່ຽນແປງທີ່ຈະສະແດງ" +msgid "Updated" +msgstr "" + msgid "Created" msgstr "ສ້າງແລ້ວ" @@ -1522,6 +1521,9 @@ msgstr "ລາຍການຂໍ້ມູນ" msgid "Change" msgstr "ປ່ຽນ" +msgid "Value" +msgstr "ຄ່າ" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "ຄວາມສໍາພັນໃໝ່ {{trackedEntityTypeName}} " diff --git a/i18n/nb.po b/i18n/nb.po index 6ec77b1236..6b566e538a 100644 --- a/i18n/nb.po +++ b/i18n/nb.po @@ -1,15 +1,15 @@ # # Translators: # Karoline Tufte Lien , 2024 -# Caroline Hesthagen Holen , 2024 # Merethe Wollan Blisten, 2024 +# Caroline Hesthagen Holen , 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Merethe Wollan Blisten, 2024\n" +"Last-Translator: Caroline Hesthagen Holen , 2024\n" "Language-Team: Norwegian Bokmål (https://app.transifex.com/hisp-uio/teams/100509/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -721,9 +721,6 @@ msgstr "Se arbeidsliste i dette programmet." msgid "Page is missing required values from URL" msgstr "Siden mangler nødvendige verdier fra URL" -msgid "Program is not valid" -msgstr "Programmet er ikke gyldig" - msgid "Org unit is not valid with current program" msgstr "Organisasjonsenheten er ikke gyldig med gjeldende program" @@ -1242,6 +1239,12 @@ msgstr "Registreringsmodulen kunne ikke lastes inn. Prøv igjen senere" msgid "Follow-up" msgstr "Oppfølging" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Kansellert" @@ -1444,13 +1447,6 @@ msgstr "{{ linkableStageLabel }} har ingen hendelser som kan kobles til" msgid "Ambiguous relationships, contact system administrator" msgstr "Uklare relasjoner, kontakt din systemadministrator" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" -"Skriv inn {{linkableStageLabel}}detaljer i neste trinn etter du har fullført" -" dette {{currentStageLabel}}." - msgid "Enter details now" msgstr "Skriv inn detaljer " @@ -1536,6 +1532,9 @@ msgstr "Endringslogg" msgid "No changes to display" msgstr "Ingen endringer å vise" +msgid "Updated" +msgstr "Oppdatert" + msgid "Created" msgstr "Opprettet" @@ -1554,6 +1553,9 @@ msgstr "Datapunkt" msgid "Change" msgstr "Endre" +msgid "Value" +msgstr "Verdi" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "Ny {{trackedEntityTypeName}} relasjon" diff --git a/i18n/nl.po b/i18n/nl.po index 62c2f2c8f9..9f88c8b400 100644 --- a/i18n/nl.po +++ b/i18n/nl.po @@ -3,15 +3,15 @@ # Cherise Beek , 2021 # Yury Rogachev , 2021 # Philip Larsen Donnelly, 2024 -# Rica, 2024 # Charel van den Elsen, 2024 +# Rica, 2024 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" "POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Charel van den Elsen, 2024\n" +"Last-Translator: Rica, 2024\n" "Language-Team: Dutch (https://app.transifex.com/hisp-uio/teams/100509/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -730,9 +730,6 @@ msgstr "Bekijk werklijst in dit programma." msgid "Page is missing required values from URL" msgstr "Pagina mist vereiste waarden van URL" -msgid "Program is not valid" -msgstr "Programma is niet geldig" - msgid "Org unit is not valid with current program" msgstr "Organisatie-eenheid is niet geldig met het huidige programma" @@ -1250,6 +1247,12 @@ msgstr "" msgid "Follow-up" msgstr "Opvolgen" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "Geannuleerd" @@ -1453,11 +1456,6 @@ msgstr "" msgid "Ambiguous relationships, contact system administrator" msgstr "" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "" - msgid "Enter details now" msgstr "" @@ -1541,6 +1539,9 @@ msgstr "" msgid "No changes to display" msgstr "" +msgid "Updated" +msgstr "" + msgid "Created" msgstr "Gemaakt" @@ -1559,6 +1560,9 @@ msgstr "Gegevensitem" msgid "Change" msgstr "" +msgid "Value" +msgstr "Waarde" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "" diff --git a/i18n/zh.po b/i18n/zh.po index dcd850bd86..d18eb1bd0d 100644 --- a/i18n/zh.po +++ b/i18n/zh.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-09-02T11:08:16.281Z\n" +"POT-Creation-Date: 2024-10-14T14:53:34.553Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: easylin , 2024\n" "Language-Team: Chinese (https://app.transifex.com/hisp-uio/teams/100509/zh/)\n" @@ -610,44 +610,6 @@ msgstr "注意" msgid "Close the notice" msgstr "关闭通知" -msgid "Use new Enrollment dashboard for {{programName}}" -msgstr "为 {{programName}} 使用新的注册仪表板" - -msgid "Opt in for {{programName}}" -msgstr "选择加入 {{programName}}" - -msgid "" -"By clicking opt-in below, you will start using the new enrollment dashboard " -"in the Capture app for this Tracker program. At the moment, there is certain" -" functionality from Tracker Capture that has not yet been added, including " -"relationship and referral functionality. The work on including this Tracker " -"functionality in Capture is ongoing and will be added in upcoming app " -"releases." -msgstr "" -"通过单击下面的选择加入,您将开始使用 Capture 应用程序中的新注册仪表板来执行此跟踪项目。目前,Tracker Capture " -"的某些功能尚未添加,包括关系和推荐功能。在 Capture 中包含此 Tracker 功能的工作正在进行中,并将添加到即将发布的应用程序中。" - -msgid "" -"The core team appreciates any feedback on this new functionality which is " -"currently being beta tested, please report any issues and feedback in the " -"DHIS2 JIRA project." -msgstr "核心团队感谢对目前正在 Beta 测试的这个新功能的任何反馈,请报告 DHIS2 JIRA 项目中的任何问题和反馈。" - -msgid "" -"Click the button below to opt-in to the new enrollment dashboard " -"functionality in the Capture app (beta) for this Tracker program for all " -"users." -msgstr "单击下面的按钮,为所有用户选择加入此 Tracker 程序的 Capture 应用程序(测试版)中的新注册仪表板功能。" - -msgid "Yes, opt in" -msgstr "是的,选择加入" - -msgid "Stop using new Enrollment dashboard for {{programName}}" -msgstr "停止为 {{programName}} 使用新的注册信息中心" - -msgid "Opt out for {{programName}}" -msgstr "选择退出 {{programName}}" - msgid "Enrollment with id \"{{enrollmentId}}\" does not exist" msgstr "ID为“ {{enrollmentId}}”的报名不存在" @@ -736,9 +698,6 @@ msgstr "查看此项目中的工作清单。" msgid "Page is missing required values from URL" msgstr "页面缺少 URL 中的必需值" -msgid "Program is not valid" -msgstr "项目无效" - msgid "Org unit is not valid with current program" msgstr "机构对当前项目无效" @@ -1232,6 +1191,12 @@ msgstr "无法加载报名的小部件。请稍后再试" msgid "Follow-up" msgstr "后续" +msgid "Started at{{escape}}" +msgstr "" + +msgid "Owned by{{escape}}" +msgstr "" + msgid "Cancelled" msgstr "已取消" @@ -1420,11 +1385,6 @@ msgstr "{{ linkableStageLabel }} 没有可链接的事件" msgid "Ambiguous relationships, contact system administrator" msgstr "关系不明确,请联系系统管理员" -msgid "" -"Enter {{linkableStageLabel}} details in the next step after completing this " -"{{currentStageLabel}}." -msgstr "完成此{{currentStageLabel}}后,在下一步中输入{{linkableStageLabel}}详细信息。" - msgid "Enter details now" msgstr "立即输入详细信息" @@ -1506,6 +1466,9 @@ msgstr "变更日志" msgid "No changes to display" msgstr "没有显示任何变化" +msgid "Updated" +msgstr "更新" + msgid "Created" msgstr "已创建" @@ -1524,6 +1487,9 @@ msgstr "数据条目" msgid "Change" msgstr "改变" +msgid "Value" +msgstr "价值" + msgid "New {{trackedEntityTypeName}} relationship" msgstr "新的 {{trackedEntityTypeName}} 关系" diff --git a/package.json b/package.json index b595eb8d70..cb10cd10a4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "capture-app", "homepage": ".", - "version": "101.12.2", + "version": "101.14.4", "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "101.12.2", + "@dhis2/rules-engine-javascript": "101.14.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 3f0e75af4f..33aba0adb7 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "101.12.2", + "version": "101.14.4", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": { diff --git a/src/core_modules/capture-core/HOC/withErrorMessageHandler.js b/src/core_modules/capture-core/HOC/withErrorMessageHandler.js index 7fc48e201d..8e6f4dcbda 100644 --- a/src/core_modules/capture-core/HOC/withErrorMessageHandler.js +++ b/src/core_modules/capture-core/HOC/withErrorMessageHandler.js @@ -1,11 +1,11 @@ // @flow import * as React from 'react'; import { withStyles } from '@material-ui/core/styles'; +import { NoticeBox } from '@dhis2/ui'; -const getStyles = (theme: Theme) => ({ +const getStyles = () => ({ errorContainer: { margin: 20, - color: theme.palette.error.main, }, }); @@ -23,12 +23,13 @@ export const withErrorMessageHandler = () => if (error) { return ( -
- {error} -
+
{error}
+ ); } diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/SingleOrgUnitSelectField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/SingleOrgUnitSelectField.component.js index 6c252f087b..48c8bb1a99 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/SingleOrgUnitSelectField.component.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/OrgUnitField/SingleOrgUnitSelectField.component.js @@ -1,14 +1,21 @@ // @flow import * as React from 'react'; import { withStyles } from '@material-ui/core'; -import { Chip } from '@dhis2/ui'; +import { Chip, colors } from '@dhis2/ui'; import { OrgUnitField } from './OrgUnitField.component'; +import { TooltipOrgUnit } from '../../../../Tooltips/TooltipOrgUnit/TooltipOrgUnit.component'; const getStyles = () => ({ selectedOrgUnitContainer: { display: 'flex', alignItems: 'center', }, + chip: { + cursor: 'text !important', + '&:hover': { + backgroundColor: `${colors.grey200} !important`, + }, + }, }); type OrgUnitValue = { @@ -27,9 +34,7 @@ type Props = { disabled?: ?boolean, classes: { selectedOrgUnitContainer: string, - clearSelectedOrgUnitButton: string, - clearSelectedOrgUnitButtonDisabled: string, - selectedOrgUnitText: string, + chip: string, } } @@ -44,7 +49,12 @@ class SingleOrgUnitSelectFieldPlain extends React.Component { const { classes } = this.props; return (
- {selectedOrgUnit.name} + + +
); } diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.component.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.component.js index 9de818c2b2..baf588711b 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.component.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.component.js @@ -9,7 +9,7 @@ import { LoadingMaskForPage } from '../../LoadingMasks/LoadingMaskForPage.compon import { withErrorMessageHandler } from '../../../HOC'; import { MissingMessage } from './MissingMessage.component'; import { EnrollmentPageDefault } from './EnrollmentPageDefault'; -import { TopBar } from './TopBar.container'; + const getStyles = ({ typography }) => ({ loadingMask: { @@ -22,36 +22,19 @@ const getStyles = ({ typography }) => ({ const EnrollmentPagePlain = ({ classes, - programId, - orgUnitId, - enrollmentId, - trackedEntityName, - teiDisplayName, enrollmentPageStatus, - enrollmentsAsOptions, }) => ( - <> - - -
- {enrollmentPageStatus === enrollmentPageStatuses.MISSING_SELECTIONS && } +
+ {enrollmentPageStatus === enrollmentPageStatuses.MISSING_SELECTIONS && } - {enrollmentPageStatus === enrollmentPageStatuses.DEFAULT && } + {enrollmentPageStatus === enrollmentPageStatuses.DEFAULT && } - {enrollmentPageStatus === enrollmentPageStatuses.LOADING && ( -
- -
- )} -
- + {enrollmentPageStatus === enrollmentPageStatuses.LOADING && ( +
+ +
+ )} +
); export const EnrollmentPageComponent: ComponentType<$Diff> = compose( diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.container.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.container.js index bacc367ac1..9de18b5331 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.container.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.container.js @@ -24,6 +24,7 @@ import { useSetEnrollmentId, } from '../../ScopeSelector'; import { useLocationQuery } from '../../../utils/routing'; +import { TopBar } from './TopBar.container'; const useComponentLifecycle = () => { const dispatch = useDispatch(); @@ -114,15 +115,26 @@ export const EnrollmentPage: ComponentType<{||}> = () => { useSelector(({ activePage }) => activePage.selectionsError && activePage.selectionsError.error); return ( - + <> + + + + ); }; diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/TopBar.container.js b/src/core_modules/capture-core/components/Pages/Enrollment/TopBar.container.js index 47477a965d..c258a96dc3 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/TopBar.container.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/TopBar.container.js @@ -52,19 +52,21 @@ export const TopBar = ({ onResetOrgUnitId={() => resetOrgUnitId()} onStartAgain={() => reset()} > - resetTeiId('/')} - options={[ - { - label: teiDisplayName, - value: 'alwaysPreselected', - }, - ]} - selectedValue="alwaysPreselected" - title={trackedEntityName} - displayOnly - /> + {trackedEntityName ? ( + resetTeiId('/')} + options={[ + { + label: teiDisplayName, + value: 'alwaysPreselected', + }, + ]} + selectedValue="alwaysPreselected" + title={trackedEntityName} + displayOnly + /> + ) : <>} {enrollmentsAsOptions?.length > 0 ? ( { + const history = useHistory(); const { teiId, programId, orgUnitId, enrollmentId } = useLocationQuery(); const { valid: validIds, loading, error: validatedIdsError } = useValidatedIDsFromCache({ programId, orgUnitId }); const { @@ -63,6 +65,12 @@ const EnrollmentAddEventPagePlain = ({ classes }: Props) => { return EnrollmentAddEventPageStatuses.DEFAULT; }, [enrollmentId, isLoading, loading, pageIsInvalid, programId, teiId, validIds]); + useEffect(() => { + if (pageStatus === EnrollmentAddEventPageStatuses.PROGRAM_INVALID) { + history.push(`/enrollment?${buildUrlQueryString({ orgUnitId, teiId, enrollmentId })}`); + } + }, [pageStatus, orgUnitId, teiId, enrollmentId, history]); + if (pageStatus === EnrollmentAddEventPageStatuses.LOADING) { return ; } @@ -89,10 +97,6 @@ const EnrollmentAddEventPagePlain = ({ classes }: Props) => { i18n.t('Page is missing required values from URL') )} - {pageStatus === EnrollmentAddEventPageStatuses.PROGRAM_INVALID && ( - i18n.t('Program is not valid') - )} - {pageStatus === EnrollmentAddEventPageStatuses.ORG_UNIT_INVALID && ( i18n.t('Org unit is not valid with current program') )} diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js index fb3656915b..88b39abb5d 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.container.js @@ -109,9 +109,9 @@ export const EnrollmentAddEventPageDefault = ({ const dataEntryHasChanges = useSelector(state => getDataEntryHasChanges(state, widgetReducerName)); const { program } = useProgramInfo(programId); - const selectedProgramStage = [...program.stages.values()].find(item => item.id === stageId); + const selectedProgramStage = [...program?.stages.values() ?? []].find(item => item.id === stageId); const outputEffects = useWidgetDataFromStore(widgetReducerName); - const hideWidgets = useHideWidgetByRuleLocations(program.programRules.concat(selectedProgramStage?.programRules ?? [])); + const hideWidgets = useHideWidgetByRuleLocations(program?.programRules.concat(selectedProgramStage?.programRules ?? [])); // $FlowFixMe const trackedEntityName = program?.trackedEntityType?.name; diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.types.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.types.js index d900462c76..a8929f2b14 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.types.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/EnrollmentAddEventPageDefault/EnrollmentAddEventPageDefault.types.js @@ -7,7 +7,7 @@ import type { import { Program } from '../../../../metaData'; export type Props = {| - program: Program, + program: ?Program, stageId: string, orgUnitId: string, teiId: string, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js index 03b56579de..d2acb94e3b 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.component.js @@ -60,7 +60,7 @@ export const EnrollmentEditEventPageComponent = ({ mode={mode} programStage={programStage} enrollmentId={enrollmentId} - programId={program.id} + programId={program?.id} enrollmentsAsOptions={enrollmentsAsOptions} trackedEntityName={trackedEntityName} teiDisplayName={teiDisplayName} diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js index 9ba1bfcdd8..995fca5429 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.js @@ -132,8 +132,8 @@ const EnrollmentEditEventPageWithContextPlain = ({ }, [dispatch]); const { program } = useProgramInfo(programId); - const programStage = [...program.stages?.values()].find(item => item.id === stageId); - const hideWidgets = useHideWidgetByRuleLocations(program.programRules.concat(programStage?.programRules)); + const programStage = [...program?.stages?.values() ?? []].find(item => item.id === stageId); + const hideWidgets = useHideWidgetByRuleLocations(program?.programRules.concat(programStage?.programRules)); const onDeleteTrackedEntitySuccess = useCallback(() => { history.push(`/?${buildUrlQueryString({ orgUnitId, programId })}`); diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js index e164d9fc02..7f48be3b24 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.types.js @@ -17,7 +17,7 @@ export type PlainProps = {| enrollmentId: string, eventId: string, stageId: string, - program: Program, + program: ?Program, trackedEntityTypeId: string, mode: string, orgUnitId: string, diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/TopBar.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/TopBar.container.js index 1798a80a16..e613228db2 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/TopBar.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/TopBar.container.js @@ -21,7 +21,7 @@ import { TopBarActions } from '../../TopBarActions'; type Props = {| programStage: ?ProgramStage, enrollmentId: string, - programId: string, + programId: ?string, mode: string, orgUnitId: string, trackedEntityName: string, diff --git a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js index 2d29de6e9b..5c9952f28d 100644 --- a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js +++ b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js @@ -3,13 +3,13 @@ import React, { useMemo } from 'react'; import { compose } from 'redux'; import { colors, spacers } from '@dhis2/ui'; import { withStyles } from '@material-ui/core/styles'; +import type { ComponentType } from 'react'; +import type { Props, ContainerProps } from './mainPage.types'; import { WorkingListsType } from './WorkingListsType'; -import type { Props, PlainProps } from './mainPage.types'; import { MainPageStatuses } from './MainPage.constants'; import { WithoutOrgUnitSelectedMessage } from './WithoutOrgUnitSelectedMessage/WithoutOrgUnitSelectedMessage'; import { WithoutCategorySelectedMessage } from './WithoutCategorySelectedMessage/WithoutCategorySelectedMessage'; import { withErrorMessageHandler, withLoadingIndicator } from '../../../HOC'; -import { TopBar } from './TopBar.container'; import { SearchBox } from '../../SearchBox'; import { TemplateSelector } from '../../TemplateSelector'; import { @@ -41,79 +41,66 @@ const getStyles = () => ({ }, }); -const useShowMainPage = ({ programId, orgUnitId, trackedEntityTypeId, displayFrontPageList, selectedTemplateId }) => - useMemo(() => { - const noProgramSelected = !programId; - const noOrgUnitSelected = !orgUnitId; - const isEventProgram = !trackedEntityTypeId; - - return noProgramSelected || noOrgUnitSelected || isEventProgram || displayFrontPageList || selectedTemplateId; - }, [programId, orgUnitId, trackedEntityTypeId, displayFrontPageList, selectedTemplateId]); - -const MainPageBody = compose( - withErrorMessageHandler(), - withStyles(getStyles), -)(({ MainPageStatus, setShowAccessible, programId, showMainPage, classes, ...passOnProps }: PlainProps) => ( - <> - {showMainPage ? ( - <> - {MainPageStatus === MainPageStatuses.WITHOUT_ORG_UNIT_SELECTED && ( - - )} - {MainPageStatus === MainPageStatuses.CATEGORY_OPTION_INVALID_FOR_ORG_UNIT && ( - - )} - {MainPageStatus === MainPageStatuses.WITHOUT_PROGRAM_CATEGORY_SELECTED && ( - - )} - {MainPageStatus === MainPageStatuses.SHOW_WORKING_LIST && ( -
- -
- )} - - ) : ( -
-
- -
-
- -
-
- )} - -)); - -const MainPage = ({ +const MainPagePlain = ({ programId, orgUnitId, trackedEntityTypeId, displayFrontPageList, selectedTemplateId, - selectedCategories, - ...passOnProps + MainPageStatus, + setShowAccessible, + classes, + onChangeTemplate, }: Props) => { - const showMainPage = useShowMainPage({ - programId, - orgUnitId, - trackedEntityTypeId, - displayFrontPageList, - selectedTemplateId, - }); + const showMainPage = useMemo(() => { + const noProgramSelected = !programId; + const noOrgUnitSelected = !orgUnitId; + const isEventProgram = !trackedEntityTypeId; + return noProgramSelected || noOrgUnitSelected || isEventProgram || displayFrontPageList || selectedTemplateId; + }, [programId, orgUnitId, trackedEntityTypeId, displayFrontPageList, selectedTemplateId]); return ( <> - - + {showMainPage ? ( + <> + {MainPageStatus === MainPageStatuses.WITHOUT_ORG_UNIT_SELECTED && ( + + )} + {MainPageStatus === MainPageStatuses.CATEGORY_OPTION_INVALID_FOR_ORG_UNIT && ( + + )} + {MainPageStatus === MainPageStatuses.WITHOUT_PROGRAM_CATEGORY_SELECTED && ( + + )} + {MainPageStatus === MainPageStatuses.SHOW_WORKING_LIST && ( +
+ +
+ )} + + ) : ( +
+
+ +
+
+ +
+
+ )} ); }; -export const MainPageComponent = withLoadingIndicator()(MainPage); + +export const MainPageComponent: ComponentType = + compose( + withLoadingIndicator(), + withErrorMessageHandler(), + withStyles(getStyles), + )(MainPagePlain); diff --git a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js index 32ac19a65b..3dc90d8bd5 100644 --- a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js +++ b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js @@ -11,6 +11,7 @@ import { buildUrlQueryString, useLocationQuery } from '../../../utils/routing'; import { MainPageStatuses } from './MainPage.constants'; import { OrgUnitFetcher } from '../../OrgUnitFetcher'; import { useCategoryOptionIsValidForOrgUnit } from '../../../hooks/useCategoryComboIsValidForOrgUnit'; +import { TopBar } from './TopBar.container'; const mapStateToProps = (state: ReduxState) => ({ error: state.activePage.selectionsError && state.activePage.selectionsError.error, // TODO: Should probably remove this @@ -169,6 +170,7 @@ const MainPageContainer = () => { return ( + { error={error} ready={ready} displayFrontPageList={displayFrontPageList} - selectedCategories={selectedCategories} /> ); diff --git a/src/core_modules/capture-core/components/Pages/MainPage/WithoutCategorySelectedMessage/WithoutCategorySelectedMessage.js b/src/core_modules/capture-core/components/Pages/MainPage/WithoutCategorySelectedMessage/WithoutCategorySelectedMessage.js index d30ef8e3dd..fd23dceaa9 100644 --- a/src/core_modules/capture-core/components/Pages/MainPage/WithoutCategorySelectedMessage/WithoutCategorySelectedMessage.js +++ b/src/core_modules/capture-core/components/Pages/MainPage/WithoutCategorySelectedMessage/WithoutCategorySelectedMessage.js @@ -32,7 +32,7 @@ const WithoutCategorySelectedMessagePlain = ({ programId, classes }) => { categories: currentSelections.categories, }), shallowEqual); - if (!program.categoryCombination) { + if (!program?.categoryCombination) { log.error(errorCreator(errorMessages.MISSING_CATEGORY)({ programId })); throw Error(i18n.t(errorMessages.GENERIC_ERROR)); } diff --git a/src/core_modules/capture-core/components/Pages/MainPage/WithoutOrgUnitSelectedMessage/WithoutOrgUnitSelectedMessage.js b/src/core_modules/capture-core/components/Pages/MainPage/WithoutOrgUnitSelectedMessage/WithoutOrgUnitSelectedMessage.js index f791f50b93..cb2379c151 100644 --- a/src/core_modules/capture-core/components/Pages/MainPage/WithoutOrgUnitSelectedMessage/WithoutOrgUnitSelectedMessage.js +++ b/src/core_modules/capture-core/components/Pages/MainPage/WithoutOrgUnitSelectedMessage/WithoutOrgUnitSelectedMessage.js @@ -39,16 +39,15 @@ type Props = {| |} const WithoutOrgUnitSelectedMessagePlain = ({ programId, setShowAccessible, classes }: Props) => { - // TODO - this hook breaks the app when the program is not found const { program, programType } = useProgramInfo(programId); const IncompleteSelectionMessage = useMemo(() => (programType === programTypes.TRACKER_PROGRAM ? ( i18n.t('Or see all records accessible to you in {{program}} ', { - program: program.name, + program: program?.name, interpolation: { escapeValue: false }, }) ) : i18n.t('Or see all events accessible to you in {{program}}', - { program: program.name, interpolation: { escapeValue: false } })), - [program.name, programType]); + { program: program?.name, interpolation: { escapeValue: false } })), + [program?.name, programType]); return (
void, -|}>; - -export type PlainProps = $ReadOnly<{| - ...PassOnProps, setShowAccessible: () => void, MainPageStatus: boolean, selectedTemplateId: string, - showMainPage: boolean, - ...CssClasses, -|}>; - -export type Props = $ReadOnly<{| - ...PassOnProps, - ...PlainProps, error: boolean, ready: boolean, - trackedEntityTypeId?: string, - displayFrontPageList?: boolean, - selectedCategories: ?{ [categoryId: string]: { writeAccess: boolean } }, +|} +>; + +export type Props = $ReadOnly<{| + ...ContainerProps, + ...CssClasses |}>; diff --git a/src/core_modules/capture-core/components/Pages/New/NewPage.component.js b/src/core_modules/capture-core/components/Pages/New/NewPage.component.js index a7b070193b..35939b44f6 100644 --- a/src/core_modules/capture-core/components/Pages/New/NewPage.component.js +++ b/src/core_modules/capture-core/components/Pages/New/NewPage.component.js @@ -7,7 +7,7 @@ import withStyles from '@material-ui/core/styles/withStyles'; import { OrgUnitFetcher } from 'capture-core/components/OrgUnitFetcher'; import i18n from '@dhis2/d2-i18n'; import { Button } from '@dhis2/ui'; -import { TopBar } from './TopBar.container'; + import type { ContainerProps, Props } from './NewPage.types'; import { withErrorMessageHandler, withLoadingIndicator } from '../../../HOC'; import { NEW_TEI_DATA_ENTRY_ID, newPageStatuses } from './NewPage.constants'; @@ -36,11 +36,7 @@ const NewPagePlain = ({ categoryOptionIsInvalidForOrgUnit, missingCategoriesInProgramSelection, orgUnitSelectionIncomplete, - isUserInteractionInProgress, - programId, - teiId, trackedEntityName, - teiDisplayName, trackedEntityInstanceAttributes, }: Props) => { const { scopeType } = useScopeInfo(currentScopeId); @@ -72,16 +68,7 @@ const NewPagePlain = ({ ]); const orgUnitId = useSelector(({ currentSelections }) => currentSelections.orgUnitId); - return (<> - + return (
{ !writeAccess ? @@ -154,7 +141,7 @@ const NewPagePlain = ({ }
- ); + ); }; export const NewPageComponent: ComponentType = diff --git a/src/core_modules/capture-core/components/Pages/New/NewPage.container.js b/src/core_modules/capture-core/components/Pages/New/NewPage.container.js index 8ea87c19e2..4286ee2c70 100644 --- a/src/core_modules/capture-core/components/Pages/New/NewPage.container.js +++ b/src/core_modules/capture-core/components/Pages/New/NewPage.container.js @@ -9,7 +9,7 @@ import { showDefaultViewOnNewPage, showMessageToSelectProgramCategoryOnNewPage, showMessageThatCategoryOptionIsInvalidForOrgUnit, } from './NewPage.actions'; -import { typeof newPageStatuses } from './NewPage.constants'; +import { newPageStatuses } from './NewPage.constants'; import { buildUrlQueryString, useLocationQuery } from '../../../utils/routing'; import { getScopeFromScopeId, TrackerProgram, TrackedEntityType } from '../../../metaData'; import { useMissingCategoriesInProgramSelection } from '../../../hooks/useMissingCategoriesInProgramSelection'; @@ -18,6 +18,7 @@ import { useTrackedEntityInstances } from './hooks'; import { deriveTeiName } from '../common/EnrollmentOverviewDomain/useTeiDisplayName'; import { programCollection } from '../../../metaDataMemoryStores/programCollection/programCollection'; import { useCategoryOptionIsValidForOrgUnit } from '../../../hooks/useCategoryComboIsValidForOrgUnit'; +import { TopBar } from './TopBar.container'; const useUserWriteAccess = (scopeId) => { const scope = getScopeFromScopeId(scopeId); @@ -89,7 +90,7 @@ export const NewPage: ComponentType<{||}> = () => { ({ currentSelections }) => !currentSelections.orgUnitId && !currentSelections.complete, ); - const newPageStatus: $Keys = + const newPageStatus: $Keys = useSelector(({ newPage }) => newPage.newPageStatus); const handleMainPageNavigation = () => { @@ -108,26 +109,34 @@ export const NewPage: ComponentType<{||}> = () => { ); return ( - ); + <> + + + + ); }; diff --git a/src/core_modules/capture-core/components/Pages/New/NewPage.types.js b/src/core_modules/capture-core/components/Pages/New/NewPage.types.js index 438b61a88f..9004f99945 100644 --- a/src/core_modules/capture-core/components/Pages/New/NewPage.types.js +++ b/src/core_modules/capture-core/components/Pages/New/NewPage.types.js @@ -29,7 +29,6 @@ export type ContainerProps = $ReadOnly<{| writeAccess: boolean, error: boolean, ready: boolean, - isUserInteractionInProgress: boolean, programId?: string, teiId?: string, trackedEntityName?: string, diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js index ea39476d43..fc4f52cfa5 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js @@ -47,7 +47,7 @@ class RegUnitSelectorPlain extends React.Component { return; } - onUpdateSelectedOrgUnit(orgUnit, program.organisationUnits ? !program.organisationUnits[orgUnit.id] : false); + onUpdateSelectedOrgUnit(orgUnit, program?.organisationUnits ? !program.organisationUnits[orgUnit.id] : false); } render() { diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/epics/getCategoriesDataFromEvent.js b/src/core_modules/capture-core/components/Pages/ViewEvent/epics/getCategoriesDataFromEvent.js index c3609baa25..1f98cf8bcd 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/epics/getCategoriesDataFromEvent.js +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/epics/getCategoriesDataFromEvent.js @@ -25,7 +25,7 @@ export async function getCategoriesDataFromEventAsync( const program = getProgramFromProgramIdThrowIfNotFound(event.programId); - const categoryCombination = program.categoryCombination; + const categoryCombination = program?.categoryCombination; if (!categoryCombination) { return null; } diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js index e75b8513ea..42cba9027f 100644 --- a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/RegUnitSelector/RegUnitSelector.component.js @@ -47,7 +47,7 @@ class RegUnitSelectorPlain extends React.Component { return; } - onUpdateSelectedOrgUnit(orgUnit, program.organisationUnits ? !program.organisationUnits[orgUnit.id] : false); + onUpdateSelectedOrgUnit(orgUnit, program?.organisationUnits ? !program.organisationUnits[orgUnit.id] : false); } render() { diff --git a/src/core_modules/capture-core/components/ScopeSelector/QuickSelector/QuickSelector.types.js b/src/core_modules/capture-core/components/ScopeSelector/QuickSelector/QuickSelector.types.js index 3b8451471c..148a0b7bfd 100644 --- a/src/core_modules/capture-core/components/ScopeSelector/QuickSelector/QuickSelector.types.js +++ b/src/core_modules/capture-core/components/ScopeSelector/QuickSelector/QuickSelector.types.js @@ -3,7 +3,7 @@ import type { Node } from 'react'; export type Props = { selectedOrgUnitId?: string, - selectedProgramId?: string, + selectedProgramId?: ?string, selectedCategories: Object, selectedOrgUnit: Object, previousOrgUnitId?: string, diff --git a/src/core_modules/capture-core/components/ScopeSelector/ScopeSelector.types.js b/src/core_modules/capture-core/components/ScopeSelector/ScopeSelector.types.js index e92d91a8a2..09faa0ece8 100644 --- a/src/core_modules/capture-core/components/ScopeSelector/ScopeSelector.types.js +++ b/src/core_modules/capture-core/components/ScopeSelector/ScopeSelector.types.js @@ -5,7 +5,7 @@ export type OwnProps = $ReadOnly<{| isUserInteractionInProgress?: boolean, pageToPush?: string, selectedOrgUnitId?: string, - selectedProgramId?: string, + selectedProgramId?: ?string, previousOrgUnitId?: string, selectedCategories?: { [categoryId: string]: { writeAccess: boolean } }, onSetProgramId?: (id: string) => void, diff --git a/src/core_modules/capture-core/components/TopBarActions/TopBarActions.types.js b/src/core_modules/capture-core/components/TopBarActions/TopBarActions.types.js index 0f13e109f2..249ab2faa9 100644 --- a/src/core_modules/capture-core/components/TopBarActions/TopBarActions.types.js +++ b/src/core_modules/capture-core/components/TopBarActions/TopBarActions.types.js @@ -1,13 +1,13 @@ // @flow export type Props = { - selectedProgramId?: string, + selectedProgramId?: ?string, selectedOrgUnitId?: string, isUserInteractionInProgress?: boolean, }; export type PlainProps = { - selectedProgramId?: string, + selectedProgramId?: ?string, onNewClick: () => void, onNewClickWithoutProgramId: () => void, onFindClick: () => void, diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.component.js index d95f39c8fb..e3e08de57d 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.component.js @@ -39,6 +39,7 @@ export const ActionsPlain = ({ onUpdate, onDelete, onUpdateOwnership, + canCascadeDeleteEnrollment, isTransferLoading, onAddNew, loading, @@ -115,6 +116,7 @@ export const ActionsPlain = ({ onUpdate={handleOnUpdateStatus} /> diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.container.js b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.container.js index 79df428da7..b7ad956d52 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.container.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Actions.container.js @@ -4,6 +4,7 @@ import { ActionsComponent } from './Actions.component'; import type { Props } from './actions.types'; import { useUpdateEnrollment, useDeleteEnrollment } from '../dataMutation/dataMutation'; import { useUpdateOwnership } from './Transfer/hooks'; +import { useAuthorities } from '../../../utils/authority/useAuthorities'; export const Actions = ({ enrollment = {}, @@ -20,6 +21,7 @@ export const Actions = ({ }: Props) => { const { updateMutation, updateLoading } = useUpdateEnrollment(refetchEnrollment, refetchTEI, onError, onSuccess); const { deleteMutation, deleteLoading } = useDeleteEnrollment(onDelete, onError, onSuccess); + const { hasAuthority } = useAuthorities({ authorities: ['F_ENROLLMENT_CASCADE_DELETE'] }); const { updateEnrollmentOwnership, isTransferLoading } = useUpdateOwnership({ teiId: enrollment.trackedEntity, programId: enrollment.program, @@ -52,6 +54,7 @@ export const Actions = ({ onUpdate={updateMutation} onUpdateStatus={handleUpdateStatus} onDelete={deleteMutation} + canCascadeDeleteEnrollment={hasAuthority} loading={updateLoading || deleteLoading || updateStatusLoading} onUpdateOwnership={updateEnrollmentOwnership} isTransferLoading={isTransferLoading} diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/Delete.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/Delete.component.js index d301326dc7..f870ed9197 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/Delete.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/Delete.component.js @@ -1,4 +1,5 @@ // @flow +import React, { useState } from 'react'; import { IconDelete16, MenuItem, @@ -9,18 +10,21 @@ import { ButtonStrip, Button, } from '@dhis2/ui'; -import React, { useState } from 'react'; import i18n from '@dhis2/d2-i18n'; import type { Props } from './delete.types'; +import { ConditionalTooltip } from '../../../Tooltips/ConditionalTooltip/'; -export const Delete = ({ enrollment, onDelete }: Props) => { +export const Delete = ({ canCascadeDeleteEnrollment, enrollment, onDelete }: Props) => { const [toggle, setToggle] = useState(false); + const disabled = !canCascadeDeleteEnrollment; + const tooltipContent = i18n.t('You do not have access to delete this enrollment'); return ( -
+ } destructive label={i18n.t('Delete')} @@ -54,6 +58,6 @@ export const Delete = ({ enrollment, onDelete }: Props) => { )} -
+ ); }; diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/delete.types.js b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/delete.types.js index 7cd649b391..71b637983f 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/delete.types.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/Delete/delete.types.js @@ -1,6 +1,7 @@ // @flow export type Props = {| + canCascadeDeleteEnrollment: boolean, enrollment: Object, onDelete: (arg: Object) => void, |}; diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/actions.types.js b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/actions.types.js index e6da45629d..b40adc2282 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/Actions/actions.types.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/Actions/actions.types.js @@ -32,6 +32,7 @@ export type PlainProps = {| onDelete: (arg: Object) => void, onAddNew: (arg: Object) => void, onUpdateOwnership: UpdateEnrollmentOwnership, + canCascadeDeleteEnrollment: boolean, isTransferLoading: boolean, loading: boolean, canAddNew: boolean, diff --git a/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js b/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js index 00c44c0a6e..1cd597f02d 100644 --- a/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js +++ b/src/core_modules/capture-core/components/WidgetEnrollment/WidgetEnrollment.component.js @@ -136,7 +136,9 @@ export const WidgetEnrollmentPlain = ({ - {i18n.t('Started at: ')} + {i18n.t('Started at{{escape}}', { + escape: ':', + })} {convertValue(orgUnitClientValue, type)}
@@ -144,7 +146,9 @@ export const WidgetEnrollmentPlain = ({ - {i18n.t('Owned by: ')} + {i18n.t('Owned by{{escape}}', { + escape: ':', + })} {convertValue(ownerOrgUnitClientValue, type)} 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 f953852223..b39bb89c7c 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.js @@ -32,7 +32,7 @@ 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 './hooks'; +import { useAuthorities } from '../../utils/authority/useAuthorities'; const styles = { header: { @@ -101,8 +101,8 @@ export const WidgetEventEditPlain = ({ const loadedValues = useSelector(({ viewEventPage }) => viewEventPage.loadedValues); const eventAccess = getProgramEventAccess(programId, stageId); - const { canEditCompletedEvent } = useAuthorities(); - const blockEntryForm = stage.blockEntryForm && !canEditCompletedEvent && eventStatus === eventStatuses.COMPLETED; + const { hasAuthority } = useAuthorities({ authorities: ['F_UNCOMPLETE_EVENT'] }); + const blockEntryForm = stage.blockEntryForm && !hasAuthority && eventStatus === eventStatuses.COMPLETED; const disableEdit = !eventAccess?.write || blockEntryForm; const tooltipContent = blockEntryForm ? diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/hooks/index.js b/src/core_modules/capture-core/components/WidgetEventEdit/hooks/index.js deleted file mode 100644 index edcfc4f241..0000000000 --- a/src/core_modules/capture-core/components/WidgetEventEdit/hooks/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// @flow -export { useAuthorities } from './useAuthorities'; 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 f2b4fca631..031f7fd462 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 @@ -1,8 +1,8 @@ // @flow import React from 'react'; +import { useAuthorities } from 'capture-core/utils/authority/useAuthorities'; import type { Props } from './OverflowMenu.types'; import { OverflowMenuComponent } from './OverflowMenu.component'; -import { useAuthorities } from './hooks'; export const OverflowMenu = ({ trackedEntityTypeName, @@ -13,13 +13,13 @@ export const OverflowMenu = ({ teiId, programAPI, }: Props) => { - const { canCascadeDeleteTei } = useAuthorities(); + const { hasAuthority } = useAuthorities({ authorities: ['F_TEI_CASCADE_DELETE'] }); return ( { - const queryKey = ['authorities']; - const queryFn = { - resource: 'me.json', - params: { - fields: 'authorities', - }, - }; - const queryOptions = { - select: ({ authorities }) => - authorities && - authorities.some(authority => authority === auth.ALL || authority === auth.F_TEI_CASCADE_DELETE), - }; - const { data } = useApiMetadataQuery(queryKey, queryFn, queryOptions); - - return { - canCascadeDeleteTei: Boolean(data), - }; -}; diff --git a/src/core_modules/capture-core/events/getSubValues.js b/src/core_modules/capture-core/events/getSubValues.js index 19b36daf2a..7d61eb702a 100644 --- a/src/core_modules/capture-core/events/getSubValues.js +++ b/src/core_modules/capture-core/events/getSubValues.js @@ -37,19 +37,23 @@ const subValueGetterByElementType = { return null; }), [dataElementTypes.IMAGE]: ({ + value, eventId, metaElementId, absoluteApiPath, }: { + value: any, eventId: string, metaElementId: string, absoluteApiPath: string, }) => (featureAvailable(FEATURES.trackerImageEndpoint) ? { + value, url: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${metaElementId}/image`, previewUrl: `${absoluteApiPath}/tracker/events/${eventId}/dataValues/${metaElementId}/image?dimension=small`, } : { + value, url: `${absoluteApiPath}/events/files?dataElementUid=${metaElementId}&eventUid=${eventId}`, previewUrl: `${absoluteApiPath}/events/files?dataElementUid=${metaElementId}&eventUid=${eventId}&dimension=SMALL`, } @@ -67,11 +71,13 @@ const subValueGetterByElementType = { }) => { const ouIds = value.split('/'); const id = ouIds[ouIds.length - 1]; - return querySingleResource({ resource: 'organisationUnits', + return querySingleResource({ + resource: 'organisationUnits', id, params: { fields: 'id,code,displayName,path', - } }) + }, + }) .then(res => ({ id: res.id, code: res.code, diff --git a/src/core_modules/capture-core/hooks/useProgramInfo/useProgramInfo.js b/src/core_modules/capture-core/hooks/useProgramInfo/useProgramInfo.js index dc2437ed04..2b2a4ddbbb 100644 --- a/src/core_modules/capture-core/hooks/useProgramInfo/useProgramInfo.js +++ b/src/core_modules/capture-core/hooks/useProgramInfo/useProgramInfo.js @@ -4,9 +4,16 @@ import { getProgramFromProgramIdThrowIfNotFound, TrackerProgram } from '../../me import { programTypes } from './programTypes.const'; export const useProgramInfo = (programId: string) => useMemo(() => { - const program = getProgramFromProgramIdThrowIfNotFound(programId); - return { - program, - programType: program instanceof TrackerProgram ? programTypes.TRACKER_PROGRAM : programTypes.EVENT_PROGRAM, - }; + try { + const program = getProgramFromProgramIdThrowIfNotFound(programId); + return { + program, + programType: program instanceof TrackerProgram ? programTypes.TRACKER_PROGRAM : programTypes.EVENT_PROGRAM, + }; + } catch (error) { + return { + program: undefined, + programType: undefined, + }; + } }, [programId]); diff --git a/src/core_modules/capture-core/metaData/helpers/getProgramEventAccess.js b/src/core_modules/capture-core/metaData/helpers/getProgramEventAccess.js index 8fad80830f..3fddd73db8 100644 --- a/src/core_modules/capture-core/metaData/helpers/getProgramEventAccess.js +++ b/src/core_modules/capture-core/metaData/helpers/getProgramEventAccess.js @@ -14,7 +14,7 @@ export function getProgramEventAccess( if (program instanceof EventProgram) { stage = program.stage; } else if (programStageId) { - stage = program.getStage(programStageId); + stage = program?.getStage(programStageId); } if (!stage) { log.error(errorCreator('stage not found')({ programId, programStageId })); diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/hooks/useAuthorities.js b/src/core_modules/capture-core/utils/authority/useAuthorities.js similarity index 54% rename from src/core_modules/capture-core/components/WidgetEventEdit/hooks/useAuthorities.js rename to src/core_modules/capture-core/utils/authority/useAuthorities.js index 78de4ab612..0bea301f9e 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/hooks/useAuthorities.js +++ b/src/core_modules/capture-core/utils/authority/useAuthorities.js @@ -2,11 +2,10 @@ import { useApiMetadataQuery } from 'capture-core/utils/reactQueryHelpers'; const auth = Object.freeze({ - F_UNCOMPLETE_EVENT: 'F_UNCOMPLETE_EVENT', ALL: 'ALL', }); -export const useAuthorities = () => { +export const useAuthorities = ({ authorities }: { authorities: Array }) => { const queryKey = ['authorities']; const queryFn = { resource: 'me.json', @@ -15,13 +14,15 @@ export const useAuthorities = () => { }, }; const queryOptions = { - select: ({ authorities }) => - authorities && - authorities.some(authority => authority === auth.ALL || authority === auth.F_UNCOMPLETE_EVENT), + select: ({ authorities: userAuthorities }) => + userAuthorities && + authorities.some( + authority => userAuthorities.includes(auth.ALL) || userAuthorities.includes(authority), + ), }; const { data } = useApiMetadataQuery(queryKey, queryFn, queryOptions); return { - canEditCompletedEvent: Boolean(data), + hasAuthority: Boolean(data), }; };