diff --git a/.cypress-cucumber-preprocessorrc.json b/.cypress-cucumber-preprocessorrc.json index f1975a9f5e..b935815d07 100644 --- a/.cypress-cucumber-preprocessorrc.json +++ b/.cypress-cucumber-preprocessorrc.json @@ -1,3 +1,3 @@ { "nonGlobalStepDefinitions": true -} \ No newline at end of file +} diff --git a/.env b/.env index 77a6b672f3..d41c6a2f62 100644 --- a/.env +++ b/.env @@ -1,3 +1,5 @@ REACT_APP_CACHE_VERSION=$npm_package_cacheVersion REACT_APP_SERVER_VERSION=$npm_package_serverVersion REACT_APP_VERSION=$npm_package_version +# TODO: Remove this when upgrading to react-scripts 5 (and webpack 5) +NODE_OPTIONS=--openssl-legacy-provider diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 6e92b4aae2..d1cf66c452 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -78,18 +78,17 @@ jobs: needs: prerequisites if: contains(fromJson(needs.prerequisites.outputs.json-labels), github.event.label.name) runs-on: ubuntu-latest - container: cypress/browsers:node14.7.0-chrome84 strategy: fail-fast: false matrix: versions: ${{ fromJSON(needs.prerequisites.outputs.versions) }} containers: ${{ fromJSON(needs.prerequisites.outputs.matrix-containers) }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - name: compute-instance-url id: instance-url @@ -98,7 +97,7 @@ jobs: version: ${{ matrix.versions }} - name: Cypress run - uses: cypress-io/github-action@v4 + uses: cypress-io/github-action@v6 with: record: true parallel: true @@ -115,6 +114,7 @@ jobs: CYPRESS_dhis2InstanceVersion: ${{matrix.versions}} CYPRESS_dhis2Username: ${{ secrets.CYPRESS_DHIS2_USERNAME }} CYPRESS_dhis2Password: ${{ secrets.CYPRESS_DHIS2_PASSWORD }} + NODE_OPTIONS: "--openssl-legacy-provider" call-e2e-tests-result: needs: cypress diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index bbef3256d9..ddea1ad671 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -4,22 +4,22 @@ on: pull_request: types: [ labeled ] -jobs: +jobs: preview: runs-on: ubuntu-latest if: (github.event.label.name == 'preview' || github.event.label.name == 'testing') && !github.event.push.repository.fork && github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/cache@v2 id: yarn-cache with: path: '**/node_modules' key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - + - name: Install if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install --frozen-lockfile diff --git a/.github/workflows/verify-app.yml b/.github/workflows/verify-app.yml index afe87ad0dd..4877b7ceed 100644 --- a/.github/workflows/verify-app.yml +++ b/.github/workflows/verify-app.yml @@ -20,10 +20,10 @@ jobs: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/cache@v2 id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) @@ -40,10 +40,10 @@ jobs: needs: install if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/cache@v2 id: yarn-cache @@ -59,10 +59,10 @@ jobs: if: "!contains(github.event.head_commit.message, '[skip ci]')" needs: install steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/cache@v2 id: yarn-cache @@ -78,10 +78,10 @@ jobs: needs: install if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/cache@v2 id: yarn-cache @@ -91,7 +91,7 @@ jobs: - name: Unit tests run: yarn test - + instance-version: runs-on: ubuntu-latest needs: [lint, flow, unit-tests] @@ -105,11 +105,10 @@ jobs: instance-url: ${{ secrets.CYPRESS_DHIS2_INSTANCES_BASE_URL }}/ca-test-dev username: ${{ secrets.CYPRESS_DHIS2_USERNAME }} password: ${{ secrets.CYPRESS_DHIS2_PASSWORD }} - + cypress-dev: runs-on: ubuntu-latest needs: instance-version - container: cypress/browsers:node14.7.0-chrome84 strategy: # when one test fails, DO NOT cancel the other # containers, because this will kill Cypress processes @@ -119,13 +118,13 @@ jobs: matrix: containers: [1, 2, 3, 4, 5] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - name: Cypress run - uses: cypress-io/github-action@v4 + uses: cypress-io/github-action@v6 with: record: true parallel: true @@ -142,16 +141,17 @@ jobs: CYPRESS_dhis2InstanceVersion: ${{ needs.instance-version.outputs.version }} CYPRESS_dhis2Username: ${{ secrets.CYPRESS_DHIS2_USERNAME }} CYPRESS_dhis2Password: ${{ secrets.CYPRESS_DHIS2_PASSWORD }} + NODE_OPTIONS: "--openssl-legacy-provider" build: runs-on: ubuntu-latest needs: [lint, flow, unit-tests] if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/cache@v2 id: yarn-cache @@ -175,13 +175,13 @@ jobs: needs: build if: "!github.event.push.repository.fork && !contains(github.event.head_commit.message, '[skip ci]') && github.actor != 'dependabot[bot]'" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: token: ${{ env.GH_TOKEN }} - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 18.x - uses: actions/download-artifact@v2 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index ddcfc2c8a2..dabf7de44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,72 @@ +## [100.44.4](https://github.com/dhis2/capture-app/compare/v100.44.3...v100.44.4) (2023-11-10) + + +### Bug Fixes + +* [DHIS2-15383] align mandatory behaviour for all value types ([#3413](https://github.com/dhis2/capture-app/issues/3413)) ([b0eddc7](https://github.com/dhis2/capture-app/commit/b0eddc76b674ceb20df0eb5bcd0672a3c42d3954)) + +## [100.44.3](https://github.com/dhis2/capture-app/compare/v100.44.2...v100.44.3) (2023-11-10) + + +### Bug Fixes + +* [DHIS2-15814] missing orgunit names ([#3449](https://github.com/dhis2/capture-app/issues/3449)) ([488f8c0](https://github.com/dhis2/capture-app/commit/488f8c090680ad47ed693214478a7ef2919b82e7)) + +## [100.44.2](https://github.com/dhis2/capture-app/compare/v100.44.1...v100.44.2) (2023-11-05) + + +### Bug Fixes + +* **translations:** sync translations from transifex (master) ([0076449](https://github.com/dhis2/capture-app/commit/0076449e545803fa8b5e36b0663d807bb6c7d410)) + +## [100.44.1](https://github.com/dhis2/capture-app/compare/v100.44.0...v100.44.1) (2023-11-02) + + +### Bug Fixes + +* [DHIS2-15945] translations in enrollment widget ([#3433](https://github.com/dhis2/capture-app/issues/3433)) ([c188767](https://github.com/dhis2/capture-app/commit/c1887673e56f2f73cfc594b682d471c10858c0fd)) + +# [100.44.0](https://github.com/dhis2/capture-app/compare/v100.43.0...v100.44.0) (2023-11-01) + + +### Features + +* [DHIS2-15483] assign an user when scheduling an enrollment event ([#3419](https://github.com/dhis2/capture-app/issues/3419)) ([556884f](https://github.com/dhis2/capture-app/commit/556884f7302215c31b7be433bbeb24503ea9b12d)) + +# [100.43.0](https://github.com/dhis2/capture-app/compare/v100.42.0...v100.43.0) (2023-10-31) + + +### Bug Fixes + +* **translations:** sync translations from transifex (master) ([9636ea1](https://github.com/dhis2/capture-app/commit/9636ea1e8f54685b2c5ca65fd7b49cd3f5d5f846)) +* [DHIS2-15879] Unable to schedule event with note ([#3421](https://github.com/dhis2/capture-app/issues/3421)) ([e017ac8](https://github.com/dhis2/capture-app/commit/e017ac8e3df69c4803d2e6f73aa9a585984a008f)) + + +### Features + +* [DHIS-14012] removed download as XML button ([#3434](https://github.com/dhis2/capture-app/issues/3434)) ([7b0e387](https://github.com/dhis2/capture-app/commit/7b0e3871c76478aabfd6696a8e7ac27339254299)) + +# [100.42.0](https://github.com/dhis2/capture-app/compare/v100.41.4...v100.42.0) (2023-10-24) + + +### Features + +* [DHIS2-12361] Tracked Entity Relationships widget ([cafed8d](https://github.com/dhis2/capture-app/commit/cafed8d8aac3fe9955739b1a8ee2cdde57722967)) + +## [100.41.4](https://github.com/dhis2/capture-app/compare/v100.41.3...v100.41.4) (2023-10-22) + + +### Bug Fixes + +* **translations:** sync translations from transifex (master) ([c82ce9f](https://github.com/dhis2/capture-app/commit/c82ce9f69159878f5ca9cc2e6ca9944544099368)) + +## [100.41.3](https://github.com/dhis2/capture-app/compare/v100.41.2...v100.41.3) (2023-10-16) + + +### Bug Fixes + +* [DHIS2-15782] navigation with program stage filter ([#3431](https://github.com/dhis2/capture-app/issues/3431)) ([8f7a017](https://github.com/dhis2/capture-app/commit/8f7a017c5860104620c6b7fc8c20916b00037302)) + ## [100.41.2](https://github.com/dhis2/capture-app/compare/v100.41.1...v100.41.2) (2023-10-12) diff --git a/config/eslint/alias.js b/config/eslint/alias.js index 553e8b60a6..345a65607b 100644 --- a/config/eslint/alias.js +++ b/config/eslint/alias.js @@ -14,4 +14,4 @@ if (fileExists(path.resolve(__dirname, '../../src/core_modules/capture-core'))) alias['capture-core-utils'] = path.resolve(__dirname, '../../src/core_modules/capture-core-utils'); } -module.exports = alias; \ No newline at end of file +module.exports = alias; diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 0000000000..837af5437d --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,35 @@ +const { chromeAllowXSiteCookies } = require('@dhis2/cypress-plugins'); +const { defineConfig } = require('cypress'); +const getCypressEnvVariables = require('./cypress/support/getCypressEnvVariables'); +const cucumberPreprocessor = require('./cypress/support/cucumberPreprocessor'); + +async function setupNodeEvents(on, config) { + await chromeAllowXSiteCookies(on); + await cucumberPreprocessor(on, config); + + config.env = getCypressEnvVariables(config); + return config; +} + +module.exports = defineConfig({ + video: true, + dhis2_datatest_prefix: 'dhis2-capture', + chromeWebSecurityComment: + 'chromeWebSecurity should removed once https://github.com/cypress-io/cypress/issues/4220 is fixed', + chromeWebSecurity: false, + defaultCommandTimeout: 25000, + projectId: '322xnh', + experimentalFetchPolyfill: true, + retries: { + runMode: 3, + }, + env: { + dhis2DataTestPrefix: 'capture-app', + networkMode: 'live', + }, + e2e: { + setupNodeEvents, + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/e2e/**/*.feature', + }, +}); diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 1520cffbe2..0000000000 --- a/cypress.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "baseUrl": "http://localhost:3000", - "video": true, - "testFiles": "**/*.feature", - "dhis2_datatest_prefix": "dhis2-capture", - "chromeWebSecurityComment": "chromeWebSecurity should removed once https://github.com/cypress-io/cypress/issues/4220 is fixed", - "chromeWebSecurity": false, - "defaultCommandTimeout": 25000, - "projectId": "322xnh", - "experimentalFetchPolyfill": true, - "retries": { - "runMode": 3 - }, - "env": { - "dhis2DataTestPrefix": "capture-app", - "networkMode": "live" - } -} \ No newline at end of file diff --git a/cypress/integration/AllAccessibleRecordsPage.feature b/cypress/e2e/AllAccessibleRecordsPage.feature similarity index 100% rename from cypress/integration/AllAccessibleRecordsPage.feature rename to cypress/e2e/AllAccessibleRecordsPage.feature diff --git a/cypress/integration/AllAccessibleRecordsPage/index.js b/cypress/e2e/AllAccessibleRecordsPage/index.js similarity index 96% rename from cypress/integration/AllAccessibleRecordsPage/index.js rename to cypress/e2e/AllAccessibleRecordsPage/index.js index a9468a7018..dfd48e73ef 100644 --- a/cypress/integration/AllAccessibleRecordsPage/index.js +++ b/cypress/e2e/AllAccessibleRecordsPage/index.js @@ -1,3 +1,5 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; + import '../sharedSteps'; Given('the user is on the the main page', () => { diff --git a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageForm.feature b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm.feature similarity index 91% rename from cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageForm.feature rename to cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm.feature index f1c0da9caf..966cd9083f 100644 --- a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageForm.feature +++ b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm.feature @@ -48,7 +48,7 @@ Feature: User interacts with the Enrollment New Event Workspace Scenario: User should be asked to create new event after completing a stage and choose to cancel Given you open the main page with Ngelehun and Malaria focus investigation context - And you opt in to use the new enrollment Dashboard for Malaria focus investigation + And you opt in to use the new enrollment Dashboard for Malaria focus investigation Then you land on the enrollment new event page by having typed #/enrollmentEventNew?enrollmentId=zRfAPUpjoG3&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&stageId=CWaAcQYKVpq&teiId=S3JjTA4QMNe Then you see the following Enrollment: New Event And you see the widget header Foci investigation & classification @@ -72,3 +72,9 @@ Feature: User interacts with the Enrollment New Event Workspace When you choose option Yes, create new event in the modal Then you will be navigate to page #/enrollmentEventNew?enrollmentId=zRfAPUpjoG3&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&teiId=S3JjTA4QMNe + Scenario: User is able to schedule an event with a note + Given you land on the enrollment new event page by having typed /#/enrollmentEventNew?enrollmentId=qcFFRp7DpcX&orgUnitId=DiszpKrYNg8&programId=WSGAb5XwJ3Y&stageId=edqlbukwRfQ&teiId=erqa3phUfpI + And you see the following Enrollment: New Event + And you select the schedule tab + When you add a comment to the event + And the events saves successfully diff --git a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageForm/index.js b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/index.js similarity index 87% rename from cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageForm/index.js rename to cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/index.js index c7b9294952..d4b2f403a9 100644 --- a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageForm/index.js +++ b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageForm/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; const showAllEventsInProgramStage = () => { @@ -29,6 +30,32 @@ Given(/^you land on the enrollment new event page by having typed (.*)$/, (url) cy.visit(url); }); +Given('you select the schedule tab', () => { + cy.get('[data-test="new-event-schedule-tab"]') + .click(); +}); + +When('you add a comment to the event', () => { + cy.get('[data-test="comment-textfield"]') + .type('This is a comment') + .blur(); + + cy.get('[data-test="add-comment-btn"]') + .click(); +}); + +And('the events saves successfully', () => { + cy.intercept('POST', '**/tracker?async=false').as('postEvent'); + + cy.get('[data-test="dhis2-uicore-button"]') + .contains('Schedule') + .click(); + + cy.wait('@postEvent') + .its('response.statusCode') + .should('eq', 200); +}); + When(/^you click the create new button number (.*)$/, (eq) => { cy.get('[data-test="create-new-button"]') .eq(eq) @@ -58,19 +85,6 @@ When(/^you click the checkbox number (.*)$/, (eq) => { .click(); }); -When(/^you click the (.*) button/, (buttonText) => { - cy.intercept({ - method: 'POST', - url: '**/tracker?async=false', - }).as('postEvents'); - - cy.get('[data-test="dhis2-uicore-button"]') - .contains(buttonText) - .click(); - - cy.wait('@postEvents'); -}); - When(/^you click the button to (.*) without post request/, (buttonText) => { cy.get('[data-test="dhis2-uicore-button"]') .contains(buttonText) diff --git a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation.feature b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation.feature similarity index 100% rename from cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation.feature rename to cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation.feature diff --git a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/index.js b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/index.js similarity index 88% rename from cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/index.js rename to cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/index.js index 593b33077a..b2f298297d 100644 --- a/cypress/integration/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/index.js +++ b/cypress/e2e/EnrollmentAddEventPage/EnrollmentAddEventPageNavigation/index.js @@ -1,3 +1,4 @@ +import { Given, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; Given(/^you land on the enrollment (.*) page by having typed (.*)$/, (_, url) => { diff --git a/cypress/integration/EnrollmentAddEventPage/ProgramStageSelector.feature b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector.feature similarity index 100% rename from cypress/integration/EnrollmentAddEventPage/ProgramStageSelector.feature rename to cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector.feature diff --git a/cypress/integration/EnrollmentAddEventPage/ProgramStageSelector/index.js b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/index.js similarity index 89% rename from cypress/integration/EnrollmentAddEventPage/ProgramStageSelector/index.js rename to cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/index.js index ddb02bd66d..6c8eac1dac 100644 --- a/cypress/integration/EnrollmentAddEventPage/ProgramStageSelector/index.js +++ b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/index.js @@ -1,3 +1,5 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; + Given('you land on the EnrollmentEventNew-page without a stageId', () => { cy.visit('/#/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=x2kJgpb0XQC&enrollmentId=RiNIt1yJoge'); }); diff --git a/cypress/integration/EnrollmentAddEventPage/sharedSteps.js b/cypress/e2e/EnrollmentAddEventPage/sharedSteps.js similarity index 83% rename from cypress/integration/EnrollmentAddEventPage/sharedSteps.js rename to cypress/e2e/EnrollmentAddEventPage/sharedSteps.js index 855e6e90f1..9be509e28c 100644 --- a/cypress/integration/EnrollmentAddEventPage/sharedSteps.js +++ b/cypress/e2e/EnrollmentAddEventPage/sharedSteps.js @@ -1,3 +1,5 @@ +import { Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; + Then(/^you see the following (.*)$/, (message) => { cy.contains(message); }); diff --git a/cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageForm.feature b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm.feature similarity index 100% rename from cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageForm.feature rename to cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm.feature diff --git a/cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageForm/index.js b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/index.js similarity index 97% rename from cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageForm/index.js rename to cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/index.js index 3651052988..3c808d775d 100644 --- a/cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageForm/index.js +++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageForm/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; import { getCurrentYear } from '../../../support/date'; import '../../sharedSteps'; diff --git a/cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation.feature b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation.feature similarity index 100% rename from cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation.feature rename to cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation.feature diff --git a/cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/index.js b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/index.js similarity index 92% rename from cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/index.js rename to cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/index.js index a51cbe9816..76fbb1915c 100644 --- a/cypress/integration/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/index.js +++ b/cypress/e2e/EnrollmentEditEventPage/EnrollmentEditEventPageNavigation/index.js @@ -1,3 +1,5 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; + Given(/^you land on the enrollment event page by having typed (.*)$/, (url) => { cy.visit(url); cy.get('[data-test="person-selector-container"]').contains('Person'); diff --git a/cypress/integration/EnrollmentPage/BreakingTheGlass.feature b/cypress/e2e/EnrollmentPage/BreakingTheGlass.feature similarity index 93% rename from cypress/integration/EnrollmentPage/BreakingTheGlass.feature rename to cypress/e2e/EnrollmentPage/BreakingTheGlass.feature index a3600593a6..ac67d53256 100644 --- a/cypress/integration/EnrollmentPage/BreakingTheGlass.feature +++ b/cypress/e2e/EnrollmentPage/BreakingTheGlass.feature @@ -1,5 +1,7 @@ Feature: Breaking the glass page + # TODO - Flaky tests should be fixed by TECH-1662 + @skip Scenario: User with search scope access tries to access an enrollment in a protected program Given the tei created by this test is cleared from the database And you opt temporarily in on new enrollment dashboard in Child programme and WHO RMNCH Tracker diff --git a/cypress/integration/EnrollmentPage/BreakingTheGlass/index.js b/cypress/e2e/EnrollmentPage/BreakingTheGlass/index.js similarity index 97% rename from cypress/integration/EnrollmentPage/BreakingTheGlass/index.js rename to cypress/e2e/EnrollmentPage/BreakingTheGlass/index.js index 70a2fdad96..120dc0a0dc 100644 --- a/cypress/integration/EnrollmentPage/BreakingTheGlass/index.js +++ b/cypress/e2e/EnrollmentPage/BreakingTheGlass/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; Given('the tei created by this test is cleared from the database', () => { @@ -89,6 +90,7 @@ And('you log out', () => { }); And('you log in as tracker2 user', () => { + cy.clearCookies(); cy.visit('/').then(() => { cy.get('#j_username').type('tracker2'); cy.get('#j_password').type('Tracker@123'); diff --git a/cypress/integration/EnrollmentPage/EnrollmentPageNavigation.feature b/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation.feature similarity index 100% rename from cypress/integration/EnrollmentPage/EnrollmentPageNavigation.feature rename to cypress/e2e/EnrollmentPage/EnrollmentPageNavigation.feature diff --git a/cypress/integration/EnrollmentPage/EnrollmentPageNavigation/index.js b/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/index.js similarity index 97% rename from cypress/integration/EnrollmentPage/EnrollmentPageNavigation/index.js rename to cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/index.js index 518d2416c3..bdc1d2f189 100644 --- a/cypress/integration/EnrollmentPage/EnrollmentPageNavigation/index.js +++ b/cypress/e2e/EnrollmentPage/EnrollmentPageNavigation/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../../sharedSteps'; Given('you are on an enrollment page', () => { @@ -105,10 +106,6 @@ When('you reset the tei selection', () => { Then('you are navigated to the main page', () => { cy.url().should('include', `${Cypress.config().baseUrl}/#/?orgUnitId=UgYg0YW7ZIh&programId=IpHINAT79UW`); }); -When('you reset the program selection', () => { - cy.get('[data-test="program-selector-container-clear-icon"]') - .click(); -}); Then('you see message explaining you need to select a program', () => { cy.url().should('not.include', 'programId'); diff --git a/cypress/integration/EnrollmentPage/EnrollmentQuickActions.feature b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions.feature similarity index 100% rename from cypress/integration/EnrollmentPage/EnrollmentQuickActions.feature rename to cypress/e2e/EnrollmentPage/EnrollmentQuickActions.feature diff --git a/cypress/integration/EnrollmentPage/EnrollmentQuickActions/index.js b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/index.js similarity index 93% rename from cypress/integration/EnrollmentPage/EnrollmentQuickActions/index.js rename to cypress/e2e/EnrollmentPage/EnrollmentQuickActions/index.js index 76142ded00..23ff019cf3 100644 --- a/cypress/integration/EnrollmentPage/EnrollmentQuickActions/index.js +++ b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/index.js @@ -1,3 +1,5 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; + Given('you are on an enrollment page with stage available', () => { cy.visit('/#/enrollment?programId=ur1Edk5Oe2n&orgUnitId=UgYg0YW7ZIh&teiId=zmgVvEZ91Kg&enrollmentId=xRnBV5aJDeF'); cy.get('[data-test="enrollment-page-content"]') diff --git a/cypress/integration/EnrollmentPage/HiddenProgramStage.feature b/cypress/e2e/EnrollmentPage/HiddenProgramStage.feature similarity index 100% rename from cypress/integration/EnrollmentPage/HiddenProgramStage.feature rename to cypress/e2e/EnrollmentPage/HiddenProgramStage.feature diff --git a/cypress/integration/EnrollmentPage/HiddenProgramStage/index.js b/cypress/e2e/EnrollmentPage/HiddenProgramStage/index.js similarity index 77% rename from cypress/integration/EnrollmentPage/HiddenProgramStage/index.js rename to cypress/e2e/EnrollmentPage/HiddenProgramStage/index.js index f9e5cf69c2..6147d114cc 100644 --- a/cypress/integration/EnrollmentPage/HiddenProgramStage/index.js +++ b/cypress/e2e/EnrollmentPage/HiddenProgramStage/index.js @@ -1,3 +1,4 @@ +import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'; import moment from 'moment'; const cleanUpIfApplicable = () => { @@ -7,13 +8,15 @@ const cleanUpIfApplicable = () => { ) .then(url => cy.request(url)) .then(({ body }) => { - const enrollment = body.enrollments?.find(e => e.enrollment === 'fmhIsWXVDmS'); - const event = enrollment?.events?.find(e => e.programStage === 'PFDfvmGpsR3'); + // TODO - Cypress does not handle optional chaining - figure out why + const enrollment = body.enrollments && body.enrollments.find(e => e.enrollment === 'fmhIsWXVDmS'); + const event = enrollment && enrollment.events && enrollment + .events + .find(e => e.programStage === 'PFDfvmGpsR3'); if (!event) { return null; } - return cy - .buildApiUrl('events', event.event) + return cy.buildApiUrl('events', event.event) .then(eventUrl => cy.request('DELETE', eventUrl)); }); diff --git a/cypress/integration/EnrollmentPage/StagesAndEventsWidget.feature b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget.feature similarity index 90% rename from cypress/integration/EnrollmentPage/StagesAndEventsWidget.feature rename to cypress/e2e/EnrollmentPage/StagesAndEventsWidget.feature index 480c827c0a..cc60abeef8 100644 --- a/cypress/integration/EnrollmentPage/StagesAndEventsWidget.feature +++ b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget.feature @@ -6,13 +6,16 @@ Feature: User interacts with Stages and Events Widget Scenario: User can close the Stages and Events Widget Given you open the enrollment page + And the program stages should be displayed When you click the stages and events widget toggle open close button Then the stages and events widget should be closed Scenario: User can close and reopen the Stages and Events Widget Given you open the enrollment page + And the program stages should be displayed + When you click the stages and events widget toggle open close button + Then the stages and events widget should be closed When you click the stages and events widget toggle open close button - And you click the stages and events widget toggle open close button Then the program stages should be displayed Scenario: User can view the list of events @@ -21,15 +24,11 @@ Feature: User interacts with Stages and Events Widget And you see the first 5 events in the table And you see buttons in the footer list - Scenario: User can view more events + Scenario: User can view more events and then view less Given you open the enrollment page which has multiples events and stages When you click show more button in stages&event list Then more events should be displayed And reset button should be displayed - - Scenario: User can reset events - Given you open the enrollment page which has multiples events and stages - When you click show more button in stages&event list And you click reset button Then there should be 5 rows in the table diff --git a/cypress/integration/EnrollmentPage/StagesAndEventsWidget/index.js b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/index.js similarity index 89% rename from cypress/integration/EnrollmentPage/StagesAndEventsWidget/index.js rename to cypress/e2e/EnrollmentPage/StagesAndEventsWidget/index.js index 13c5dc60eb..7d867b0437 100644 --- a/cypress/integration/EnrollmentPage/StagesAndEventsWidget/index.js +++ b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import { getCurrentYear } from '../../../support/date'; import '../sharedSteps'; @@ -22,7 +23,6 @@ Then('the stages and events widget should be closed', () => { cy.get('[data-test="stages-and-events-widget"]') .within(() => { cy.get('[data-test="widget-contents"]') - .children() .should('not.exist'); cy.contains('Birth').should('not.exist'); }); @@ -114,23 +114,29 @@ Then('the default list should be displayed', () => { }); When(/^you sort list asc by (.*)$/, (columnName) => { - cy.get('[data-test="stages-and-events-widget"]') - .find('[data-test="widget-contents"]') + cy.get('[data-test="stages-and-events-widget"]').should('exist') + .find('[data-test="widget-contents"]').should('exist') .find('[data-test="stage-content"]') + .should('exist') .eq(2) .find('thead') + .should('exist') .find('th') + .should('exist') + .contains(columnName) + .parent() + .should('exist') .within(() => { - cy.contains('th', columnName) - .find('button') - .click(); - cy.wait(100); - cy.contains('th', columnName) - .find('button') - .click(); + cy.get('button').should('exist') // Use cy.get() instead of cy.find() + .click() + .then(() => { + // Perform further actions or assertions if needed + cy.get('button').should('exist').click(); + }); }); }); + Then(/^the sorted list by (.*) asc should be displayed$/, () => { const rows = [ '07-13|Bumbeh MCHP', diff --git a/cypress/integration/EnrollmentPage/sharedSteps.js b/cypress/e2e/EnrollmentPage/sharedSteps.js similarity index 79% rename from cypress/integration/EnrollmentPage/sharedSteps.js rename to cypress/e2e/EnrollmentPage/sharedSteps.js index 8dea95f553..8f8e2992ca 100644 --- a/cypress/integration/EnrollmentPage/sharedSteps.js +++ b/cypress/e2e/EnrollmentPage/sharedSteps.js @@ -1,3 +1,5 @@ +import { Given } from '@badeball/cypress-cucumber-preprocessor'; + Given('you open the enrollment page', () => { cy.visit('#/enrollment?enrollmentId=wBU0RAsYjKE'); }); diff --git a/cypress/integration/MainPage.feature b/cypress/e2e/MainPage.feature similarity index 95% rename from cypress/integration/MainPage.feature rename to cypress/e2e/MainPage.feature index c95a65b9ff..70a5430db6 100644 --- a/cypress/integration/MainPage.feature +++ b/cypress/e2e/MainPage.feature @@ -1,6 +1,6 @@ Feature: User interacts with Main page - Scenario: The Working list is displayed + Scenario: The Working list is displayed Given you are in the main page with no selections made And the user selects the program Child Programme And the user selects the org unit Ngelehun CHC @@ -29,7 +29,7 @@ Feature: User interacts with Main page Then the current url is /#/?orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&selectedTemplateId=PpGINOT00UX Scenario: You are redirected to create a custom working list - Given you are in the search page with Ngelehun and MNCH / PNC context + Given you are in the search page with Ngelehun and MNCH PNC context And the search form is displayed When the user clicks the element containing the text: Create saved list Then the current url is /#/?orgUnitId=DiszpKrYNg8&programId=uy2gU8kT1jF&selectedTemplateId=uy2gU8kT1jF-default @@ -41,4 +41,4 @@ Feature: User interacts with Main page When you opt in to use the new enrollment Dashboard for Child Programme Then you see the opt out component for Child Programme When you opt out to use the new enrollment Dashboard for Child Programme - Then you see the opt in component for Child Programme \ No newline at end of file + Then you see the opt in component for Child Programme diff --git a/cypress/integration/MainPage/index.js b/cypress/e2e/MainPage/index.js similarity index 89% rename from cypress/integration/MainPage/index.js rename to cypress/e2e/MainPage/index.js index 60fcc41f37..f004659122 100644 --- a/cypress/integration/MainPage/index.js +++ b/cypress/e2e/MainPage/index.js @@ -1,6 +1,7 @@ +import { Given, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; -Given('you are in the search page with Ngelehun and MNCH / PNC context', () => { +Given('you are in the search page with Ngelehun and MNCH PNC context', () => { cy.visit('/#/search?orgUnitId=DiszpKrYNg8&programId=uy2gU8kT1jF'); }); diff --git a/cypress/integration/NewEventThroughAddRelationship.feature b/cypress/e2e/NewEventThroughAddRelationship.feature similarity index 100% rename from cypress/integration/NewEventThroughAddRelationship.feature rename to cypress/e2e/NewEventThroughAddRelationship.feature diff --git a/cypress/integration/NewEventThroughAddRelationship/index.js b/cypress/e2e/NewEventThroughAddRelationship/index.js similarity index 96% rename from cypress/integration/NewEventThroughAddRelationship/index.js rename to cypress/e2e/NewEventThroughAddRelationship/index.js index f0fb89eb86..d525b595a4 100644 --- a/cypress/integration/NewEventThroughAddRelationship/index.js +++ b/cypress/e2e/NewEventThroughAddRelationship/index.js @@ -1,3 +1,4 @@ +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; import { v4 as uuid } from 'uuid'; import '../sharedSteps'; @@ -15,8 +16,7 @@ When('you add data to the form', () => { }); When('you submit the form', () => { - cy.server(); - cy.route('POST', '**/tracker?async=false').as('postData'); + cy.intercept('POST', '**/tracker?async=false').as('postData'); cy.get('[data-test="dhis2-uicore-splitbutton-button"]') .click(); }); @@ -24,7 +24,7 @@ When('you submit the form', () => { Then('the event should be sent to the server successfully', () => { cy.wait('@postData', { timeout: 30000 }) .then((result) => { - expect(result.status).to.equal(200); + expect(result.response.statusCode).to.equal(200); // clean up const id = result.response.body.bundleReport.typeReportMap.EVENT.objectReports[0].uid; cy.buildApiUrl('events', id) diff --git a/cypress/integration/NewPage.feature b/cypress/e2e/NewPage.feature similarity index 98% rename from cypress/integration/NewPage.feature rename to cypress/e2e/NewPage.feature index 85aaa709d3..220ca7e971 100644 --- a/cypress/integration/NewPage.feature +++ b/cypress/e2e/NewPage.feature @@ -1,9 +1,19 @@ Feature: User creates a new entries from the registration page + Scenario: Viewing the registration page without any selections Given you are on the default registration page Then there should be informative message explaining you need to select an organisation unit + @v>=41 + Scenario: New person in Tracker Program > Filling the Allergies with multiple options + Given you are in the WHO RMNCH program registration page + When you fill in multiple Allergies options + Then you can see the multiple selections in the form + And you fill the WHO RMNCH program registration form with its required unique values + And you click the save person submit button + Then you are navigated to the WHO RMNCH program in Tracker Capture app + Scenario: Viewing the registration page with incomplete program categories selection Given you are in the main page with no selections made And you select org unit @@ -108,10 +118,10 @@ Feature: User creates a new entries from the registration page ### New Person Scenario: New person > Submitting the form with unique name navigates you to the user dashboard - Given you are in the Person registration page - When you fill in a unique first name - And you click the save person submit button - Then you are navigated to the Tracker Capture + Given you are in the Person registration page + When you fill in a unique first name + And you click the save person submit button + Then you are navigated to the Tracker Capture Scenario: New person > Submitting the form from the duplicates modal navigates you to the user dashboard Given you are in the Person registration page @@ -166,14 +176,6 @@ Feature: User creates a new entries from the registration page When you submit the form again from the duplicates modal Then you are navigated to the WHO RMNCH program in Tracker Capture app -@v>=41 - Scenario: New person in Tracker Program > Filling the Allergies with multiple options - Given you are in the WHO RMNCH program registration page - When you fill in multiple Allergies options - Then you can see the multiple selections in the form - And you fill the WHO RMNCH program registration form with its required unique values - And you click the save person submit button - Then you are navigated to the WHO RMNCH program in Tracker Capture app Scenario: New person in Tracker Program > Submitting the form shows a list with duplicates Given you are in Child programme registration page diff --git a/cypress/integration/NewPage/index.js b/cypress/e2e/NewPage/index.js similarity index 99% rename from cypress/integration/NewPage/index.js rename to cypress/e2e/NewPage/index.js index 4cc6af0afb..901452f440 100644 --- a/cypress/integration/NewPage/index.js +++ b/cypress/e2e/NewPage/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import moment from 'moment'; import { getCurrentYear } from '../../support/date'; import '../sharedSteps'; diff --git a/cypress/integration/ScopeSelector.feature b/cypress/e2e/ScopeSelector.feature similarity index 98% rename from cypress/integration/ScopeSelector.feature rename to cypress/e2e/ScopeSelector.feature index ec80a5d2b0..7610b86994 100644 --- a/cypress/integration/ScopeSelector.feature +++ b/cypress/e2e/ScopeSelector.feature @@ -28,9 +28,10 @@ Feature: User uses the ScopeSelector to navigate When you select both org unit and program Malaria case registration Then you should see the table - 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 +# TODO - App crashes on invalid program id (DHIS2-16010) +# 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 Scenario: Main page > Url with invalid org unit id Given you land on a main page with an invalid org unit id diff --git a/cypress/integration/ScopeSelector/index.js b/cypress/e2e/ScopeSelector/index.js similarity index 97% rename from cypress/integration/ScopeSelector/index.js rename to cypress/e2e/ScopeSelector/index.js index 79fa8f698c..3daaa87f6e 100644 --- a/cypress/integration/ScopeSelector/index.js +++ b/cypress/e2e/ScopeSelector/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import { getCurrentYear } from '../../support/date'; import '../sharedSteps'; @@ -231,18 +232,6 @@ Then('you are navigated to the search page with the same org unit and program Ch cy.url().should('eq', `${Cypress.config().baseUrl}/#/search?orgUnitId=DiszpKrYNg8&programId=IpHINAT79UW`); }); -Then('there should be visible a title with Child Program', () => { - cy.get('[data-test="search-page-content"]') - .contains('person in program: Child Programme') - .should('exist'); -}); - -And('there should be Child Programme domain forms visible to search with', () => { - cy.get('[data-test="search-page-content"]') - .find('[data-test="capture-ui-input"]') - .should('have.length', 1); -}); - const selectedChildProgram = ['Program', 'Child Programme']; const selectedMalariaProgram = ['Program', 'Malaria case diagnosis']; const selectedEventProgram = ['Program', 'Antenatal care visit']; diff --git a/cypress/integration/SearchForDuplicatesThroughAddRelationship.feature b/cypress/e2e/SearchForDuplicatesThroughAddRelationship.feature similarity index 100% rename from cypress/integration/SearchForDuplicatesThroughAddRelationship.feature rename to cypress/e2e/SearchForDuplicatesThroughAddRelationship.feature diff --git a/cypress/integration/SearchForDuplicatesThroughAddRelationship/index.js b/cypress/e2e/SearchForDuplicatesThroughAddRelationship/index.js similarity index 94% rename from cypress/integration/SearchForDuplicatesThroughAddRelationship/index.js rename to cypress/e2e/SearchForDuplicatesThroughAddRelationship/index.js index 9110c3f6d2..388903e301 100644 --- a/cypress/integration/SearchForDuplicatesThroughAddRelationship/index.js +++ b/cypress/e2e/SearchForDuplicatesThroughAddRelationship/index.js @@ -1,3 +1,4 @@ +import { When, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; And('you fill in the first name with values that have duplicates', () => { diff --git a/cypress/integration/SearchPage.feature b/cypress/e2e/SearchPage.feature similarity index 100% rename from cypress/integration/SearchPage.feature rename to cypress/e2e/SearchPage.feature diff --git a/cypress/integration/SearchPage/index.js b/cypress/e2e/SearchPage/index.js similarity index 99% rename from cypress/integration/SearchPage/index.js rename to cypress/e2e/SearchPage/index.js index 93d0212d34..b322ef00bc 100644 --- a/cypress/integration/SearchPage/index.js +++ b/cypress/e2e/SearchPage/index.js @@ -1,3 +1,4 @@ +import { Given, Then, When, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; Given('you are on the default search page', () => { diff --git a/cypress/integration/SearchThroughAddRelationship.feature b/cypress/e2e/SearchThroughAddRelationship.feature similarity index 100% rename from cypress/integration/SearchThroughAddRelationship.feature rename to cypress/e2e/SearchThroughAddRelationship.feature diff --git a/cypress/integration/SearchThroughAddRelationship/index.js b/cypress/e2e/SearchThroughAddRelationship/index.js similarity index 97% rename from cypress/integration/SearchThroughAddRelationship/index.js rename to cypress/e2e/SearchThroughAddRelationship/index.js index 8839e685a6..4d46ed2b22 100644 --- a/cypress/integration/SearchThroughAddRelationship/index.js +++ b/cypress/e2e/SearchThroughAddRelationship/index.js @@ -1,3 +1,4 @@ +import { When, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; And('you select search scope TB program', () => { diff --git a/cypress/integration/SmokeTests.feature b/cypress/e2e/SmokeTests.feature similarity index 100% rename from cypress/integration/SmokeTests.feature rename to cypress/e2e/SmokeTests.feature diff --git a/cypress/integration/SmokeTests/index.js b/cypress/e2e/SmokeTests/index.js similarity index 85% rename from cypress/integration/SmokeTests/index.js rename to cypress/e2e/SmokeTests/index.js index eff621b5e0..7deac3868d 100644 --- a/cypress/integration/SmokeTests/index.js +++ b/cypress/e2e/SmokeTests/index.js @@ -1,3 +1,6 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; +import '../sharedSteps'; + Then('you should see the app main selections', () => { cy.get('[data-test="org-unit-selector-container"]'); cy.get('[data-test="program-selector-container"]'); @@ -36,12 +39,6 @@ When('you sign in', () => { .submit(); }); -Then('you should see the app main selections', () => { - cy.get('[data-test="org-unit-selector-container"]'); - - cy.get('[data-test="program-selector-container"]'); -}); - Then('you should see the header bar', () => { cy.get('[data-test="headerbar-title"]'); }); diff --git a/cypress/integration/StageEventListPage/StageEventListNavigation.feature b/cypress/e2e/StageEventListPage/StageEventListNavigation.feature similarity index 100% rename from cypress/integration/StageEventListPage/StageEventListNavigation.feature rename to cypress/e2e/StageEventListPage/StageEventListNavigation.feature diff --git a/cypress/integration/StageEventListPage/StageEventListNavigation/index.js b/cypress/e2e/StageEventListPage/StageEventListNavigation/index.js similarity index 83% rename from cypress/integration/StageEventListPage/StageEventListNavigation/index.js rename to cypress/e2e/StageEventListPage/StageEventListNavigation/index.js index b160ccb7f6..2aa919ca13 100644 --- a/cypress/integration/StageEventListPage/StageEventListNavigation/index.js +++ b/cypress/e2e/StageEventListPage/StageEventListNavigation/index.js @@ -1,3 +1,5 @@ +import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'; + Given('you are visiting Stage Event List Page by url', () => { cy.visit('/#/enrollment/stageEvents?enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR&orgUnitId=DiszpKrYNg8&programId=IpHINAT79UW'); }); diff --git a/cypress/integration/TopBarActions.feature b/cypress/e2e/TopBarActions.feature similarity index 100% rename from cypress/integration/TopBarActions.feature rename to cypress/e2e/TopBarActions.feature diff --git a/cypress/integration/TopBarActions/index.js b/cypress/e2e/TopBarActions/index.js similarity index 91% rename from cypress/integration/TopBarActions/index.js rename to cypress/e2e/TopBarActions/index.js index 36a5c77f8e..24321c87fc 100644 --- a/cypress/integration/TopBarActions/index.js +++ b/cypress/e2e/TopBarActions/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; import '../sharedSteps'; Given(/^you land on a enrollment page domain by having typed (.*)$/, (url) => { diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetEnrollment/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js similarity index 93% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetEnrollment/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js index 4584e1ba2a..be1a87296a 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetEnrollment/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollment/index.js @@ -1,4 +1,5 @@ -const { getCurrentYear } = require('../../../support/date'); +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; +import { getCurrentYear } from '../../../support/date'; When('you click the enrollment widget toggle open close button', () => { cy.get('[data-test="widget-enrollment"]').within(() => { @@ -8,13 +9,13 @@ When('you click the enrollment widget toggle open close button', () => { Then('the enrollment widget should be closed', () => { cy.get('[data-test="widget-enrollment"]').within(() => { - cy.get('[data-test="widget-contents"]').children().should('not.exist'); + cy.get('[data-test="widget-contents"]').should('not.exist'); }); }); Then('the enrollment widget should be opened', () => { cy.get('[data-test="widget-enrollment"]').within(() => { - cy.get('[data-test="widget-contents"]').children().should('exist'); + cy.get('[data-test="widget-enrollment-contents"]').children().should('exist'); }); }); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetEnrollmentComment/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollmentComment/index.js similarity index 90% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetEnrollmentComment/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollmentComment/index.js index 1d0c464bc9..1e97d61e19 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetEnrollmentComment/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEnrollmentComment/index.js @@ -1,8 +1,9 @@ +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; + Then('the stages and events should be loaded', () => { cy.contains('Stages and Events').should('exist'); }); - When(/^you fill in the comment: (.*)$/, (comment) => { cy.get('[data-test="enrollment-comment-widget"]').within(() => { cy.get('[data-test="comment-textfield"]').type(comment); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetEventComment/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventComment/index.js similarity index 92% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetEventComment/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetEventComment/index.js index 96211b0803..83fbe2c68d 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetEventComment/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetEventComment/index.js @@ -1,3 +1,5 @@ +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; + Then('the enrollment widget should be loaded', () => { cy.contains('The enrollment event data could not be found').should('not.exist'); }); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetProfile/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetProfile/index.js similarity index 95% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetProfile/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetProfile/index.js index 8ac1a3f29b..31370a0606 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetProfile/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetProfile/index.js @@ -1,3 +1,4 @@ +import { Then } from '@badeball/cypress-cucumber-preprocessor'; import '../../sharedSteps'; Then('the profile details should be displayed', () => { @@ -16,7 +17,6 @@ Then('the widget profile should be closed', () => { cy.get('[data-test="profile-widget"]') .within(() => { cy.get('[data-test="widget-contents"]') - .children() .should('not.exist'); }); }); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetTab/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetTab/index.js similarity index 86% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetTab/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetTab/index.js index 085b0950ba..036671a649 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetTab/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetTab/index.js @@ -1,4 +1,5 @@ -const { getCurrentYear } = require('../../../support/date'); +import { When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; +import { getCurrentYear } from '../../../support/date'; Then(/you should see tabs: (.*)/, (tabNames) => { const tabs = tabNames.split(','); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage.feature similarity index 92% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage.feature rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage.feature index f5db1e30e2..24017fb326 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage.feature @@ -16,13 +16,16 @@ Feature: The user interacts with the widgets on the enrollment add event page Scenario: User can close the Enrollment Widget Given you land on the enrollment add event page by having typed #/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=EaOyKGOIGRp&enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR + And the enrollment widget should be opened When you click the enrollment widget toggle open close button Then the enrollment widget should be closed Scenario: User can close and reopen the Enrollment Widget Given you land on the enrollment add event page by having typed #/enrollmentEventNew?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=EaOyKGOIGRp&enrollmentId=wBU0RAsYjKE&stageId=A03MvHHogjR + And the enrollment widget should be opened + When you click the enrollment widget toggle open close button + Then the enrollment widget should be closed When you click the enrollment widget toggle open close button - And you click the enrollment widget toggle open close button Then the enrollment widget should be opened Scenario: User can see the enrollment details @@ -96,3 +99,8 @@ Feature: The user interacts with the widgets on the enrollment add event page When you click switch tab to Schedule Then you should see Schedule tab And you should see suggested date: 08-01 + + Scenario: You can assign a user when scheduling an event + Given you land on the enrollment edit event page by having typed /#/enrollmentEventNew?enrollmentId=zRfAPUpjoG3&orgUnitId=DiszpKrYNg8&programId=M3xtLkYBlKI&stageId=uvMKOn1oWvd&teiId=S3JjTA4QMNe + When you click switch tab to Schedule + Then you can assign a user when scheduling the event diff --git a/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/index.js new file mode 100644 index 0000000000..547b242c5a --- /dev/null +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/index.js @@ -0,0 +1,16 @@ +import { Then } from '@badeball/cypress-cucumber-preprocessor'; +import '../sharedSteps'; +import '../WidgetEnrollment'; +import '../WidgetProfile'; +import '../WidgetTab'; + +Then('you can assign a user when scheduling the event', () => { + cy.get('[data-test="assignee-section"]').within(() => { + cy.get('[data-test="capture-ui-input"]').click(); + cy.get('[data-test="capture-ui-input"]').type('Tracker demo'); + cy.contains('Tracker demo User').click(); + }); + cy.get('[data-test="assignee-section"]').within(() => { + cy.get('[data-test="dhis2-uicore-chip"]').contains('Tracker demo User').should('exist'); + }); +}); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard.feature similarity index 97% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard.feature rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard.feature index c71f98ef99..89af2a7ea9 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard.feature @@ -48,13 +48,16 @@ Feature: The user interacts with the widgets on the enrollment dashboard Scenario: User can close the Enrollment Widget Given you land on the enrollment dashboard page by having typed #/enrollment?enrollmentId=wBU0RAsYjKE + And the enrollment widget should be opened When you click the enrollment widget toggle open close button Then the enrollment widget should be closed Scenario: User can close and reopen the Enrollment Widget Given you land on the enrollment dashboard page by having typed #/enrollment?enrollmentId=wBU0RAsYjKE + And the enrollment widget should be opened + When you click the enrollment widget toggle open close button + Then the enrollment widget should be closed When you click the enrollment widget toggle open close button - And you click the enrollment widget toggle open close button Then the enrollment widget should be opened Scenario: User can see the enrollment details diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js similarity index 96% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js index 6ffc2f3caa..c0fa3ca18e 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentDashboard/index.js @@ -1,3 +1,4 @@ +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; import moment from 'moment'; import '../sharedSteps'; import '../WidgetEnrollment'; diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent.feature similarity index 96% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent.feature rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent.feature index 1f551735a3..c3add0d727 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent.feature +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent.feature @@ -16,13 +16,16 @@ Feature: The user interacts with the widgets on the enrollment edit event Scenario: User can close the Enrollment Widget Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=XGLkLlOXgmE&orgUnitId=DiszpKrYNg8 + And the enrollment widget should be opened When you click the enrollment widget toggle open close button Then the enrollment widget should be closed Scenario: User can close and reopen the Enrollment Widget Given you land on the enrollment edit event page by having typed /#/enrollmentEventEdit?eventId=XGLkLlOXgmE&orgUnitId=DiszpKrYNg8 + And the enrollment widget should be opened + When you click the enrollment widget toggle open close button + Then the enrollment widget should be closed When you click the enrollment widget toggle open close button - And you click the enrollment widget toggle open close button Then the enrollment widget should be opened Scenario: User can see the enrollment details diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/index.js similarity index 100% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEnrollmentEditEvent/index.js diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEventSchedule.feature b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEventSchedule.feature similarity index 100% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEventSchedule.feature rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEventSchedule.feature diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEventSchedule/index.js b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEventSchedule/index.js similarity index 93% rename from cypress/integration/WidgetsForEnrollmentPages/WidgetsForEventSchedule/index.js rename to cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEventSchedule/index.js index 6cc1222f24..1b606aa3c0 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEventSchedule/index.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/WidgetsForEventSchedule/index.js @@ -1,3 +1,4 @@ +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; import { getCurrentYear } from '../../../support/date'; import '../sharedSteps'; import '../WidgetTab'; diff --git a/cypress/integration/WidgetsForEnrollmentPages/sharedSteps.js b/cypress/e2e/WidgetsForEnrollmentPages/sharedSteps.js similarity index 88% rename from cypress/integration/WidgetsForEnrollmentPages/sharedSteps.js rename to cypress/e2e/WidgetsForEnrollmentPages/sharedSteps.js index f3773fc011..7cea912966 100644 --- a/cypress/integration/WidgetsForEnrollmentPages/sharedSteps.js +++ b/cypress/e2e/WidgetsForEnrollmentPages/sharedSteps.js @@ -1,3 +1,5 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; + Given(/^you land on the enrollment (.*) page by having typed (.*)$/, (_, url) => { cy.visit(url); }); diff --git a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsDev.feature b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsDev.feature similarity index 100% rename from cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsDev.feature rename to cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsDev.feature diff --git a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsDev/index.js b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsDev/index.js similarity index 75% rename from cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsDev/index.js rename to cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsDev/index.js index b1b8f6c79a..e1d79d283a 100644 --- a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsDev/index.js +++ b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsDev/index.js @@ -1,8 +1,22 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; import '../../sharedSteps'; +beforeEach(() => { + // Disable cache for chromium browsers to force the api to be called + if (Cypress.browser.family === 'chromium') { + Cypress.automation('remote:debugger:protocol', { + command: 'Network.enable', + params: {}, + }); + Cypress.automation('remote:debugger:protocol', { + command: 'Network.setCacheDisabled', + params: { cacheDisabled: true }, + }); + } +}); + Given('you open the main page with Ngelehun and malaria case context', () => { - cy.server(); - cy.route('GET', '**/tracker/events**').as('getDefaultEvents'); + cy.intercept('GET', '**/tracker/events**').as('getDefaultEvents'); cy.visit('#/?programId=VBqh0ynB2wv&orgUnitId=DiszpKrYNg8'); }); @@ -11,15 +25,15 @@ Then('events should be retrieved from the api using the default query args', () cy.wait('@getDefaultEvents', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'pageSize=15'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('events'); @@ -51,7 +65,7 @@ When('you select the working list called events assigned to anyone', () => { .contains('Events assigned to anyone') .should('exist'); - cy.route('GET', '**/tracker/events**').as('getEventsAssignedToAnyone'); + cy.intercept('GET', '**/tracker/events**').as('getEventsAssignedToAnyone'); cy.get('[data-test="workinglists-template-selector-chips-container"]') .contains('Events assigned to anyone') @@ -62,29 +76,29 @@ Then('events assigned to anyone should be retrieved from the api', () => { cy.wait('@getEventsAssignedToAnyone', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'assignedUserMode=ANY'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('events'); }); When('you apply the assignee filter', () => { - cy.route('GET', '**/tracker/events**').as('getEventsAssignedToAnyone'); + cy.intercept('GET', '**/tracker/events**').as('getEventsAssignedToAnyone'); cy.get('[data-test="list-view-filter-apply-button"]') .click(); }); When('you apply the status filter', () => { - cy.route('GET', '**/tracker/events**').as('getActiveEventsAssignedToAnyone'); + cy.intercept('GET', '**/tracker/events**').as('getActiveEventsAssignedToAnyone'); cy.get('[data-test="list-view-filter-apply-button"]') .click(); @@ -94,68 +108,48 @@ Then('active events that are assigned to anyone should be retrieved from the api cy.wait('@getActiveEventsAssignedToAnyone', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'assignedUserMode=ANY'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'status=ACTIVE'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('events'); }); -When('you set the age filter to 10-20', () => { - cy.get('[data-test="event-working-lists"]') - .contains('Age (years)') - .click(); - - cy.get('[data-test="list-view-filter-contents"]') - .find('input[placeholder="Min"]') - .type('10'); - - cy.get('[data-test="list-view-filter-contents"]') - .find('input[placeholder="Max"]') - .type('20'); -}); - When('you apply the current filter on the event working list', () => { - cy.route('GET', '**/tracker/events**').as('getEvents'); + cy.intercept('GET', '**/tracker/events**').as('getEvents'); cy.get('[data-test="list-view-filter-apply-button"]') .click(); }); -Then('the age filter button should show that the filter is in effect', () => { - cy.get('[data-test="event-working-lists"]') - .contains('Age (years): 10 to 20') - .should('exist'); -}); - Then('events where age is between 10 and 20 should be retrieved from the api', () => { cy.wait('@getEvents', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@result') - .its('url') + .its('response.url') .should('match', /filter=.*10/); cy.get('@result') - .its('url') + .its('response.url') .should('match', /filter=.*20/); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('events'); @@ -165,7 +159,7 @@ When('you click the next page button on the event working list', () => { cy.get('[data-test="search-pagination-next-page"]') .should('exist'); - cy.route('GET', '**/tracker/events**').as('getEvents'); + cy.intercept('GET', '**/tracker/events**').as('getEvents'); cy.get('[data-test="search-pagination-next-page"]') .click(); @@ -175,21 +169,21 @@ Then('new events should be retrieved from the api', () => { cy.wait('@getEvents', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@result').its('response.body.instances').as('events'); }); When('you click the previous page button on the event working list', () => { - cy.route('GET', '**/tracker/events**').as('getEvents'); + cy.intercept('GET', '**/tracker/events**').as('getEvents'); cy.get('[data-test="search-pagination-previous-page"]') .click(); }); When('you click the first page button on the event working list', () => { - cy.route('GET', '**/tracker/events**').as('getEvents'); + cy.intercept('GET', '**/tracker/events**').as('getEvents'); cy.get('[data-test="search-pagination-first-page"]') .click(); @@ -199,7 +193,7 @@ When('you change rows per page to 50', () => { cy.get('div[data-test="rows-per-page-selector"]') .should('exist'); - cy.route('GET', '**/tracker/events**').as('getEvents'); + cy.intercept('GET', '**/tracker/events**').as('getEvents'); cy.get('div[data-test="rows-per-page-selector"]') .click() @@ -211,15 +205,15 @@ Then('an event batch capped at 50 records should be retrieved from the api', () cy.wait('@getEvents', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'pageSize=50'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('events'); @@ -230,7 +224,7 @@ When('you click the report date column header', () => { .contains('Report date') .should('exist'); - cy.route('GET', '**/tracker/events**').as('getEvents'); + cy.intercept('GET', '**/tracker/events**').as('getEvents'); cy.get('[data-test="dhis2-uicore-tableheadercellaction"]') .eq(0) @@ -243,15 +237,15 @@ Then('events should be retrieved from the api ordered ascendingly by report date cy.wait('@getEvents', { timeout: 40000 }).as('resultAsc'); cy.get('@resultAsc') - .its('status') + .its('response.statusCode') .should('equal', 200); cy.get('@resultAsc') - .its('url') + .its('response.url') .should('match', /order=.*asc/); cy.get('@resultAsc') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@resultAsc').its('response.body.instances').as('events'); diff --git a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsUser.feature b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser.feature similarity index 99% rename from cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsUser.feature rename to cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser.feature index 529b1f43d4..46f86686fc 100644 --- a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsUser.feature +++ b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser.feature @@ -117,4 +117,4 @@ Given you open the main page with Ngelehun and Inpatient morbidity and mortality When you set the date of admission filter And you save the view as toDeleteWorkingList When you delete the name toDeleteWorkingList -Then the custom events working list is deleted \ No newline at end of file +Then the custom events working list is deleted diff --git a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsUser/index.js b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/index.js similarity index 94% rename from cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsUser/index.js rename to cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/index.js index cacda7707f..d16c2a84d7 100644 --- a/cypress/integration/WorkingLists/EventWorkingLists/EventWorkingListsUser/index.js +++ b/cypress/e2e/WorkingLists/EventWorkingLists/EventWorkingListsUser/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; import { v4 as uuid } from 'uuid'; import '../../sharedSteps'; import '../../../sharedSteps'; @@ -106,26 +107,6 @@ Then('the list should display active events that are assigned to anyone', () => }); }); -When('you set the age filter to 10-20', () => { - cy.get('[data-test="event-working-lists"]') - .contains('Age (years)') - .click(); - - cy.get('[data-test="list-view-filter-contents"]') - .find('input[placeholder="Min"]') - .type('10'); - - cy.get('[data-test="list-view-filter-contents"]') - .find('input[placeholder="Max"]') - .type('20'); -}); - -Then('the age filter button should show that the filter is in effect', () => { - cy.get('[data-test="event-working-lists"]') - .contains('Age (years): 10 to 20') - .should('exist'); -}); - Then('the list should display events where age is between 10 and 20', () => { const rows = [ '14 Male', @@ -274,7 +255,7 @@ Then('the list should display data ordered descendingly by report date', () => { cy.get('input[placeholder="To"]').click(); cy.contains('Update') - .click(); + .click({ force: true }); const rows = combineDataAndYear(lastYear, { '01-01': ['14 Female'], @@ -439,8 +420,7 @@ When(/^you save the view as (.*)$/, (name) => { cy.get('[data-test="view-name-content"]') .type(name); - cy.server(); - cy.route('POST', '**/eventFilters**').as('newEventFilter'); + cy.intercept('POST', '**/eventFilters**').as('newEventFilter'); cy.get('button') .contains('Save') @@ -479,7 +459,7 @@ Then('the admission filter should be in effect', () => { // clean up cy.get('@newEventResult') .then((result) => { - expect(result.status).to.equal(201); + expect(result.response.statusCode).to.equal(201); const id = result.response.body.response.uid; cy.buildApiUrl('eventFilters', id) .then((eventFiltersUrl) => { @@ -515,8 +495,7 @@ When('you delete the name toDeleteWorkingList', () => { .click(); cy.contains('Delete view') .click(); - cy.server(); - cy.route('DELETE', '**/eventFilters/**').as('deleteEventFilters'); + cy.intercept('DELETE', '**/eventFilters/**').as('deleteEventFilters'); cy.get('button') .contains('Confirm') .click(); diff --git a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsDev.feature b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsDev.feature similarity index 100% rename from cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsDev.feature rename to cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsDev.feature diff --git a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsDev/index.js b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsDev/index.js similarity index 71% rename from cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsDev/index.js rename to cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsDev/index.js index d7bbe85138..56989dd920 100644 --- a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsDev/index.js +++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsDev/index.js @@ -1,15 +1,28 @@ +import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; import '../../sharedSteps'; +beforeEach(() => { + // Disable cache for chromium browsers to force the api to be called + if (Cypress.browser.family === 'chromium') { + Cypress.automation('remote:debugger:protocol', { + command: 'Network.enable', + params: {}, + }); + Cypress.automation('remote:debugger:protocol', { + command: 'Network.setCacheDisabled', + params: { cacheDisabled: true }, + }); + } +}); + Given('you open the main page with Ngelehun and child programme context', () => { - cy.server(); - cy.route('GET', '**/tracker/trackedEntities**').as('getDefaultTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getDefaultTeis'); cy.visit('#/?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8'); }); Given('you open the main page with Ngelehun and Malaria focus investigation context', () => { - cy.server(); - cy.route('GET', '**/tracker/trackedEntities**').as('getDefaultTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getDefaultTeis'); cy.visit('#/?programId=M3xtLkYBlKI&orgUnitId=DiszpKrYNg8'); }); @@ -18,15 +31,15 @@ Then('teis should be retrieved from the api using the default query args', () => cy.wait('@getDefaultTeis', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'pageSize=15'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('teis'); @@ -46,7 +59,7 @@ When('you select the working list called Active enrollments', () => { .contains('Active enrollments') .should('exist'); - cy.route('GET', '**/tracker/trackedEntities**').as('getTeisWithEnrollmentStatusActive'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeisWithEnrollmentStatusActive'); cy.get('[data-test="workinglists-template-selector-chips-container"]') .contains('Active enrollments') @@ -55,7 +68,7 @@ When('you select the working list called Active enrollments', () => { When('you apply the enrollment status filter', () => { - cy.route('GET', '**/tracker/trackedEntities**').as('getTeisWithEnrollmentStatusActive'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeisWithEnrollmentStatusActive'); cy.get('[data-test="list-view-filter-apply-button"]') .click(); @@ -65,22 +78,22 @@ Then('teis with an active enrollment should be retrieved from the api', () => { cy.wait('@getTeisWithEnrollmentStatusActive', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'programStatus=ACTIVE'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('teis'); }); When('you apply the assignee filter', () => { - cy.route('GET', '**/tracker/trackedEntities**').as('getTeisStatusAndAssigneeFilter'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeisStatusAndAssigneeFilter'); cy.get('[data-test="list-view-filter-apply-button"]') .click(); @@ -90,26 +103,26 @@ Then('teis with active enrollments and unassigned events should be retrieved fro cy.wait('@getTeisStatusAndAssigneeFilter', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'programStatus=ACTIVE'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'assignedUserMode=NONE'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('teis'); }); When('you apply the current filter on the tei working list', () => { - cy.route('GET', '**/tracker/trackedEntities**').as('getTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeis'); cy.get('[data-test="list-view-filter-apply-button"]') .click(); @@ -119,15 +132,15 @@ Then('teis with a first name containing John should be retrieved from the api', cy.wait('@getTeis', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result') - .its('url') + .its('response.url') .should('match', /filter=.*John/); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('teis'); @@ -158,7 +171,7 @@ When('you click the next page button on the tei working list', () => { cy.get('[data-test="search-pagination-next-page"]') .should('exist'); - cy.route('GET', '**/tracker/trackedEntities**').as('getTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeis'); cy.get('[data-test="search-pagination-next-page"]') .click(); @@ -168,15 +181,15 @@ Then('new teis should be retrieved from the api', () => { cy.wait('@getTeis', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result').its('response.body.instances').as('teis'); }); When('you click the previous page button on the tei working list', () => { - cy.route('GET', '**/tracker/trackedEntities**').as('getTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeis'); cy.get('[data-test="search-pagination-previous-page"]') .click(); @@ -186,7 +199,7 @@ When('you change rows per page to 50', () => { cy.get('div[data-test="rows-per-page-selector"]') .should('exist'); - cy.route('GET', '**/tracker/trackedEntities**').as('getTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeis'); cy.get('div[data-test="rows-per-page-selector"]') .click() @@ -198,22 +211,22 @@ Then('a tei batch capped at 50 records should be retrieved from the api', () => cy.wait('@getTeis', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'pageSize=50'); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('teis'); }); When('you click the first page button on the tei working list', () => { - cy.route('GET', '**/tracker/trackedEntities**').as('getTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeis'); cy.get('[data-test="search-pagination-first-page"]') .click(); @@ -224,7 +237,7 @@ When('you click the first name column header', () => { .contains('First name') .should('exist'); - cy.route('GET', '**/tracker/trackedEntities**').as('getTeis'); + cy.intercept('GET', '**/tracker/trackedEntities**').as('getTeis'); cy.get('[data-test="dhis2-uicore-tableheadercellaction"]') .eq(0) @@ -235,15 +248,15 @@ Then('teis should be retrieved from the api ordered ascendingly by first name', cy.wait('@getTeis', { timeout: 40000 }).as('result'); cy.get('@result') - .its('status') - .should('equal', 200); + .its('response.statusCode') + .should('eq', 200); cy.get('@result') - .its('url') + .its('response.url') .should('match', /order=.*asc/); cy.get('@result') - .its('url') + .its('response.url') .should('include', 'page=1'); cy.get('@result').its('response.body.instances').as('teis'); diff --git a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsUser.feature b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsUser.feature similarity index 97% rename from cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsUser.feature rename to cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsUser.feature index d3f07adfe2..b0bdfef1e0 100644 --- a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsUser.feature +++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsUser.feature @@ -186,6 +186,13 @@ And you select the events scheduled today And you apply the current filter Then you see the selected option in the scheduledAt filter +@v>=39 +Scenario: The program stage working list configureation is kept when navigating +Given you open the main page with Ngelehun and WHO RMNCH Tracker context and configure a program stage working list +When you open an enrollment event from the working list +And you go back using the browser button +Then the program stage working list is loaded + @v>=40 Scenario: The user creates, updates and deletes a Program stage custom working list Given you open the main page with Ngelehun and Malaria case diagnosis and Household investigation context diff --git a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsUser/index.js b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsUser/index.js similarity index 92% rename from cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsUser/index.js rename to cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsUser/index.js index 7e5825c8a1..1c1574e25d 100644 --- a/cypress/integration/WorkingLists/TeiWorkingLists/TeiWorkingListsUser/index.js +++ b/cypress/e2e/WorkingLists/TeiWorkingLists/TeiWorkingListsUser/index.js @@ -1,3 +1,4 @@ +import { Given, When, Then, defineStep as And } from '@badeball/cypress-cucumber-preprocessor'; import { v4 as uuid } from 'uuid'; import '../../sharedSteps'; import '../../../sharedSteps'; @@ -6,7 +7,7 @@ const cleanUpIfApplicable = (programId) => { cy.buildApiUrl(`programStageWorkingLists?filter=program.id:eq:${programId}&fields=id,displayName`) .then(url => cy.request(url)) .then(({ body }) => { - const workingList = body.programStageWorkingLists?.find(e => e.displayName === 'Custom Program stage list'); + const workingList = body.programStageWorkingLists && body.programStageWorkingLists.find(e => e.displayName === 'Custom Program stage list'); if (!workingList) { return null; } @@ -167,21 +168,6 @@ When('you set the enrollment date to a relative range', () => { .type('1000'); }); -When('you apply the current filter', () => { - cy.get('[data-test="list-view-filter-apply-button"]') - .click(); -}); - -When('you set the enrollment status filter to active', () => { - cy.get('[data-test="tei-working-lists"]') - .contains('Enrollment status') - .click(); - - cy.get('[data-test="list-view-filter-contents"]') - .contains('Active') - .click(); -}); - When('you set the event status filter to completed', () => { cy.get('[data-test="tei-working-lists"]') .contains('Event status') @@ -192,17 +178,6 @@ When('you set the event status filter to completed', () => { .click(); }); -When(/^you set the first name filter to (.*)$/, (name) => { - cy.get('[data-test="tei-working-lists"]') - .contains('First name') - .click(); - - cy.get('[data-test="list-view-filter-contents"]') - .find('input') - .type(name) - .blur(); -}); - When('you set the WHOMCH Smoking filter to No', () => { cy.get('[data-test="tei-working-lists"]') .within(() => { @@ -227,12 +202,6 @@ When('you set the assginee filter to none', () => { .click(); }); -Then('the enrollment status filter button should show that the active filter is in effect', () => { - cy.get('[data-test="tei-working-lists"]') - .contains('Enrollment status: Active') - .should('exist'); -}); - Then('the assignee filter button should show that unassigned filter is in effect', () => { cy.get('[data-test="tei-working-lists"]') .contains('Assigned to: None') @@ -453,8 +422,8 @@ When('you save the list with the name My custom list', () => { .click(); cy.get('[data-test="view-name-content"]') .type('My custom list'); - cy.server(); - cy.route('POST', '**/trackedEntityInstanceFilters**').as('newTrackedEntityInstanceFilters'); + + cy.intercept('POST', '**/trackedEntityInstanceFilters**').as('newTrackedEntityInstanceFilters'); cy.get('button') .contains('Save') .click(); @@ -468,8 +437,8 @@ When('you save the list with the name Custom Program stage list', () => { .click(); cy.get('[data-test="view-name-content"]') .type('Custom Program stage list'); - cy.server(); - cy.route('POST', '**/programStageWorkingLists**').as('newProgramStageWorkingLists'); + + cy.intercept('POST', '**/programStageWorkingLists**').as('newProgramStageWorkingLists'); cy.get('button') .contains('Save') .click(); @@ -479,8 +448,8 @@ When('you save the list with the name Custom Program stage list', () => { When('you update the list with the name My custom list', () => { cy.get('[data-test="list-view-menu-button"]') .click(); - cy.server(); - cy.route('PUT', '**/trackedEntityInstanceFilters/**').as('editTrackedEntityInstanceFilters'); + + cy.intercept('PUT', '**/trackedEntityInstanceFilters/**').as('editTrackedEntityInstanceFilters'); cy.contains('Update view') .click(); cy.wait('@editTrackedEntityInstanceFilters', { timeout: 30000 }); @@ -489,8 +458,8 @@ When('you update the list with the name My custom list', () => { When('you update the list with the name Custom Program stage list', () => { cy.get('[data-test="list-view-menu-button"]') .click(); - cy.server(); - cy.route('PUT', '**/programStageWorkingLists/**').as('editProgramStageWorkingLists'); + + cy.intercept('PUT', '**/programStageWorkingLists/**').as('editProgramStageWorkingLists'); cy.contains('Update view') .click(); cy.wait('@editProgramStageWorkingLists', { timeout: 30000 }); @@ -508,8 +477,8 @@ When('you delete the name My custom list', () => { .click(); cy.contains('Delete view') .click(); - cy.server(); - cy.route('DELETE', '**/trackedEntityInstanceFilters/**').as('deleteTrackedEntityInstanceFilters'); + + cy.intercept('DELETE', '**/trackedEntityInstanceFilters/**').as('deleteTrackedEntityInstanceFilters'); cy.get('button') .contains('Confirm') .click(); @@ -521,8 +490,8 @@ When('you delete the name Custom Program stage list', () => { .click(); cy.contains('Delete view') .click(); - cy.server(); - cy.route('DELETE', '**/programStageWorkingLists/**').as('deleteProgramStageWorkingLists'); + + cy.intercept('DELETE', '**/programStageWorkingLists/**').as('deleteProgramStageWorkingLists'); cy.get('button') .contains('Confirm') .click(); @@ -748,15 +717,48 @@ Then('the working list configuration was kept', () => { .should('exist'); }); -Then('the working list configuration was kept', () => { +Then('the program stage custom working list filters are loaded', () => { cy.get('[data-test="tei-working-lists"]') - .contains('Event status: Completed') - .should('exist'); + .find('[data-test="more-filters"]') + .should('have.length', 2); }); -Then('the program stage custom working list filters are loaded', () => { +Given('you open the main page with Ngelehun and WHO RMNCH Tracker context and configure a program stage working list', () => { + cy.visit('#/?programId=WSGAb5XwJ3Y&orgUnitId=DiszpKrYNg8'); + cy.get('[data-test="template-selector-create-list"]') + .click(); + + cy.get('[data-test="tei-working-lists"]') + .within(() => { + cy.contains('More filters') + .click(); + cy.contains('Program stage') + .click(); + }); + + cy.get('[data-test="list-view-filter-contents"]') + .contains('Postpartum care visit') + .click(); + + cy.get('[data-test="list-view-filter-apply-button"]') + .click(); +}); + +When('you open an enrollment event from the working list', () => { + cy.contains('Linda') + .click(); +}); + +When('you go back using the browser button', () => { + cy.go('back'); +}); + +Then('the program stage working list is loaded', () => { cy.get('[data-test="tei-working-lists"]') .find('[data-test="more-filters"]') .should('have.length', 2); -}); + cy.get('[data-test="tei-working-lists"]') + .contains('WHOMCH Hemoglobin value') + .should('exist'); +}); diff --git a/cypress/integration/WorkingLists/sharedSteps.js b/cypress/e2e/WorkingLists/sharedSteps.js similarity index 96% rename from cypress/integration/WorkingLists/sharedSteps.js rename to cypress/e2e/WorkingLists/sharedSteps.js index 5c359d2569..c28bdb5ccb 100644 --- a/cypress/integration/WorkingLists/sharedSteps.js +++ b/cypress/e2e/WorkingLists/sharedSteps.js @@ -1,3 +1,5 @@ +import { When, Then } from '@badeball/cypress-cucumber-preprocessor'; + Then('for an event program the page navigation should show that you are on the first page', () => { cy.get('[data-test="event-working-lists"]') .contains('Page 1') @@ -106,14 +108,14 @@ When('you set the assginee filter to none', () => { .click(); }); -When('you set the first name filter to John', () => { +When(/^you set the first name filter to (.*)$/, (name) => { cy.get('[data-test="tei-working-lists"]') .contains('First name') .click(); cy.get('[data-test="list-view-filter-contents"]') .find('input') - .type('John') + .type(name) .blur(); }); diff --git a/cypress/integration/sharedSteps.js b/cypress/e2e/sharedSteps.js similarity index 93% rename from cypress/integration/sharedSteps.js rename to cypress/e2e/sharedSteps.js index 7b8e7a3bc9..16967a0c21 100644 --- a/cypress/integration/sharedSteps.js +++ b/cypress/e2e/sharedSteps.js @@ -1,3 +1,18 @@ +import { + Given, + When, + Then, + defineStep as And, Before, +} from '@badeball/cypress-cucumber-preprocessor'; +import { filterInstanceVersion, shouldClearCookies } from '../support/tagUtils'; + +Before(function callback() { + filterInstanceVersion(() => this.skip()); + if (shouldClearCookies()) { + cy.clearAllCookies(); + } +}); + Given('you are in the main page with no selections made', () => { cy.visit('/#/'); cy.get('[data-test="new-event-button"]') @@ -186,8 +201,7 @@ Then(/^you see the opt out component for (.*)$/, (program) => { }); When(/^you opt out to use the new enrollment Dashboard for (.*)$/, (program) => { - cy.server(); - cy.route('PUT', '**/dataStore/capture/useNewDashboard').as('optOutEnrollmentDashboard'); + cy.intercept('PUT', '**/dataStore/capture/useNewDashboard').as('optOutEnrollmentDashboard'); cy.contains('[data-test="dhis2-uicore-button"]', `Opt out for ${program}`).click(); cy.wait('@optOutEnrollmentDashboard', { timeout: 30000 }); }); diff --git a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/index.js b/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/index.js deleted file mode 100644 index 1640f4243c..0000000000 --- a/cypress/integration/WidgetsForEnrollmentPages/WidgetsForEnrollmentAddEventPage/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import '../sharedSteps'; -import '../WidgetEnrollment'; -import '../WidgetProfile'; -import '../WidgetTab'; diff --git a/cypress/integration/common/index.js b/cypress/integration/common/index.js deleted file mode 100644 index c104ab1095..0000000000 --- a/cypress/integration/common/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import { Before } from 'cypress-cucumber-preprocessor/steps'; -import { filterInstanceVersion, shouldLogin } from '../../support/tagUtils'; - - -let skip; -Before(() => { - filterInstanceVersion(skip); - shouldLogin() && cy.loginThroughForm(); -}); - -beforeEach(function callback() { - /* - Cypress automatically clears all cookies between tests except cross domain cookies. - When we are working with a remote instance, we need to clear the login cookie associated with the remote domain. - Currently, calling clearCookies with domain:null is a workaround to get rid of the cross domain cookies. - Related Cypress issue https://github.com/cypress-io/cypress/issues/408 - */ - cy.clearCookies({ domain: null }); - skip = this.skip.bind(this); -}); diff --git a/cypress/plugins/ignore-x-frame-headers/README.md b/cypress/plugins/ignore-x-frame-headers/README.md deleted file mode 100644 index b3df5337a5..0000000000 --- a/cypress/plugins/ignore-x-frame-headers/README.md +++ /dev/null @@ -1,5 +0,0 @@ -We include this plugin as a fix to the [issue]( https://github.com/cypress-io/cypress/issues/4220). - -When the issue is resolved we can remove: -1. the plugin from the `plugins/index.js` -2. the chromeWebSecurity from the `cypress.json` file \ No newline at end of file diff --git a/cypress/plugins/ignore-x-frame-headers/background.js b/cypress/plugins/ignore-x-frame-headers/background.js deleted file mode 100644 index 236bfe801f..0000000000 --- a/cypress/plugins/ignore-x-frame-headers/background.js +++ /dev/null @@ -1,13 +0,0 @@ -/* eslint-disable */ -const HEADERS_TO_STRIP_LOWERCASE = [ - 'content-security-policy', - 'x-frame-options', -]; - -chrome.webRequest.onHeadersReceived.addListener( - details => ({ - responseHeaders: details.responseHeaders.filter(header => HEADERS_TO_STRIP_LOWERCASE.indexOf(header.name.toLowerCase()) < 0), - }), { - urls: [''], - }, ['blocking', 'responseHeaders']); - diff --git a/cypress/plugins/ignore-x-frame-headers/icon128.png b/cypress/plugins/ignore-x-frame-headers/icon128.png deleted file mode 100644 index c0a240b2f3..0000000000 Binary files a/cypress/plugins/ignore-x-frame-headers/icon128.png and /dev/null differ diff --git a/cypress/plugins/ignore-x-frame-headers/icon16.png b/cypress/plugins/ignore-x-frame-headers/icon16.png deleted file mode 100644 index b6448e9114..0000000000 Binary files a/cypress/plugins/ignore-x-frame-headers/icon16.png and /dev/null differ diff --git a/cypress/plugins/ignore-x-frame-headers/icon48.png b/cypress/plugins/ignore-x-frame-headers/icon48.png deleted file mode 100644 index f0b40c731f..0000000000 Binary files a/cypress/plugins/ignore-x-frame-headers/icon48.png and /dev/null differ diff --git a/cypress/plugins/ignore-x-frame-headers/manifest.json b/cypress/plugins/ignore-x-frame-headers/manifest.json deleted file mode 100644 index ceb41f11da..0000000000 --- a/cypress/plugins/ignore-x-frame-headers/manifest.json +++ /dev/null @@ -1,24 +0,0 @@ -{ -"update_url": "https://clients2.google.com/service/update2/crx", - - "manifest_version": 2, - "name": "Ignore X-Frame headers", - "version": "1.1.1", - - "description": "Drops X-Frame-Options and Content-Security-Policy HTTP response headers, allowing all pages to be iframed.", - "icons": { - "16": "icon16.png", - "48": "icon48.png", - "128": "icon128.png" - }, - - "background": { - "scripts": ["background.js"] - }, - - "permissions": [ - "webRequest", - "webRequestBlocking", - "" - ] -} diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index a79fe99d49..0000000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,25 +0,0 @@ -const { - chromeAllowXSiteCookies, - cucumberPreprocessor, -} = require('@dhis2/cypress-plugins'); - -const getCypressEnvVariables = require('./getCypressEnvVariables'); -const path = require('path'); - -module.exports = (on, config) => { - chromeAllowXSiteCookies(on); - cucumberPreprocessor(on, config); - on('before:browser:launch', (browser, launchOptions) => { - if (browser.family === 'chromium' && browser.name !== 'electron') { - launchOptions.extensions.push(path.join(__dirname, '/ignore-x-frame-headers')); - - launchOptions.args.push('--disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure,SameSiteDefaultChecksMethodRigorously'); - } - - return launchOptions; - }); - - // Add additional plugins here - config.env = getCypressEnvVariables(config); - return config; -}; diff --git a/cypress/support/cucumberPreprocessor.js b/cypress/support/cucumberPreprocessor.js new file mode 100644 index 0000000000..53e2010cbc --- /dev/null +++ b/cypress/support/cucumberPreprocessor.js @@ -0,0 +1,31 @@ +const preprocessor = require('@badeball/cypress-cucumber-preprocessor'); +const webpack = require('@cypress/webpack-preprocessor'); + +module.exports = async function cucumberPreprocessor(on, config) { + // This is required for the preprocessor to be able to generate JSON reports after each run, and more, + await preprocessor.addCucumberPreprocessorPlugin(on, config); + + on( + 'file:preprocessor', + webpack({ + webpackOptions: { + resolve: { + extensions: ['.ts', '.js'], + }, + module: { + rules: [ + { + test: /\.feature$/, + use: [ + { + loader: '@badeball/cypress-cucumber-preprocessor/dist/bundler-utils/webpack', + options: config, + }, + ], + }, + ], + }, + }, + }), + ); +}; diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js new file mode 100644 index 0000000000..031f323471 --- /dev/null +++ b/cypress/support/e2e.js @@ -0,0 +1,4 @@ +import { enableAutoLogin } from '@dhis2/cypress-commands'; +import './commands'; + +enableAutoLogin(); diff --git a/cypress/plugins/getCypressEnvVariables.js b/cypress/support/getCypressEnvVariables/index.js similarity index 100% rename from cypress/plugins/getCypressEnvVariables.js rename to cypress/support/getCypressEnvVariables/index.js diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index 2af960ceb6..0000000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import './commands'; - diff --git a/cypress/support/tagUtils/filterInstanceVersion.js b/cypress/support/tagUtils/filterInstanceVersion.js index 8ec2b8870d..68ae9ea14d 100644 --- a/cypress/support/tagUtils/filterInstanceVersion.js +++ b/cypress/support/tagUtils/filterInstanceVersion.js @@ -1,5 +1,5 @@ export const filterInstanceVersion = (skip) => { - const { tags } = window.testState.currentScenario; + const { tags } = window.testState.pickle; if (!tags || !tags.length) { return; } @@ -26,8 +26,15 @@ export const filterInstanceVersion = (skip) => { .some((versionTag) => { const version = Number(versionTag[2]); const operator = versionTag[1] || '='; - return operation[operator]?.(currentInstanceVersion, version) ?? false; + + if (!operation[operator] || !currentInstanceVersion) { + return false; + } + + return operation[operator](currentInstanceVersion, version); }); - !shouldRun && skip(); + if (!shouldRun) { + skip(); + } }; diff --git a/cypress/support/tagUtils/index.js b/cypress/support/tagUtils/index.js index e2c3a66227..7471fd0a2f 100644 --- a/cypress/support/tagUtils/index.js +++ b/cypress/support/tagUtils/index.js @@ -1,2 +1,2 @@ export { filterInstanceVersion } from './filterInstanceVersion'; -export { shouldLogin } from './shouldLogin'; +export { shouldClearCookies } from './shouldClearCookies'; diff --git a/cypress/support/tagUtils/shouldClearCookies.js b/cypress/support/tagUtils/shouldClearCookies.js new file mode 100644 index 0000000000..a0691ce760 --- /dev/null +++ b/cypress/support/tagUtils/shouldClearCookies.js @@ -0,0 +1,10 @@ +export const shouldClearCookies = () => { + const { tags } = window.testState.pickle; + + if (!tags || !tags.length) { + return false; + } + + return tags + .some(({ name }) => name === '@skip-login'); +}; diff --git a/cypress/support/tagUtils/shouldLogin.js b/cypress/support/tagUtils/shouldLogin.js deleted file mode 100644 index d5307db163..0000000000 --- a/cypress/support/tagUtils/shouldLogin.js +++ /dev/null @@ -1,9 +0,0 @@ -export const shouldLogin = () => { - const { tags } = window.testState.currentScenario; - if (!tags || !tags.length) { - return true; - } - - return tags - .every(({ name }) => name !== '@skip-login'); -}; diff --git a/docs/user/resources/images/enrollment-dash-relationship-widget-add-choose.png b/docs/user/resources/images/enrollment-dash-relationship-widget-add-choose.png new file mode 100644 index 0000000000..59c052056b Binary files /dev/null and b/docs/user/resources/images/enrollment-dash-relationship-widget-add-choose.png differ diff --git a/docs/user/resources/images/enrollment-dash-relationship-widget-add-existing.png b/docs/user/resources/images/enrollment-dash-relationship-widget-add-existing.png new file mode 100644 index 0000000000..467adef42c Binary files /dev/null and b/docs/user/resources/images/enrollment-dash-relationship-widget-add-existing.png differ diff --git a/docs/user/resources/images/enrollment-dash-relationship-widget-add-new.png b/docs/user/resources/images/enrollment-dash-relationship-widget-add-new.png new file mode 100644 index 0000000000..6e64a2f58d Binary files /dev/null and b/docs/user/resources/images/enrollment-dash-relationship-widget-add-new.png differ diff --git a/docs/user/resources/images/enrollment-dash-relationship-widget-add.png b/docs/user/resources/images/enrollment-dash-relationship-widget-add.png new file mode 100644 index 0000000000..78c0e82f83 Binary files /dev/null and b/docs/user/resources/images/enrollment-dash-relationship-widget-add.png differ diff --git a/docs/user/resources/images/enrollment-dash-relationship-widget.png b/docs/user/resources/images/enrollment-dash-relationship-widget.png new file mode 100644 index 0000000000..baf3c48666 Binary files /dev/null and b/docs/user/resources/images/enrollment-dash-relationship-widget.png differ diff --git a/docs/user/using-the-capture-app.md b/docs/user/using-the-capture-app.md index b335b3e7ce..138568e172 100644 --- a/docs/user/using-the-capture-app.md +++ b/docs/user/using-the-capture-app.md @@ -986,6 +986,39 @@ The enrollment comment widget displays comments and allows addition of comments, By clicking in the text field, you will be able to enter new text and see action buttons **Save comment** and **Cancel**. Note that Enrollment comments are attributed to a user and cannot be deleted. +### Relationship widget + +The Relationships widget on the enrollment dashboard is used for viewing the record’s linked relationships to other records. +The number next to the title signifies the total number of relationships + +![](resources/images/enrollment-dash-relationship-widget.png) + +For tracked entity instance relationships, the key attributes shown in the widget are the attributes that have been selected to be displayed on the relationship type page in Maintenance. + +If no attributes are selected, it will just show a row per record with tracked entity type name and relationship creation date. + +When clicking a tracked entity instance you should be taken to the Enrollment Dashboard. If the relationship type includes a program, you should be taken to the latest enrollment for that program. If no program is specified, you should still be sent to the enrollment dashboard, but without a program. + +Click the **Add new** button to add a new relationship. Adding a new relationship opens a dialog where you can select the applicable relationship type. + +![](resources/images/enrollment-dash-relationship-widget-add.png) + +Choose between linking to an existing tracked entity instance or creating a new one. + +![](resources/images/enrollment-dash-relationship-widget-add-choose.png) + +#### Existing tracked entity instance + +Use the search form to find any existing record to link to. + +![](resources/images/enrollment-dash-relationship-widget-add-existing.png) + +#### New tracked entity instance + +Use the form to create a new record and link. + +![](resources/images/enrollment-dash-relationship-widget-add-new.png) + ### Tracked entity instance profile widget On the enrollment dashboard, you can view the tracked entity instance profile widget. Inside the profile widget you can view the key attributes values. @@ -1226,4 +1259,4 @@ The attribute option combo selector will be displayed when you are adding or cha Example from new Tracker event: -![](resources/images/attribute-option-combo-tracker.png) \ No newline at end of file +![](resources/images/attribute-option-combo-tracker.png) diff --git a/i18n/ar.po b/i18n/ar.po index 5960f8ec4d..e1e1024ca7 100644 --- a/i18n/ar.po +++ b/i18n/ar.po @@ -1,16 +1,16 @@ # # Translators: # KRG HIS , 2020 -# Philip Larsen Donnelly, 2022 # Viktor Varland , 2023 # Hamza Assada <7amza.it@gmail.com>, 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Hamza Assada <7amza.it@gmail.com>, 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Arabic (https://app.transifex.com/hisp-uio/teams/100509/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -160,6 +160,15 @@ msgstr "الإحداثية" msgid "Enrollment" msgstr "التسجيل" +msgid "Complete event" +msgstr "إكمال الحدث" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -178,15 +187,18 @@ msgstr "إلغاء" msgid "Metadata error. see log for details" msgstr "خطأ في بيانات التعريف، للتفاصيل انظر السجل" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "المستخدم المعين" msgid "Search for user" msgstr "البحث عن مستخدم" -msgid "Complete event" -msgstr "إكمال الحدث" - msgid "Basic info" msgstr "المعلومات الأساسية" @@ -924,6 +936,18 @@ msgstr "تعذر تحميل الحدث" msgid "Organisation unit could not be loaded" msgstr "تعذر تحميل الوحدة التنظيمية" +msgid "Selected program" +msgstr "البرنامج المحدد" + +msgid "Search {{uniqueAttrName}}" +msgstr "بحث {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "البحث حسب السمة" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "إحتمال وجود تكرار" @@ -988,9 +1012,6 @@ msgstr "البحث عن {{name}}" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "البحث حسب السمة" - msgid "all programs" msgstr "" @@ -1044,12 +1065,6 @@ msgstr "" msgid "Results found" msgstr "العثور على نتائج" -msgid "Selected program" -msgstr "البرنامج المحدد" - -msgid "Search {{uniqueAttrName}}" -msgstr "بحث {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1324,15 +1339,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "اسم مرحلة البرنامج" + msgid "Working list could not be loaded" msgstr "تعذر تحميل قائمة العمل" msgid "Download as JSON" msgstr "تنزيل كملف JSON" -msgid "Download as XML" -msgstr "تنزيل كملف XML" - msgid "Download as CSV" msgstr "تنزيل كملف CSV" diff --git a/i18n/ar_IQ.po b/i18n/ar_IQ.po index 24c60620d7..abb184100a 100644 --- a/i18n/ar_IQ.po +++ b/i18n/ar_IQ.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: KRG HIS , 2022\n" "Language-Team: Arabic (Iraq) (https://app.transifex.com/hisp-uio/teams/100509/ar_IQ/)\n" @@ -157,6 +157,15 @@ msgstr "الإحداثية" msgid "Enrollment" msgstr "التسجيل" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -175,15 +184,18 @@ msgstr "إلغاء" msgid "Metadata error. see log for details" msgstr "خطأ في بيانات التعريف، للتفاصيل انظر السجل" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "المستخدم المعين" msgid "Search for user" msgstr "البحث عن مستخدم" -msgid "Complete event" -msgstr "" - msgid "Basic info" msgstr "المعلومات الأساسية" @@ -921,6 +933,18 @@ msgstr "تعذر تحميل الحدث" msgid "Organisation unit could not be loaded" msgstr "تعذر تحميل الوحدة التنظيمية" +msgid "Selected program" +msgstr "البرنامج المحدد" + +msgid "Search {{uniqueAttrName}}" +msgstr "بحث {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "البحث حسب السمة" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "إحتمال وجود تكرار" @@ -985,9 +1009,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "البحث حسب السمة" - msgid "all programs" msgstr "" @@ -1039,12 +1060,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "البرنامج المحدد" - -msgid "Search {{uniqueAttrName}}" -msgstr "بحث {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1319,15 +1334,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + msgid "Working list could not be loaded" msgstr "تعذر تحميل قائمة العمل" msgid "Download as JSON" msgstr "تنزيل كملف JSON" -msgid "Download as XML" -msgstr "تنزيل كملف XML" - msgid "Download as CSV" msgstr "تنزيل كملف CSV" diff --git a/i18n/ckb.po b/i18n/ckb.po index 8ad5dab62a..0d217c9962 100644 --- a/i18n/ckb.po +++ b/i18n/ckb.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Viktor Varland , 2022\n" "Language-Team: Central Kurdish (https://app.transifex.com/hisp-uio/teams/100509/ckb/)\n" @@ -159,6 +159,15 @@ msgstr "هیڵی ستوونی و ئاسۆیی زەوی" msgid "Enrollment" msgstr "تۆمارکردن" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -177,15 +186,18 @@ msgstr "لابردن" msgid "Metadata error. see log for details" msgstr "هەڵەیەک لە مێتا داتا هەیە، سەیری لۆگ فایل بکە بۆ زانیاری زیاتر" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "بەکارهێنەری دەستنیشانکراو" msgid "Search for user" msgstr "بۆ بەکاربەرەکە بگەرێ" -msgid "Complete event" -msgstr "" - msgid "Basic info" msgstr "زانیاریە سەرەتاییەکان" @@ -931,6 +943,18 @@ msgstr "حاڵەتەکە نەتواندرا بهێندرێت" msgid "Organisation unit could not be loaded" msgstr "ئۆرگانیزەیشن یونتەکە نەتواندرا بهێندرێت" +msgid "Selected program" +msgstr "پرۆگرامە هەڵبژێردراوەکە" + +msgid "Search {{uniqueAttrName}}" +msgstr "بەدوای بگەڕێ {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "بە پێ ئاتریبیوت بگەڕێ" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "ڕەنگە دووبارەبوونەوە دۆزرابێتەوە" @@ -995,9 +1019,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "بە پێ ئاتریبیوت بگەڕێ" - msgid "all programs" msgstr "" @@ -1049,12 +1070,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "پرۆگرامە هەڵبژێردراوەکە" - -msgid "Search {{uniqueAttrName}}" -msgstr "بەدوای بگەڕێ {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1329,15 +1344,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + msgid "Working list could not be loaded" msgstr "لیستی کارکردن نەتواندرا بهێندرێت" msgid "Download as JSON" msgstr "دایبەزێنە وەکو JSON" -msgid "Download as XML" -msgstr "دایبەزێنە وەکو XML" - msgid "Download as CSV" msgstr "دایبەزێنە وەکو CSV" diff --git a/i18n/cs.po b/i18n/cs.po index e85d26ac5f..4e2e113dd9 100644 --- a/i18n/cs.po +++ b/i18n/cs.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Jiří Podhorecký, 2023\n" "Language-Team: Czech (https://app.transifex.com/hisp-uio/teams/100509/cs/)\n" @@ -157,6 +157,15 @@ msgstr "Souřadnice" msgid "Enrollment" msgstr "Zápis" +msgid "Complete event" +msgstr "Dokončit událost" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -175,15 +184,18 @@ msgstr "Zrušit" msgid "Metadata error. see log for details" msgstr "Chyba metadat. viz log pro podrobnosti" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Přiřazený uživatel" msgid "Search for user" msgstr "Hledat uživatele" -msgid "Complete event" -msgstr "Dokončit událost" - msgid "Basic info" msgstr "Základní informace" @@ -934,6 +946,18 @@ msgstr "Událost se nepodařilo načíst" msgid "Organisation unit could not be loaded" msgstr "Organizační jednotku se nepodařilo načíst" +msgid "Selected program" +msgstr "Vybraný program" + +msgid "Search {{uniqueAttrName}}" +msgstr "Hledat {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Hledání podle atributů" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Byly nalezeny možné duplikáty" @@ -998,9 +1022,6 @@ msgstr "Hledat {{name}}" msgid "Search by {{name}}" msgstr "Hledat podle {{name}}" -msgid "Search by attributes" -msgstr "Hledání podle atributů" - msgid "all programs" msgstr "všechny programy" @@ -1058,12 +1079,6 @@ msgstr "" msgid "Results found" msgstr "Nalezeny výsledky" -msgid "Selected program" -msgstr "Vybraný program" - -msgid "Search {{uniqueAttrName}}" -msgstr "Hledat {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "Uložené seznamy v tomto programu" @@ -1350,15 +1365,49 @@ msgstr "{{ scheduledEvents }} naplánováno" msgid "Stages and Events" msgstr "Fáze a události" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Název fáze programu" + msgid "Working list could not be loaded" msgstr "Pracovní seznam nelze načíst" msgid "Download as JSON" msgstr "Stáhnout jako JSON" -msgid "Download as XML" -msgstr "Stáhnout jako XML" - msgid "Download as CSV" msgstr "Stáhnout jako CSV" diff --git a/i18n/en.pot b/i18n/en.pot index 06e8de8d33..5078cc26d8 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: 2023-09-29T14:14:34.330Z\n" -"PO-Revision-Date: 2023-09-29T14:14:34.330Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" +"PO-Revision-Date: 2023-09-12T06:24:49.265Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -530,6 +530,9 @@ msgstr "start typing to search" msgid "suggestions could not be retrieved" msgstr "suggestions could not be retrieved" +msgid "No results found" +msgstr "No results found" + msgid "No items to display" msgstr "No items to display" @@ -947,12 +950,21 @@ msgstr "Event could not be loaded" msgid "Organisation unit could not be loaded" msgstr "Organisation unit could not be loaded" +msgid "Selected program" +msgstr "Selected program" + +msgid "Search {{uniqueAttrName}}" +msgstr "Search {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Search by attributes" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "Could not retrieve metadata. Please try again later." + msgid "Possible duplicates found" msgstr "Possible duplicates found" -msgid "No results found" -msgstr "No results found" - msgid "An error occurred loading possible duplicates" msgstr "An error occurred loading possible duplicates" @@ -1010,9 +1022,6 @@ msgstr "Search {{name}}" msgid "Search by {{name}}" msgstr "Search by {{name}}" -msgid "Search by attributes" -msgstr "Search by attributes" - msgid "all programs" msgstr "all programs" @@ -1070,12 +1079,6 @@ msgstr "Missing search criteria" msgid "Results found" msgstr "Results found" -msgid "Selected program" -msgstr "Selected program" - -msgid "Search {{uniqueAttrName}}" -msgstr "Search {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "Saved lists in this program" @@ -1359,15 +1362,48 @@ msgstr "{{ scheduledEvents }} scheduled" msgid "Stages and Events" msgstr "Stages and Events" +msgid "New TEI Relationship" +msgstr "New TEI Relationship" + +msgid "Missing implementation step" +msgstr "Missing implementation step" + +msgid "Go back without saving relationship" +msgstr "Go back without saving relationship" + +msgid "New Relationship" +msgstr "New Relationship" + +msgid "Link to an existing {{tetName}}" +msgstr "Link to an existing {{tetName}}" + +msgid "An error occurred while adding the relationship" +msgstr "An error occurred while adding the relationship" + +msgid "Something went wrong while loading relationships. Please try again later." +msgstr "Something went wrong while loading relationships. Please try again later." + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "{{trackedEntityTypeName}} relationships" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "To open this relationship, please wait until saving is complete" + +msgid "Type" +msgstr "Type" + +msgid "Created date" +msgstr "Created date" + +msgid "Program stage name" +msgstr "Program stage name" + msgid "Working list could not be loaded" msgstr "Working list could not be loaded" msgid "Download as JSON" msgstr "Download as JSON" -msgid "Download as XML" -msgstr "Download as XML" - msgid "Download as CSV" msgstr "Download as CSV" diff --git a/i18n/es.po b/i18n/es.po index b74921f8ef..a009f61377 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -4,7 +4,6 @@ # ericbp , 2021 # Jaime Bosque , 2021 # Philip Larsen Donnelly, 2022 -# Gabriela Rodriguez , 2022 # Marta Vila , 2022 # Pablo Pajuelo Cabezas , 2022 # Viktor Varland , 2023 @@ -13,13 +12,14 @@ # Prabhjot Singh, 2023 # Christian Atavillos, 2023 # Enzo Nicolas Rossi , 2023 +# Gabriela Rodriguez , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Enzo Nicolas Rossi , 2023\n" +"Last-Translator: Gabriela Rodriguez , 2023\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" @@ -172,6 +172,15 @@ msgstr "Coordenada" msgid "Enrollment" msgstr "Inscripción" +msgid "Complete event" +msgstr "Completar el evento" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "Seleccione {{categoryName}}" @@ -192,15 +201,18 @@ msgstr "Cancelar" msgid "Metadata error. see log for details" msgstr "Error de metadatos. ver registro para más detalles" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Usuario asignado" msgid "Search for user" msgstr "Buscar usuario" -msgid "Complete event" -msgstr "Completar el evento" - msgid "Basic info" msgstr "Información básica" @@ -438,13 +450,13 @@ msgid "Days in the future" msgstr "Días en el futuro" msgid "From" -msgstr "De" +msgstr "Desde" msgid "Days in the past" msgstr "Días en el pasado" msgid "To" -msgstr "Para" +msgstr "Hasta" msgid "This week" msgstr "Esta semana" @@ -791,7 +803,7 @@ msgid "There was an error loading the page" msgstr "Hubo un error al cargar la página" msgid "Choose a registering unit to start reporting" -msgstr "Elija una unidad de registro para comenzar a informar" +msgstr "Seleccione una unidad de registro para comenzar a informar" msgid "There are no feedbacks for this event" msgstr "No hay comentarios para este evento." @@ -866,7 +878,7 @@ msgid "View enrollment" msgstr "Ver inscripción" msgid "Create for" -msgstr "Crear para" +msgstr "Crear" msgid "" "You can also choose a program from the top bar and create in that program" @@ -977,6 +989,18 @@ msgstr "No se pudo cargar el evento" msgid "Organisation unit could not be loaded" msgstr "No se pudo cargar la unidad organizativa" +msgid "Selected program" +msgstr "Programa seleccionado" + +msgid "Search {{uniqueAttrName}}" +msgstr "Buscar {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Buscar por atributos de {{trackedEntityName}}" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Posibles duplicados encontrados" @@ -1044,9 +1068,6 @@ msgstr "Buscar {{name}}" msgid "Search by {{name}}" msgstr "Buscar por {{name}}" -msgid "Search by attributes" -msgstr "Buscar por atributos de {{trackedEntityName}}" - msgid "all programs" msgstr "todos los programas" @@ -1106,12 +1127,6 @@ msgstr "Faltan criterios de búsqueda" msgid "Results found" msgstr "Resultados encontrados" -msgid "Selected program" -msgstr "Programa seleccionado" - -msgid "Search {{uniqueAttrName}}" -msgstr "Buscar {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "Listas guardadas en este programa" @@ -1414,15 +1429,49 @@ msgstr "{{ eventos programados }} programado(s)" msgid "Stages and Events" msgstr "Etapas y eventos" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Tipo" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + msgid "Working list could not be loaded" msgstr "No se pudo cargar la lista de trabajo" msgid "Download as JSON" msgstr "Descargar como JSON" -msgid "Download as XML" -msgstr "Descargar como XML" - msgid "Download as CSV" msgstr "Descargar como CSV" diff --git a/i18n/fr.po b/i18n/fr.po index c0ce1365e1..9e40ff42d3 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -5,17 +5,17 @@ # Karoline Tufte Lien , 2022 # tx_e2f_fr r25 , 2022 # Bram Piot , 2022 -# Gabriela Rodriguez , 2022 -# Viktor Varland , 2023 # Yao Selom SAKA (HISP WCA) , 2023 # Yayra Gomado , 2023 +# Viktor Varland , 2023 +# Gabriela Rodriguez , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Yayra Gomado , 2023\n" +"Last-Translator: Gabriela Rodriguez , 2023\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" @@ -167,6 +167,15 @@ msgstr "Coordonnée" msgid "Enrollment" msgstr "Enrôlement" +msgid "Complete event" +msgstr "Evènement complété" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -189,15 +198,18 @@ msgstr "Annuler" msgid "Metadata error. see log for details" msgstr "Erreur de métadonnées. Voir le journal pour plus de détails" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Utilisateur affecté" msgid "Search for user" msgstr "Rechercher un utilisateur" -msgid "Complete event" -msgstr "Evènement complété" - msgid "Basic info" msgstr "Infos de base" @@ -968,6 +980,18 @@ msgstr "L'événement n'a pas pu être chargé" msgid "Organisation unit could not be loaded" msgstr "L'unité d'organisation n'a pas pu être chargée" +msgid "Selected program" +msgstr "Programme choisi" + +msgid "Search {{uniqueAttrName}}" +msgstr "Chercher {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Chercher par attributs" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Doublons éventuellement détectés" @@ -1034,9 +1058,6 @@ msgstr "Rechercher {{nom}}" msgid "Search by {{name}}" msgstr "Rechercher par {{name}}" -msgid "Search by attributes" -msgstr "Chercher par attributs" - msgid "all programs" msgstr "tous les programmes" @@ -1100,12 +1121,6 @@ msgstr "" msgid "Results found" msgstr "Résultats trouvés" -msgid "Selected program" -msgstr "Programme choisi" - -msgid "Search {{uniqueAttrName}}" -msgstr "Chercher {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "Listes sauvegardées dans ce programme" @@ -1399,15 +1414,49 @@ msgstr "{{ scheduledEvents }} programmé" msgid "Stages and Events" msgstr "Étapes et événements" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Type" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Nom de l'étape du programme" + msgid "Working list could not be loaded" msgstr "La liste des tâches n'a pas pu être chargée" msgid "Download as JSON" msgstr "Télécharger en JSON" -msgid "Download as XML" -msgstr "Download as XML" - msgid "Download as CSV" msgstr "Télécharger en CSV" diff --git a/i18n/id.po b/i18n/id.po index c8da75e732..b22da534fd 100644 --- a/i18n/id.po +++ b/i18n/id.po @@ -3,19 +3,19 @@ # Carwoto Sa'an , 2021 # Yusuf Setiawan , 2021 # Guardian Sanjaya , 2022 -# Viktor Varland , 2022 # Untoro Dwi Raharjo , 2023 # Philip Larsen Donnelly, 2023 -# Aprisa Chrysantina , 2023 # Raja Fathurrahim, 2023 # Farida Sibuea , 2023 +# Viktor Varland , 2023 +# Aprisa Chrysantina , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Farida Sibuea , 2023\n" +"Last-Translator: Aprisa Chrysantina , 2023\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" @@ -166,6 +166,15 @@ msgstr "Koordinat" msgid "Enrollment" msgstr "Pendaftaran" +msgid "Complete event" +msgstr "Even lengkap" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -184,15 +193,18 @@ msgstr "batal" msgid "Metadata error. see log for details" msgstr "Kesalahan metadata. lihat log untuk detailnya" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Pengguna yang ditetapkan" msgid "Search for user" msgstr "Cari pengguna" -msgid "Complete event" -msgstr "Even lengkap" - msgid "Basic info" msgstr "Info dasar" @@ -945,6 +957,18 @@ msgstr "Even tidak dapat dimuat" msgid "Organisation unit could not be loaded" msgstr "Unit organisasi tidak dapat dimuat" +msgid "Selected program" +msgstr "Program yang dipilih" + +msgid "Search {{uniqueAttrName}}" +msgstr "Telusuri {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Cari berdasarkan atribut" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Kemungkinan duplikat ditemukan" @@ -1011,9 +1035,6 @@ msgstr "Telusuri {{nama}}" msgid "Search by {{name}}" msgstr "Cari berdasarkan {{nama}}" -msgid "Search by attributes" -msgstr "Cari berdasarkan atribut" - msgid "all programs" msgstr "" @@ -1069,12 +1090,6 @@ msgstr "" msgid "Results found" msgstr "Hasil ditemukan" -msgid "Selected program" -msgstr "Program yang dipilih" - -msgid "Search {{uniqueAttrName}}" -msgstr "Telusuri {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1351,15 +1366,49 @@ msgstr "" msgid "Stages and Events" msgstr "Tahapan dan Even" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Tipe" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Nama tahapan program" + msgid "Working list could not be loaded" msgstr "Daftar kerja tidak dapat dimuat" msgid "Download as JSON" msgstr "Download dalam bentuk JSON" -msgid "Download as XML" -msgstr "Download dalam bentuk XML" - msgid "Download as CSV" msgstr "Download dalam bentuk CSV" diff --git a/i18n/lo.po b/i18n/lo.po index 96a462426d..0521d1c02a 100644 --- a/i18n/lo.po +++ b/i18n/lo.po @@ -1,16 +1,16 @@ # # Translators: -# Philip Larsen Donnelly, 2022 # Thuy Nguyen , 2022 -# Viktor Varland , 2023 # Saysamone Sibounma, 2023 +# Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Saysamone Sibounma, 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\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" @@ -154,6 +154,15 @@ msgstr "ເສັ້ນສະແດງ" msgid "Enrollment" msgstr "ການລົງທະບຽນ" +msgid "Complete event" +msgstr "Complete event" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -172,15 +181,18 @@ msgstr "ຍົກເລີກ" msgid "Metadata error. see log for details" msgstr "" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "" msgid "Search for user" msgstr "" -msgid "Complete event" -msgstr "Complete event" - msgid "Basic info" msgstr "" @@ -914,6 +926,18 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" +msgid "Selected program" +msgstr "ເລືອກສາຍງານ" + +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "" @@ -978,9 +1002,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1032,12 +1053,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "ເລືອກສາຍງານ" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1312,15 +1327,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Type" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Program stage name" + msgid "Working list could not be loaded" msgstr "" msgid "Download as JSON" msgstr "Download as JSON" -msgid "Download as XML" -msgstr "Download as XML" - msgid "Download as CSV" msgstr "ດາວໂຫຼດເປັນ CSV" diff --git a/i18n/my.po b/i18n/my.po index 370b8a7761..d5cd6a50f3 100644 --- a/i18n/my.po +++ b/i18n/my.po @@ -3,14 +3,14 @@ # Aung Kyi Min , 2019 # Viktor Varland , 2021 # Wanda , 2021 -# Philip Larsen Donnelly, 2022 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Philip Larsen Donnelly, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Burmese (https://app.transifex.com/hisp-uio/teams/100509/my/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -154,6 +154,15 @@ msgstr "Coordinate" msgid "Enrollment" msgstr "" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -172,13 +181,16 @@ msgstr "ပယ်ဖျက်သည်" msgid "Metadata error. see log for details" msgstr "" -msgid "Assigned user" +msgid "{{ stageName }} - Details" msgstr "" -msgid "Search for user" +msgid "{{ stageName }} - {{ sectionName }}" msgstr "" -msgid "Complete event" +msgid "Assigned user" +msgstr "" + +msgid "Search for user" msgstr "" msgid "Basic info" @@ -511,6 +523,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "No results found" + msgid "No items to display" msgstr "" @@ -914,11 +929,20 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" -msgid "Possible duplicates found" +msgid "Selected program" msgstr "" -msgid "No results found" -msgstr "No results found" +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + +msgid "Possible duplicates found" +msgstr "" msgid "An error occurred loading possible duplicates" msgstr "" @@ -978,9 +1002,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1032,12 +1053,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1312,13 +1327,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" msgstr "" -msgid "Download as XML" +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "ပုံစံ" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" diff --git a/i18n/nb.po b/i18n/nb.po index 036767eb33..e562d46e97 100644 --- a/i18n/nb.po +++ b/i18n/nb.po @@ -1,14 +1,14 @@ # # Translators: -# Caroline Hesthagen Holen , 2022 # Karoline Tufte Lien , 2023 +# Caroline Hesthagen Holen , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Karoline Tufte Lien , 2023\n" +"Last-Translator: Caroline Hesthagen Holen , 2023\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" @@ -82,6 +82,7 @@ msgstr "Feil" msgid "" "Plugins are not yet available - Please contact your system administrator" msgstr "" +"Plugins er ikke tilgjengelig ennå - Ta kontakt med systemadministratoren din" msgid "This value is validating" msgstr "Denne verdien validerer" @@ -158,8 +159,17 @@ msgstr "Koordinater" msgid "Enrollment" msgstr "Registrering" +msgid "Complete event" +msgstr "Fullfør hendelse" + +msgid "{{ stageName }} - Basic info" +msgstr "{{ stageName }} - Grunnleggende informasjon" + +msgid "{{ stageName }} - Status" +msgstr "{{ stageName }} - Status" + msgid "Please select {{categoryName}}" -msgstr "" +msgstr "Velg {{categoryName}}" msgid "A future date is not allowed" msgstr "En fremtidig dato er ikke tillatt" @@ -176,15 +186,18 @@ msgstr "Avbryt" msgid "Metadata error. see log for details" msgstr "Metadata feil, se logg for detaljer" +msgid "{{ stageName }} - Details" +msgstr "{{ stageName }} - Detaljer" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "{{ stageName }} - {{ sectionName }}" + msgid "Assigned user" msgstr "Tildelt programbruker" msgid "Search for user" msgstr "Søk etter bruker" -msgid "Complete event" -msgstr "Fullfør hendelse" - msgid "Basic info" msgstr "Grunnleggende informasjon" @@ -281,7 +294,7 @@ msgstr "" "relasjon" msgid "Yes, discard changes" -msgstr "" +msgstr "Ja, forkast endringer" msgid "No, cancel" msgstr "Nei, avbryt" @@ -385,6 +398,9 @@ msgid "" "This event has unsaved changes. Leaving this page without saving will lose " "these changes. Are you sure you want to discard unsaved changes?" msgstr "" +"Denne hendelsen har ulagrede endringer. Hvis du forlater denne siden uten å " +"lagre, mister du disse endringene. Er du sikker på at du vil forkaste " +"ulagrede endringer?" msgid "No events to display" msgstr "Ingen hendelser å vise" @@ -498,7 +514,7 @@ msgid "Type to filter options" msgstr "Skriv for å filtrere alternativer" msgid "No match found" -msgstr "" +msgstr "Ingen treff funnet" msgid "Search" msgstr "Søk" @@ -597,13 +613,13 @@ msgid "Write comment" msgstr "Skriv kommentar" msgid "was blanked out and hidden by your last action" -msgstr "" +msgstr "ble tømt og skjult av den siste handlingen din" msgid "Notice" -msgstr "" +msgstr "Merknad" msgid "Close the notice" -msgstr "" +msgstr "Lukk merknaden" msgid "Use new Enrollment dashboard for {{programName}}" msgstr "Bruk nytt registreringsdashbord for {{programName}}" @@ -619,18 +635,30 @@ msgid "" "functionality in Capture is ongoing and will be added in upcoming app " "releases." msgstr "" +"Ved å klikke på \"meld på\" nedenfor, vil du begynne å bruke det nye " +"registreringsdashbordet i Capture-appen for dette Tracker-programmet. For " +"øyeblikket er det noe funksjonalitet fra Tracker Capture som ennå ikke er " +"lagt til, inkludert relasjons- og henvisningsfunksjonalitet. Arbeidet med å " +"inkludere denne Tracker-funksjonaliteten i Capture pågår og vil bli lagt til" +" i kommende apputgivelser." 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 "" +"Kjerneteamet setter pris på all tilbakemelding på denne nye funksjonaliteten" +" som for øyeblikket blir betatestet, vennligst rapporter eventuelle " +"problemer og tilbakemeldinger i DHIS2 JIRA-prosjektet." 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 "" +"Klikk på knappen nedenfor for å melde deg på den nye funksjonen for " +"registreringsdashbord i Capture-appen (beta) for dette Tracker-programmet " +"for alle brukere." msgid "Yes, opt in" msgstr "Ja, meld på" @@ -653,6 +681,8 @@ msgstr "" msgid "" "An error occurred while fetching enrollments. Please enter a valid url." msgstr "" +"Det oppsto en feil under henting av registreringer. Vennligst skriv inn en " +"gyldig url." msgid "Enrollment Dashboard" msgstr "Registreringsdashbord" @@ -694,19 +724,19 @@ msgstr "" "{{programName}} har kategorier. Velg alle kategorier for å se dashbordet." msgid "Invalid enrollment id {{enrollmentId}}." -msgstr "" +msgstr "Ugyldig registrerings-ID {{enrollmentId}}" msgid "Choose an enrollment to view the dashboard." msgstr "Velg en registrering for å se dashbordet." msgid "There are no active enrollments." -msgstr "" +msgstr "Det er ingen aktive registreringer." msgid "Add new enrollment for {{teiDisplayName}} in this program." -msgstr "" +msgstr "Legg til ny registrering for {{teiDisplayName}} i dette programmet." msgid "No access to program owner." -msgstr "" +msgstr "Ingen tilgang til programeier." msgid "{{teiDisplayName}} is not enrolled in this program." msgstr "{{teiDisplayName}} er ikke registrert i dette programmet." @@ -774,7 +804,7 @@ msgid "Refer" msgstr "Henvis" msgid "You can't add any more {{ programStageName }} events" -msgstr "" +msgstr "Du kan ikke legge til flere {{ programStageName }}-hendelser" msgid "Cancel without saving" msgstr "Avbryt uten å lagre" @@ -839,7 +869,7 @@ msgid "New Enrollment in program{{escape}} {{programName}}" msgstr "Ny registrering i programmet{{escape}} {{programName}}" msgid "Save {{trackedEntityTypeName}}" -msgstr "" +msgstr "Lagre {{trackedEntityTypeName}}" msgid "Save {{trackedEntityName}}" msgstr "Lagre {{trackedEntityName}}" @@ -938,6 +968,18 @@ msgstr "Hendelse kunne ikke lastes inn" msgid "Organisation unit could not be loaded" msgstr "Organisasjonsenhet kunne ikke lastes inn" +msgid "Selected program" +msgstr "Valgt program" + +msgid "Search {{uniqueAttrName}}" +msgstr "Søk {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Søk etter attributter" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Mulige duplikater funnet" @@ -957,13 +999,13 @@ msgid "Add relationship" msgstr "Legg til relasjon" msgid "No results found for " -msgstr "" +msgstr "Ingen resultater funnet for" msgid "Registering unit" msgstr "Registreringsenhet" msgid "Choose a registering unit" -msgstr "" +msgstr "Velg en registreringsenhet" msgid "Clear selection" msgstr "Fjern valg" @@ -972,13 +1014,13 @@ msgid "No programs available." msgstr "Ingen programmer tilgjengelig." msgid "Search for a program" -msgstr "" +msgstr "Søk for et program" msgid "Some programs are being filtered by the chosen registering unit" -msgstr "" +msgstr "Noen programmer blir filtrert av den valgte registreringsenheten" msgid "Show all programs" -msgstr "" +msgstr "Vis alle programmer" msgid "Choose a program" msgstr "Velg et program" @@ -1002,9 +1044,6 @@ msgstr "Søk {{name}}" msgid "Search by {{name}}" msgstr "Søk etter {{name}}" -msgid "Search by attributes" -msgstr "Søk etter attributter" - msgid "all programs" msgstr "alle programmer" @@ -1055,17 +1094,11 @@ msgid "Cannot search in all programs" msgstr "Kan ikke søke i alle programmer" msgid "Missing search criteria" -msgstr "" +msgstr "Manglende søkekriterier" msgid "Results found" msgstr "Resultater funnet" -msgid "Selected program" -msgstr "Valgt program" - -msgid "Search {{uniqueAttrName}}" -msgstr "Søk {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "Lagrede lister i dette programmet" @@ -1096,23 +1129,27 @@ msgid "open the Tracker Capture app" msgstr "åpne Tracker Capture app" msgid "This program is protected" -msgstr "" +msgstr "Dette programmet er beskyttet" msgid "Reason to check for enrollments" -msgstr "" +msgstr "Grunn til å sjekke for registreringer" msgid "" "Describe the reason you are checking for enrollments in this protected " "program" msgstr "" +"Beskriv årsaken til at du ser etter registreringer i dette beskyttede " +"programmet" msgid "Check for enrollments" -msgstr "" +msgstr "Se etter registreringer" msgid "" "You must provide a reason to check for enrollments in this protected " "program. All activity will be logged." msgstr "" +"Du må oppgi en grunn for å se etter registreringer i dette beskyttede " +"programmet. All aktivitet vil bli logget." msgid "Save comment" msgstr "Lagre kommentar" @@ -1159,6 +1196,8 @@ msgstr "Merk for oppfølging" msgid "Existing dates for auto-generated events will not be updated." msgstr "" +"Eksisterende datoer for automatisk genererte hendelser vil ikke bli " +"oppdatert." msgid "Enrollment date" msgstr "Registreringsdato" @@ -1322,7 +1361,7 @@ msgid "New {{ eventName }} event" msgstr "Ny {{ eventName }} hendelse" msgid "To open this event, please wait until saving is complete" -msgstr "" +msgstr "For å åpne denne hendelsen, vennligst vent til lagringen er fullført" msgid "Show {{ rest }} more" msgstr "Vis {{ rest }} flere" @@ -1351,15 +1390,49 @@ msgstr "{{ scheduledEvents }} planlagt" msgid "Stages and Events" msgstr "Faser og hendelser" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Type" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Programfase navn" + msgid "Working list could not be loaded" msgstr "Arbeidslisten kunne ikke lastes inn" msgid "Download as JSON" msgstr "Last ned i JSON format" -msgid "Download as XML" -msgstr "Last ned i XML format" - msgid "Download as CSV" msgstr "Last ned i CSV format" @@ -1388,19 +1461,19 @@ msgid "Choose a program stage to filter by {{label}}" msgstr "Velg en programfase å filtrere etter {{label}}" msgid "Active enrollments" -msgstr "" +msgstr "Aktive registreringer" msgid "Completed enrollments" -msgstr "" +msgstr "Fullførte registreringer" msgid "Cancelled enrollments" -msgstr "" +msgstr "Avbrutte registreringer" msgid "Working list could not be updated" msgstr "Arbeidslisten kunne ikke oppdateres" msgid "an error occurred loading the working lists" -msgstr "" +msgstr "det oppstod en feil ved lasting av arbeidslistene" msgid "an error occurred loading Tracked entity instance lists" msgstr "det oppstod en feil ved lasting av lister over sporede enheter" @@ -1535,7 +1608,7 @@ msgid "Set area" msgstr "Sett område" msgid "Area on map saved" -msgstr "" +msgstr "Området på kartet er lagret" msgid "Compatibility mode" msgstr "Kompabilitetsmodus" diff --git a/i18n/nl.po b/i18n/nl.po index a8a6c3ffe5..4a2d38b67b 100644 --- a/i18n/nl.po +++ b/i18n/nl.po @@ -2,15 +2,15 @@ # Translators: # Cherise Beek , 2021 # Yury Rogachev , 2021 -# Rica Zamora Duchateau, 2023 # Charel van den Elsen, 2023 +# Rica Zamora Duchateau, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Charel van den Elsen, 2023\n" +"Last-Translator: Rica Zamora Duchateau, 2023\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" @@ -161,6 +161,15 @@ msgstr "Coördineren" msgid "Enrollment" msgstr "Inschrijving" +msgid "Complete event" +msgstr "Compleet evenement" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "Selecteer {{categorieNaam}}" @@ -181,15 +190,18 @@ msgstr "Annuleer" msgid "Metadata error. see log for details" msgstr "Metadatafout. zie logboek voor details" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Toegewezen gebruiker" msgid "Search for user" msgstr "Zoek naar gebruiker" -msgid "Complete event" -msgstr "Compleet evenement" - msgid "Basic info" msgstr "Basis informatie" @@ -973,6 +985,18 @@ msgstr "Evenement kan niet worden geladen" msgid "Organisation unit could not be loaded" msgstr "Organisatie-eenheid kan niet worden geladen" +msgid "Selected program" +msgstr "Geselecteerd programma" + +msgid "Search {{uniqueAttrName}}" +msgstr "Zoek {{uniqueAttrNaam}}" + +msgid "Search by attributes" +msgstr "Zoek op attributen" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Mogelijke duplicaten gevonden" @@ -1040,9 +1064,6 @@ msgstr "Zoek {{naam}}" msgid "Search by {{name}}" msgstr "Zoek op {{naam}}" -msgid "Search by attributes" -msgstr "Zoek op attributen" - msgid "all programs" msgstr "alle programma's" @@ -1099,12 +1120,6 @@ msgstr "Ontbrekende zoekcriteria" msgid "Results found" msgstr "Gevonden resultaten" -msgid "Selected program" -msgstr "Geselecteerd programma" - -msgid "Search {{uniqueAttrName}}" -msgstr "Zoek {{uniqueAttrNaam}}" - msgid "Saved lists in this program" msgstr "Opgeslagen lijsten in dit programma" @@ -1403,15 +1418,49 @@ msgstr "{{ geplandeEvenementen}} gepland" msgid "Stages and Events" msgstr "stadiums en evenementen" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Soort" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + msgid "Working list could not be loaded" msgstr "Werklijst kon niet worden geladen" msgid "Download as JSON" msgstr "Downloaden als JSON" -msgid "Download as XML" -msgstr "Downloaden als XML" - msgid "Download as CSV" msgstr "Downloaden als CSV" diff --git a/i18n/prs.po b/i18n/prs.po index 6bf95b4907..4ae583ebe5 100644 --- a/i18n/prs.po +++ b/i18n/prs.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Persian (Afghanistan) (https://app.transifex.com/hisp-uio/teams/100509/fa_AF/)\n" @@ -151,6 +151,15 @@ msgstr "مختصات" msgid "Enrollment" msgstr "ثبت نام" +msgid "Complete event" +msgstr "تکمیلی رویداد" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -169,15 +178,18 @@ msgstr "انصراف" msgid "Metadata error. see log for details" msgstr "" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "" msgid "Search for user" msgstr "" -msgid "Complete event" -msgstr "تکمیلی رویداد" - msgid "Basic info" msgstr "" @@ -911,6 +923,18 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" +msgid "Selected program" +msgstr "برنامه های انتخاب شده" + +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "" @@ -975,9 +999,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1029,12 +1050,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "برنامه های انتخاب شده" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1309,15 +1324,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "نمونه/ډول" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "نام مرحله پروگرام" + msgid "Working list could not be loaded" msgstr "" msgid "Download as JSON" msgstr "داونلود به فارمت JSON" -msgid "Download as XML" -msgstr "داونلود به فارمت XML" - msgid "Download as CSV" msgstr "داونلود به فارمت سی اس وی" diff --git a/i18n/ps.po b/i18n/ps.po index eadda919e9..51a9bdd72a 100644 --- a/i18n/ps.po +++ b/i18n/ps.po @@ -1,14 +1,14 @@ # # Translators: -# phil_dhis2, 2022 # Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-06-27T06:20:33.460Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Viktor Varland , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Pashto (https://app.transifex.com/hisp-uio/teams/100509/ps/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -67,6 +67,16 @@ msgstr "" msgid "Last updated" msgstr "د وروستي ځل لپاره تجدید شوې" +msgid "error encountered during field validation" +msgstr "" + +msgid "error" +msgstr "" + +msgid "" +"Plugins are not yet available - Please contact your system administrator" +msgstr "" + msgid "This value is validating" msgstr "" @@ -142,6 +152,15 @@ msgstr "همغږي کول" msgid "Enrollment" msgstr "شاملول یا نوم لیکنه" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -160,13 +179,16 @@ msgstr "رد یې کړئ [ ژباړه ـ ردول ]" msgid "Metadata error. see log for details" msgstr "" -msgid "Assigned user" +msgid "{{ stageName }} - Details" msgstr "" -msgid "Search for user" +msgid "{{ stageName }} - {{ sectionName }}" msgstr "" -msgid "Complete event" +msgid "Assigned user" +msgstr "" + +msgid "Search for user" msgstr "" msgid "Basic info" @@ -356,9 +378,6 @@ msgstr "" msgid "Operations running" msgstr "" -msgid "Sort" -msgstr "" - msgid "" "This event has unsaved changes. Leaving this page without saving will lose " "these changes. Are you sure you want to discard unsaved changes?" @@ -472,6 +491,12 @@ msgstr "" msgid "Select image" msgstr "" +msgid "Type to filter options" +msgstr "" + +msgid "No match found" +msgstr "" + msgid "Search" msgstr "جستجو" @@ -496,6 +521,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "هېڅ پایله ترلاسه نه شوه" + msgid "No items to display" msgstr "" @@ -737,7 +765,7 @@ msgstr "مهالوېش یې وټاکئ" msgid "Refer" msgstr "" -msgid "You can’t add any more {{ programStageName }} events" +msgid "You can't add any more {{ programStageName }} events" msgstr "" msgid "Cancel without saving" @@ -801,6 +829,9 @@ msgstr "" msgid "New Enrollment in program{{escape}} {{programName}}" msgstr "" +msgid "Save {{trackedEntityTypeName}}" +msgstr "" + msgid "Save {{trackedEntityName}}" msgstr "" @@ -896,11 +927,20 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" -msgid "Possible duplicates found" +msgid "Selected program" +msgstr "انتخاب شوی پروګرام" + +msgid "Search {{uniqueAttrName}}" msgstr "" -msgid "No results found" -msgstr "هېڅ پایله ترلاسه نه شوه" +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + +msgid "Possible duplicates found" +msgstr "" msgid "An error occurred loading possible duplicates" msgstr "" @@ -954,20 +994,12 @@ msgstr "" msgid "Choose a type to start searching" msgstr "" -msgid "Fill in at least {{count}} attribute to search" -msgid_plural "Fill in at least {{count}} attribute to search" -msgstr[0] "" -msgstr[1] "" - msgid "Search {{name}}" msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1019,12 +1051,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "انتخاب شوی پروګرام" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1111,6 +1137,9 @@ msgstr "" msgid "Mark for follow-up" msgstr "د تعقيب لپاره يې په نښه کړئ" +msgid "Existing dates for auto-generated events will not be updated." +msgstr "" + msgid "Enrollment date" msgstr "د نوم ثبتونې نېټه" @@ -1199,24 +1228,12 @@ msgid "" "The scheduled date matches the suggested date, but can be changed if needed." msgstr "" -msgid "The scheduled date is {{count}} days {{position}} the suggested date." -msgid_plural "" -"The scheduled date is {{count}} days {{position}} the suggested date." -msgstr[0] "" -msgstr[1] "" - msgid "after" msgstr "" msgid "before" msgstr "" -msgid "There are {{count}} scheduled event in {{orgUnitName}} on this day." -msgid_plural "" -"There are {{count}} scheduled event in {{orgUnitName}} on this day." -msgstr[0] "" -msgstr[1] "" - msgid "" "Scheduling an event in {{stageName}} for {{programName}} in {{orgUnitName}}" msgstr "" @@ -1260,9 +1277,6 @@ msgstr "" msgid "Try again or contact your system administrator for support" msgstr "" -msgid "tracked entity instance" -msgstr "" - msgid "Fix errors in the form to continue." msgstr "" @@ -1275,6 +1289,9 @@ msgstr "" msgid "Edit" msgstr "درست یې کړئ" +msgid "tracked entity instance" +msgstr "" + msgid "New {{ eventName }} event" msgstr "" @@ -1308,13 +1325,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" msgstr "" -msgid "Download as XML" +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "نمونه/ډول" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "د پروګرام د پړاو یا مرحلې نوم" + +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" @@ -1464,9 +1515,6 @@ msgstr "" msgid "Set coordinate" msgstr "همغږي ترتیب کړئ" -msgid "Page {{currentPage}}" -msgstr "" - msgid "Date" msgstr "نېټه" @@ -1485,10 +1533,7 @@ msgstr "" msgid "To time" msgstr "" -msgid "error encountered during field validation" -msgstr "" - -msgid "error" +msgid "Page {{currentPage}}" msgstr "" msgid "Delete polygon" diff --git a/i18n/pt.po b/i18n/pt.po index eb4e766806..cea52926b0 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -3,17 +3,17 @@ # David Júnior , 2021 # Philip Larsen Donnelly, 2022 # Ge Joao , 2022 -# Gabriela Rodriguez , 2022 -# Viktor Varland , 2023 # Fernando Jorge Bade, 2023 # Sheila André , 2023 +# Viktor Varland , 2023 +# Gabriela Rodriguez , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Sheila André , 2023\n" +"Last-Translator: Gabriela Rodriguez , 2023\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" @@ -163,6 +163,15 @@ msgstr "Coordenada" msgid "Enrollment" msgstr "Inscrição" +msgid "Complete event" +msgstr "Evento completo" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "Por Favor Selecione {{categoryName}}" @@ -182,15 +191,18 @@ msgstr "Cancelar" msgid "Metadata error. see log for details" msgstr "Erro de metadados. veja o log para detalhes" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Utilizador atribuído" msgid "Search for user" msgstr "Pesquisar por utilizador" -msgid "Complete event" -msgstr "Evento completo" - msgid "Basic info" msgstr "Informação básica" @@ -964,6 +976,18 @@ msgstr "O evento não pôde ser carregado" msgid "Organisation unit could not be loaded" msgstr "Não foi possível carregar a unidade organizacional" +msgid "Selected program" +msgstr "Programa selecionado" + +msgid "Search {{uniqueAttrName}}" +msgstr "Pesquise {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Pesquisa por atributos" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Possíveis duplicatas encontradas" @@ -1031,9 +1055,6 @@ msgstr "Pesquise {{name}}" msgid "Search by {{name}}" msgstr "Procura por {{nome}}" -msgid "Search by attributes" -msgstr "Pesquisa por atributos" - msgid "all programs" msgstr "todos os programas" @@ -1092,12 +1113,6 @@ msgstr "Critérios de pesquisa ausentes" msgid "Results found" msgstr "Resultados encontrados" -msgid "Selected program" -msgstr "Programa selecionado" - -msgid "Search {{uniqueAttrName}}" -msgstr "Pesquise {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "Listas gravadas neste programa" @@ -1398,15 +1413,49 @@ msgstr "{{scheduleEvents}} agendado" msgid "Stages and Events" msgstr "Fases e Eventos" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Tipo" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Nome do estágio do programa" + msgid "Working list could not be loaded" msgstr "A lista de trabalho não pôde ser carregada" msgid "Download as JSON" msgstr "Baixar como JSON" -msgid "Download as XML" -msgstr "Baixar como XML" - msgid "Download as CSV" msgstr "Baixar como CSV" diff --git a/i18n/pt_BR.po b/i18n/pt_BR.po index 0ec8fcac15..cca4b80e6f 100644 --- a/i18n/pt_BR.po +++ b/i18n/pt_BR.po @@ -1,15 +1,15 @@ # # Translators: # Oscar Mesones Lapouble , 2021 -# Viktor Varland , 2021 -# Philip Larsen Donnelly, 2022 +# Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Philip Larsen Donnelly, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Portuguese (Brazil) (https://app.transifex.com/hisp-uio/teams/100509/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -153,6 +153,15 @@ msgstr "" msgid "Enrollment" msgstr "" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -171,13 +180,16 @@ msgstr "Cancelar" msgid "Metadata error. see log for details" msgstr "" -msgid "Assigned user" +msgid "{{ stageName }} - Details" msgstr "" -msgid "Search for user" +msgid "{{ stageName }} - {{ sectionName }}" msgstr "" -msgid "Complete event" +msgid "Assigned user" +msgstr "" + +msgid "Search for user" msgstr "" msgid "Basic info" @@ -510,6 +522,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "" + msgid "No items to display" msgstr "" @@ -913,10 +928,19 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" -msgid "Possible duplicates found" +msgid "Selected program" msgstr "" -msgid "No results found" +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + +msgid "Possible duplicates found" msgstr "" msgid "An error occurred loading possible duplicates" @@ -977,9 +1001,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1031,12 +1052,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1311,13 +1326,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" msgstr "" -msgid "Download as XML" +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Tipo" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Nome do estágio do programa" + +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" diff --git a/i18n/ro.po b/i18n/ro.po index c0eda2217d..93e566e328 100644 --- a/i18n/ro.po +++ b/i18n/ro.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Valeriu Plesca , 2022\n" "Language-Team: Romanian (https://app.transifex.com/hisp-uio/teams/100509/ro/)\n" @@ -158,6 +158,15 @@ msgstr "Coordonate" msgid "Enrollment" msgstr "Înrolare" +msgid "Complete event" +msgstr "Eveniment complet" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -177,15 +186,18 @@ msgstr "Anulare" msgid "Metadata error. see log for details" msgstr "" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Utilizatorul atribuit" msgid "Search for user" msgstr "Căutare utilizator" -msgid "Complete event" -msgstr "Eveniment complet" - msgid "Basic info" msgstr "Informații de bază" @@ -928,6 +940,18 @@ msgstr "Evenimentul nu a putut fi încărcat" msgid "Organisation unit could not be loaded" msgstr "Unitatea organizațională nu a putut fi încărcată" +msgid "Selected program" +msgstr "Programul selectat" + +msgid "Search {{uniqueAttrName}}" +msgstr "Căutare {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Căutare după atribute" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "S-au găsit posibile duplicate" @@ -994,9 +1018,6 @@ msgstr "Căutare {{name}}" msgid "Search by {{name}}" msgstr "Căutare după {{name}}" -msgid "Search by attributes" -msgstr "Căutare după atribute" - msgid "all programs" msgstr "" @@ -1050,12 +1071,6 @@ msgstr "" msgid "Results found" msgstr "Rezultate găsite" -msgid "Selected program" -msgstr "Programul selectat" - -msgid "Search {{uniqueAttrName}}" -msgstr "Căutare {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1331,15 +1346,49 @@ msgstr "" msgid "Stages and Events" msgstr "Etape și Evenimente" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + msgid "Working list could not be loaded" msgstr "" msgid "Download as JSON" msgstr "Descărcare ca JSON" -msgid "Download as XML" -msgstr "Descărcare ca XML" - msgid "Download as CSV" msgstr "Descărcare ca CSV" diff --git a/i18n/ru.po b/i18n/ru.po index fd40060b7b..3ba772b823 100644 --- a/i18n/ru.po +++ b/i18n/ru.po @@ -2,16 +2,16 @@ # Translators: # Ulanbek Abakirov , 2020 # Wanda , 2021 -# Philip Larsen Donnelly, 2022 # Valeriu Plesca , 2022 # Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Viktor Varland , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Russian (https://app.transifex.com/hisp-uio/teams/100509/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -162,6 +162,15 @@ msgstr "Координата" msgid "Enrollment" msgstr "Регистрация" +msgid "Complete event" +msgstr "Завершить событие" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -180,15 +189,18 @@ msgstr "Отмена" msgid "Metadata error. see log for details" msgstr "Ошибка в метаданных. Смотрите детали в лог-файле." +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Назначенный пользователь" msgid "Search for user" msgstr "Поиск пользователя" -msgid "Complete event" -msgstr "Завершить событие" - msgid "Basic info" msgstr "Начальные сведения" @@ -937,6 +949,18 @@ msgstr "Событие не может быть загружено" msgid "Organisation unit could not be loaded" msgstr "Не могу загрузить Организационную единицу" +msgid "Selected program" +msgstr "Выбранная программа" + +msgid "Search {{uniqueAttrName}}" +msgstr "Поиск {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Поиск по атрибутам" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Найдены возможные дубликаты" @@ -1001,9 +1025,6 @@ msgstr "Искать {{name}}" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "Поиск по атрибутам" - msgid "all programs" msgstr "" @@ -1055,12 +1076,6 @@ msgstr "" msgid "Results found" msgstr "Найдены результаты" -msgid "Selected program" -msgstr "Выбранная программа" - -msgid "Search {{uniqueAttrName}}" -msgstr "Поиск {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1335,15 +1350,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Тип" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Название стадии программы" + msgid "Working list could not be loaded" msgstr "Не могу загрузить Рабочий список " msgid "Download as JSON" msgstr "Скачать как JSON" -msgid "Download as XML" -msgstr "Скачать как XML" - msgid "Download as CSV" msgstr "Загрузить в формате CSV" diff --git a/i18n/sv.po b/i18n/sv.po index 095c7c794d..12f88faefd 100644 --- a/i18n/sv.po +++ b/i18n/sv.po @@ -1,15 +1,15 @@ # # Translators: -# Philip Larsen Donnelly, 2022 # Viktor Varland , 2023 # Jason Pickering , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Jason Pickering , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Swedish (https://app.transifex.com/hisp-uio/teams/100509/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -153,6 +153,15 @@ msgstr "Coordinate" msgid "Enrollment" msgstr "" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -171,13 +180,16 @@ msgstr "Annullera" msgid "Metadata error. see log for details" msgstr "" -msgid "Assigned user" +msgid "{{ stageName }} - Details" msgstr "" -msgid "Search for user" +msgid "{{ stageName }} - {{ sectionName }}" msgstr "" -msgid "Complete event" +msgid "Assigned user" +msgstr "" + +msgid "Search for user" msgstr "" msgid "Basic info" @@ -510,6 +522,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "Inga resultat hittades" + msgid "No items to display" msgstr "" @@ -913,11 +928,20 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" -msgid "Possible duplicates found" +msgid "Selected program" msgstr "" -msgid "No results found" -msgstr "Inga resultat hittades" +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + +msgid "Possible duplicates found" +msgstr "" msgid "An error occurred loading possible duplicates" msgstr "" @@ -977,9 +1001,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1031,12 +1052,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1311,13 +1326,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" msgstr "" -msgid "Download as XML" +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Typ" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "" + +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" diff --git a/i18n/tet.po b/i18n/tet.po index 316f197e1b..12ce131f18 100644 --- a/i18n/tet.po +++ b/i18n/tet.po @@ -1,14 +1,14 @@ # # Translators: # Viktor Varland , 2019 -# phil_dhis2, 2022 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-05-15T10:06:53.276Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: phil_dhis2, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Tetum (Tetun) (https://app.transifex.com/hisp-uio/teams/100509/tet/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -67,6 +67,16 @@ msgstr "" msgid "Last updated" msgstr "Atualizasaun ikus" +msgid "error encountered during field validation" +msgstr "" + +msgid "error" +msgstr "" + +msgid "" +"Plugins are not yet available - Please contact your system administrator" +msgstr "" + msgid "This value is validating" msgstr "" @@ -142,6 +152,15 @@ msgstr "" msgid "Enrollment" msgstr "" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -160,13 +179,16 @@ msgstr "Kansela" msgid "Metadata error. see log for details" msgstr "" -msgid "Assigned user" +msgid "{{ stageName }} - Details" msgstr "" -msgid "Search for user" +msgid "{{ stageName }} - {{ sectionName }}" msgstr "" -msgid "Complete event" +msgid "Assigned user" +msgstr "" + +msgid "Search for user" msgstr "" msgid "Basic info" @@ -251,7 +273,7 @@ msgstr "" msgid "Go back to event without saving relationship" msgstr "" -msgid "Unsaved changes" +msgid "Discard unsaved changes?" msgstr "" msgid "" @@ -259,10 +281,10 @@ msgid "" "relationship" msgstr "" -msgid "Yes, discard" +msgid "Yes, discard changes" msgstr "" -msgid "No, stay here" +msgid "No, cancel" msgstr "" msgid "New event" @@ -332,21 +354,9 @@ msgstr "" msgid "Do you want to create another event?" msgstr "" -msgid "No, cancel" -msgstr "" - msgid "Yes, create new event" msgstr "" -msgid "Leaving this page will discard the changes you made to this event." -msgstr "" - -msgid "Warnings found" -msgstr "" - -msgid "Abort" -msgstr "" - msgid "Back to form" msgstr "" @@ -368,7 +378,9 @@ msgstr "" msgid "Operations running" msgstr "" -msgid "Sort" +msgid "" +"This event has unsaved changes. Leaving this page without saving will lose " +"these changes. Are you sure you want to discard unsaved changes?" msgstr "" msgid "No events to display" @@ -479,6 +491,12 @@ msgstr "" msgid "Select image" msgstr "" +msgid "Type to filter options" +msgstr "" + +msgid "No match found" +msgstr "" + msgid "Search" msgstr "" @@ -503,6 +521,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "" + msgid "No items to display" msgstr "" @@ -575,12 +596,42 @@ msgstr "" msgid "Write comment" msgstr "" +msgid "was blanked out and hidden by your last action" +msgstr "" + +msgid "Notice" +msgstr "" + +msgid "Close the notice" +msgstr "" + msgid "Use new Enrollment dashboard for {{programName}}" msgstr "" msgid "Opt in for {{programName}}" msgstr "" +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 "" + +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 "" + +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 "" + msgid "Yes, opt in" msgstr "" @@ -714,7 +765,7 @@ msgstr "" msgid "Refer" msgstr "" -msgid "You can’t add any more {{ programStageName }} events" +msgid "You can't add any more {{ programStageName }} events" msgstr "" msgid "Cancel without saving" @@ -778,6 +829,9 @@ msgstr "" msgid "New Enrollment in program{{escape}} {{programName}}" msgstr "" +msgid "Save {{trackedEntityTypeName}}" +msgstr "" + msgid "Save {{trackedEntityName}}" msgstr "" @@ -826,196 +880,189 @@ msgstr "" msgid "Register" msgstr "" -msgid "" -"Fill in at least {{minAttributesRequiredToSearch}} attributes to search" -msgstr "" - -msgid "Search {{name}}" -msgstr "" - -msgid "Search by {{name}}" -msgstr "" +msgid "Back" +msgstr "Ba kotuk" -msgid "Search by attributes" +msgid "events" msgstr "" -msgid "Fill in these fields to search{{escape}} {{ searchableAttributes }}" +msgid "event" msgstr "" -msgid "" -"Fill in at least {{minAttributesRequiredToSearch}} of these fields to " -"search{{escape}} {{searchableAttributes}}" +msgid "You don't have access to edit this event" msgstr "" -msgid "Fill in this field to search{{escape}} {{searchableAttributes}}" +msgid "Edit event" msgstr "" -msgid "No results found" +msgid "Event details" msgstr "" msgid "" -"You can change your search terms and search again to find what you are " -"looking for." +"Leaving this page will discard any selections you made for a new " +"relationship" msgstr "" -msgid "Register a user" +msgid "No one is assigned to this event" msgstr "" -msgid "Back to search" +msgid "Assign" msgstr "" -msgid "An error has occurred" +msgid "Event assigned to {{name}}" msgstr "" -msgid "Too many results" +msgid "Feedbacks" msgstr "" -msgid "Cannot search in all programs" +msgid "Show all events" msgstr "" -msgid "Back" -msgstr "Ba kotuk" - -msgid "Search for {{titleText}}" +msgid "Event could not be loaded. Are you sure it exists?" msgstr "" -msgid "Search for" +msgid "Event could not be loaded" msgstr "" -msgid "" -"You can also choose a program from the top bar and search in that program" +msgid "Organisation unit could not be loaded" msgstr "" -msgid "Choose a type to start searching" +msgid "Selected program" msgstr "" -msgid "all programs" +msgid "Search {{uniqueAttrName}}" msgstr "" -msgid "" -"Not finding the results you were looking for? Try to search all programs " -"that use type " +msgid "Search by attributes" msgstr "" -msgid "Search in all programs" +msgid "Could not retrieve metadata. Please try again later." msgstr "" -msgid "If none of search results match, you can create a new " +msgid "Possible duplicates found" msgstr "" -msgid "Create new" +msgid "An error occurred loading possible duplicates" msgstr "" -msgid "Saved lists in this program" +msgid "You don't have access to delete this relationship" msgstr "" -msgid "Saved lists offer quick access to your most used views in a program." +msgid "You don't have access to create any relationships" msgstr "" -msgid "" -"There are no saved lists in this program yet, create one using the button " -"below." +msgid "Add relationship" msgstr "" -msgid "Create saved list" +msgid "No results found for " msgstr "" -msgid "events" +msgid "Registering unit" msgstr "" -msgid "event" +msgid "Choose a registering unit" msgstr "" -msgid "You don't have access to edit this event" +msgid "Clear selection" msgstr "" -msgid "Edit event" +msgid "No programs available." msgstr "" -msgid "Event details" +msgid "Search for a program" msgstr "" -msgid "" -"Leaving this page will discard any selections you made for a new " -"relationship" +msgid "Some programs are being filtered by the chosen registering unit" msgstr "" -msgid "No one is assigned to this event" +msgid "Show all programs" msgstr "" -msgid "Assign" +msgid "Choose a program" msgstr "" -msgid "Event assigned to {{name}}" +msgid "Search for {{titleText}}" msgstr "" -msgid "Feedbacks" +msgid "Search for" msgstr "" -msgid "Show all events" +msgid "" +"You can also choose a program from the top bar and search in that program" msgstr "" -msgid "Event could not be loaded. Are you sure it exists?" +msgid "Choose a type to start searching" msgstr "" -msgid "Event could not be loaded" +msgid "Search {{name}}" msgstr "" -msgid "Organisation unit could not be loaded" +msgid "Search by {{name}}" msgstr "" -msgid "Possible duplicates found" +msgid "all programs" msgstr "" -msgid "An error occurred loading possible duplicates" +msgid "" +"Not finding the results you were looking for? Try to search all programs " +"that use type " msgstr "" -msgid "You don't have access to delete this relationship" +msgid "Search in all programs" msgstr "" -msgid "You don't have access to create any relationships" +msgid "If none of search results match, you can create a new " msgstr "" -msgid "Add relationship" +msgid "Create new" msgstr "" -msgid "No results found for " +msgid "Fill in these fields to search{{escape}} {{ searchableAttributes }}" msgstr "" -msgid "Registering unit" +msgid "" +"Fill in at least {{minAttributesRequiredToSearch}} of these fields to " +"search{{escape}} {{searchableAttributes}}" msgstr "" -msgid "Choose a registering unit" +msgid "Fill in this field to search{{escape}} {{searchableAttributes}}" msgstr "" -msgid "Clear selection" +msgid "" +"You can change your search terms and search again to find what you are " +"looking for." msgstr "" -msgid "No programs available." +msgid "Back to search" msgstr "" -msgid "Search for a program" +msgid "An error has occurred" msgstr "" -msgid "Some programs are being filtered by the chosen registering unit" +msgid "Too many results" msgstr "" -msgid "Show all programs" +msgid "Cannot search in all programs" msgstr "" -msgid "Choose a program" +msgid "Missing search criteria" msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" +msgid "Saved lists in this program" msgstr "" -msgid "Search {{uniqueAttrName}}" +msgid "Saved lists offer quick access to your most used views in a program." +msgstr "" + +msgid "" +"There are no saved lists in this program yet, create one using the button " +"below." msgstr "" -msgid "Fill in at least {{minAttributesRequired}} attributes to search" +msgid "Create saved list" msgstr "" msgid "New {{trackedEntityName}} in {{programName}}" @@ -1090,6 +1137,9 @@ msgstr "" msgid "Mark for follow-up" msgstr "" +msgid "Existing dates for auto-generated events will not be updated." +msgstr "" + msgid "Enrollment date" msgstr "" @@ -1178,22 +1228,12 @@ msgid "" "The scheduled date matches the suggested date, but can be changed if needed." msgstr "" -msgid "The scheduled date is {{count}} days {{position}} the suggested date." -msgid_plural "" -"The scheduled date is {{count}} days {{position}} the suggested date." -msgstr[0] "" - msgid "after" msgstr "" msgid "before" msgstr "" -msgid "There are {{count}} scheduled event in {{orgUnitName}} on this day." -msgid_plural "" -"There are {{count}} scheduled event in {{orgUnitName}} on this day." -msgstr[0] "" - msgid "" "Scheduling an event in {{stageName}} for {{programName}} in {{orgUnitName}}" msgstr "" @@ -1237,9 +1277,6 @@ msgstr "" msgid "Try again or contact your system administrator for support" msgstr "" -msgid "tracked entity instance" -msgstr "" - msgid "Fix errors in the form to continue." msgstr "" @@ -1252,10 +1289,13 @@ msgstr "" msgid "Edit" msgstr "Edita" +msgid "tracked entity instance" +msgstr "" + msgid "New {{ eventName }} event" msgstr "" -msgid "This event is not yet preserved and cannot be edited" +msgid "To open this event, please wait until saving is complete" msgstr "" msgid "Show {{ rest }} more" @@ -1285,13 +1325,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Tipu" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" msgstr "" -msgid "Download as XML" +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" @@ -1306,6 +1380,9 @@ msgstr "" msgid "an error occurred loading working lists" msgstr "" +msgid "Assigned to" +msgstr "" + msgid "Registration Date" msgstr "" @@ -1315,10 +1392,16 @@ msgstr "" msgid "Enrollment status" msgstr "" -msgid "Assigned to" +msgid "Choose a program stage to filter by {{label}}" msgstr "" -msgid "Choose a program stage to filter by {{label}}" +msgid "Active enrollments" +msgstr "" + +msgid "Completed enrollments" +msgstr "" + +msgid "Cancelled enrollments" msgstr "" msgid "Working list could not be updated" @@ -1432,9 +1515,6 @@ msgstr "" msgid "Set coordinate" msgstr "" -msgid "Page {{currentPage}}" -msgstr "" - msgid "Date" msgstr "Data" @@ -1453,10 +1533,7 @@ msgstr "" msgid "To time" msgstr "" -msgid "error encountered during field validation" -msgstr "" - -msgid "error" +msgid "Page {{currentPage}}" msgstr "" msgid "Delete polygon" @@ -1465,5 +1542,8 @@ msgstr "" msgid "Set area" msgstr "" +msgid "Area on map saved" +msgstr "" + msgid "Compatibility mode" msgstr "" diff --git a/i18n/tg.po b/i18n/tg.po index 6280c6636c..e231a3fb60 100644 --- a/i18n/tg.po +++ b/i18n/tg.po @@ -1,14 +1,14 @@ # # Translators: -# Viktor Varland , 2022 -# Philip Larsen Donnelly, 2022 +# Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Philip Larsen Donnelly, 2022\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Tajik (https://app.transifex.com/hisp-uio/teams/100509/tg/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -152,6 +152,15 @@ msgstr "" msgid "Enrollment" msgstr "" +msgid "Complete event" +msgstr "Рӯйдоди анҷомёфта" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -170,15 +179,18 @@ msgstr "Бекор кардан" msgid "Metadata error. see log for details" msgstr "" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "" msgid "Search for user" msgstr "" -msgid "Complete event" -msgstr "Рӯйдоди анҷомёфта" - msgid "Basic info" msgstr "" @@ -509,6 +521,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "" + msgid "No items to display" msgstr "" @@ -912,10 +927,19 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" -msgid "Possible duplicates found" +msgid "Selected program" msgstr "" -msgid "No results found" +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + +msgid "Possible duplicates found" msgstr "" msgid "An error occurred loading possible duplicates" @@ -976,9 +1000,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1030,12 +1051,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1310,13 +1325,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" msgstr "" -msgid "Download as XML" +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Навъ" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Номи марҳилаи барнома" + +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" diff --git a/i18n/uk.po b/i18n/uk.po index 4d2411c675..0697b82144 100644 --- a/i18n/uk.po +++ b/i18n/uk.po @@ -1,17 +1,17 @@ # # Translators: # Wanda , 2021 -# phil_dhis2, 2022 # Nadiia , 2023 # Viktor Varland , 2023 # Éva Tamási, 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-06-27T06:20:33.460Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Éva Tamási, 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Ukrainian (https://app.transifex.com/hisp-uio/teams/100509/uk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -70,6 +70,16 @@ msgstr "" msgid "Last updated" msgstr "Останнє оновлення" +msgid "error encountered during field validation" +msgstr "" + +msgid "error" +msgstr "" + +msgid "" +"Plugins are not yet available - Please contact your system administrator" +msgstr "" + msgid "This value is validating" msgstr "" @@ -145,6 +155,15 @@ msgstr "Координата" msgid "Enrollment" msgstr "Реєстрація" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -163,15 +182,18 @@ msgstr "Скасувати" msgid "Metadata error. see log for details" msgstr "" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Призначений користувач" msgid "Search for user" msgstr "" -msgid "Complete event" -msgstr "" - msgid "Basic info" msgstr "" @@ -359,9 +381,6 @@ msgstr "" msgid "Operations running" msgstr "" -msgid "Sort" -msgstr "" - msgid "" "This event has unsaved changes. Leaving this page without saving will lose " "these changes. Are you sure you want to discard unsaved changes?" @@ -505,6 +524,9 @@ msgstr "" msgid "suggestions could not be retrieved" msgstr "" +msgid "No results found" +msgstr "Результатів не знайдено" + msgid "No items to display" msgstr "" @@ -746,7 +768,7 @@ msgstr "Запланувати" msgid "Refer" msgstr "" -msgid "You can’t add any more {{ programStageName }} events" +msgid "You can't add any more {{ programStageName }} events" msgstr "" msgid "Cancel without saving" @@ -810,6 +832,9 @@ msgstr "" msgid "New Enrollment in program{{escape}} {{programName}}" msgstr "" +msgid "Save {{trackedEntityTypeName}}" +msgstr "" + msgid "Save {{trackedEntityName}}" msgstr "" @@ -905,11 +930,20 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" -msgid "Possible duplicates found" +msgid "Selected program" +msgstr "Обрані програми" + +msgid "Search {{uniqueAttrName}}" msgstr "" -msgid "No results found" -msgstr "Результатів не знайдено" +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + +msgid "Possible duplicates found" +msgstr "" msgid "An error occurred loading possible duplicates" msgstr "" @@ -963,22 +997,12 @@ msgstr "" msgid "Choose a type to start searching" msgstr "" -msgid "Fill in at least {{count}} attribute to search" -msgid_plural "Fill in at least {{count}} attribute to search" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" - msgid "Search {{name}}" msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1030,12 +1054,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "Обрані програми" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1122,6 +1140,9 @@ msgstr "" msgid "Mark for follow-up" msgstr "Позначте для подальших дій" +msgid "Existing dates for auto-generated events will not be updated." +msgstr "" + msgid "Enrollment date" msgstr "Дата реєстрації" @@ -1210,28 +1231,12 @@ msgid "" "The scheduled date matches the suggested date, but can be changed if needed." msgstr "" -msgid "The scheduled date is {{count}} days {{position}} the suggested date." -msgid_plural "" -"The scheduled date is {{count}} days {{position}} the suggested date." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" - msgid "after" msgstr "" msgid "before" msgstr "" -msgid "There are {{count}} scheduled event in {{orgUnitName}} on this day." -msgid_plural "" -"There are {{count}} scheduled event in {{orgUnitName}} on this day." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" - msgid "" "Scheduling an event in {{stageName}} for {{programName}} in {{orgUnitName}}" msgstr "" @@ -1323,13 +1328,47 @@ msgstr "" msgid "Stages and Events" msgstr "" -msgid "Working list could not be loaded" +msgid "New TEI Relationship" msgstr "" -msgid "Download as JSON" +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" msgstr "" -msgid "Download as XML" +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Тип" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Назва етапу програми" + +msgid "Working list could not be loaded" +msgstr "" + +msgid "Download as JSON" msgstr "" msgid "Download as CSV" @@ -1479,9 +1518,6 @@ msgstr "" msgid "Set coordinate" msgstr "Встановити координату" -msgid "Page {{currentPage}}" -msgstr "" - msgid "Date" msgstr "Дата" @@ -1500,10 +1536,7 @@ msgstr "" msgid "To time" msgstr "" -msgid "error encountered during field validation" -msgstr "" - -msgid "error" +msgid "Page {{currentPage}}" msgstr "" msgid "Delete polygon" diff --git a/i18n/ur.po b/i18n/ur.po index 1165076415..c87c2e613a 100644 --- a/i18n/ur.po +++ b/i18n/ur.po @@ -1,14 +1,14 @@ # # Translators: -# Philip Larsen Donnelly, 2022 # Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Viktor Varland , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Urdu (https://app.transifex.com/hisp-uio/teams/100509/ur/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -152,6 +152,15 @@ msgstr "کورڈینیٹ" msgid "Enrollment" msgstr "اندراج" +msgid "Complete event" +msgstr "مکمل ایونٹ" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -170,15 +179,18 @@ msgstr "منسوخ" msgid "Metadata error. see log for details" msgstr "" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "" msgid "Search for user" msgstr "" -msgid "Complete event" -msgstr "مکمل ایونٹ" - msgid "Basic info" msgstr "" @@ -912,6 +924,18 @@ msgstr "" msgid "Organisation unit could not be loaded" msgstr "" +msgid "Selected program" +msgstr "منتخب کردہ پروگرام" + +msgid "Search {{uniqueAttrName}}" +msgstr "" + +msgid "Search by attributes" +msgstr "" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "" @@ -976,9 +1000,6 @@ msgstr "" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "" - msgid "all programs" msgstr "" @@ -1030,12 +1051,6 @@ msgstr "" msgid "Results found" msgstr "" -msgid "Selected program" -msgstr "منتخب کردہ پروگرام" - -msgid "Search {{uniqueAttrName}}" -msgstr "" - msgid "Saved lists in this program" msgstr "" @@ -1310,15 +1325,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "ٹائپ" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "پروگرام مرحلے کا نام" + msgid "Working list could not be loaded" msgstr "" msgid "Download as JSON" msgstr "JSON کے طور پر ڈاؤن لوڈ کریں" -msgid "Download as XML" -msgstr "XML کے طور پر ڈاؤن لوڈ کریں" - msgid "Download as CSV" msgstr "CSV کے طور پر ڈاؤن لوڈ کریں" diff --git a/i18n/uz_UZ_Cyrl.po b/i18n/uz_UZ_Cyrl.po index 3c0495f761..5e07eebebb 100644 --- a/i18n/uz_UZ_Cyrl.po +++ b/i18n/uz_UZ_Cyrl.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Ibatov , 2023\n" "Language-Team: Uzbek (Cyrillic) (https://app.transifex.com/hisp-uio/teams/100509/uz@Cyrl/)\n" @@ -157,6 +157,15 @@ msgstr "Мувофиқлаштириш" msgid "Enrollment" msgstr "Рўйхатга олиш" +msgid "Complete event" +msgstr "Ҳодиса/тадбирни якунлаш" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -175,15 +184,18 @@ msgstr "Бекор қилиш" msgid "Metadata error. see log for details" msgstr "МетаМаълумотда хатолик. Тафсилотлар учун журналга қаранг." +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Тавсия этилган фойдаланувчи" msgid "Search for user" msgstr "Фойдаланувчини излаш" -msgid "Complete event" -msgstr "Ҳодиса/тадбирни якунлаш" - msgid "Basic info" msgstr "Асосий маълумот" @@ -940,6 +952,18 @@ msgstr "Ҳодиса/тадбирни юклаб бўлмади" msgid "Organisation unit could not be loaded" msgstr "Ташкилий бўлимни юклаб бўлмади" +msgid "Selected program" +msgstr "Танланган дастур" + +msgid "Search {{uniqueAttrName}}" +msgstr "{{uniqueAttrName}} орқали қидириш" + +msgid "Search by attributes" +msgstr "Атрибут орқали қидириш" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Бўлиши мумкин бўлган нусхалар топилди" @@ -1006,9 +1030,6 @@ msgstr "{{name}} орқали қидириш" msgid "Search by {{name}}" msgstr "{{name}} орқали қидириш" -msgid "Search by attributes" -msgstr "Атрибут орқали қидириш" - msgid "all programs" msgstr "" @@ -1064,12 +1085,6 @@ msgstr "" msgid "Results found" msgstr "Натижалар топилди" -msgid "Selected program" -msgstr "Танланган дастур" - -msgid "Search {{uniqueAttrName}}" -msgstr "{{uniqueAttrName}} орқали қидириш" - msgid "Saved lists in this program" msgstr "" @@ -1346,15 +1361,49 @@ msgstr "" msgid "Stages and Events" msgstr "Босқичлар ва Ҳодиса/тадбирлар" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Тури" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Дастур босқичи номи" + msgid "Working list could not be loaded" msgstr "Ишчи рўйхатни юклаб бўлмади" msgid "Download as JSON" msgstr "JSON сифатида юклаш" -msgid "Download as XML" -msgstr "XML сифатида юклаш" - msgid "Download as CSV" msgstr "CSV сифатида юкланг" diff --git a/i18n/uz_UZ_Latn.po b/i18n/uz_UZ_Latn.po index 76fee00b45..944b63b094 100644 --- a/i18n/uz_UZ_Latn.po +++ b/i18n/uz_UZ_Latn.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: Yury Rogachev , 2023\n" "Language-Team: Uzbek (Latin) (https://app.transifex.com/hisp-uio/teams/100509/uz@Latn/)\n" @@ -159,6 +159,15 @@ msgstr "Muvofiqlashtirish" msgid "Enrollment" msgstr "Roʼyxatga olish" +msgid "Complete event" +msgstr "" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -177,15 +186,18 @@ msgstr "Bekor qilish" msgid "Metadata error. see log for details" msgstr "MetaMaʼlumotda xatolik. Tafsilotlar uchun jurnalga qarang." +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Tavsiya etilgan foydalanuvchi" msgid "Search for user" msgstr "Foydalanuvchini izlash" -msgid "Complete event" -msgstr "" - msgid "Basic info" msgstr "Аsosiy maʼlumot" @@ -936,6 +948,18 @@ msgstr "Hodisa/tadbirni yuklab boʼlmadi" msgid "Organisation unit could not be loaded" msgstr "Tashkiliy boʼlimni yuklab boʼlmadi" +msgid "Selected program" +msgstr "Tanlangan dastur" + +msgid "Search {{uniqueAttrName}}" +msgstr "{{uniqueAttrName}} orqali qidirish" + +msgid "Search by attributes" +msgstr "Аtribut orqali qidirish" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Boʼlishi mumkin boʼlgan nusxalar topildi" @@ -1002,9 +1026,6 @@ msgstr "{{name}} orqali qidirish" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "Аtribut orqali qidirish" - msgid "all programs" msgstr "" @@ -1058,12 +1079,6 @@ msgstr "" msgid "Results found" msgstr "Natijalar topildi" -msgid "Selected program" -msgstr "Tanlangan dastur" - -msgid "Search {{uniqueAttrName}}" -msgstr "{{uniqueAttrName}} orqali qidirish" - msgid "Saved lists in this program" msgstr "" @@ -1338,15 +1353,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Turi" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Dastur bosqichi nomi" + msgid "Working list could not be loaded" msgstr "Ishchi roʼyxatni yuklab boʼlmadi" msgid "Download as JSON" msgstr "JSON sifatida yuklash" -msgid "Download as XML" -msgstr "XML sifatida yuklash" - msgid "Download as CSV" msgstr "CSV sifatida yuklang" diff --git a/i18n/vi.po b/i18n/vi.po index 83fb30e45c..a2b56bfc1e 100644 --- a/i18n/vi.po +++ b/i18n/vi.po @@ -1,16 +1,16 @@ # # Translators: -# Philip Larsen Donnelly, 2022 # Mai Nguyen , 2022 # Thuy Nguyen , 2023 # Viktor Varland , 2023 +# Philip Larsen Donnelly, 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: Viktor Varland , 2023\n" +"Last-Translator: Philip Larsen Donnelly, 2023\n" "Language-Team: Vietnamese (https://app.transifex.com/hisp-uio/teams/100509/vi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -160,6 +160,15 @@ msgstr "Tọa độ" msgid "Enrollment" msgstr "Đăng ký" +msgid "Complete event" +msgstr "Hoàn tất chương trình" + +msgid "{{ stageName }} - Basic info" +msgstr "" + +msgid "{{ stageName }} - Status" +msgstr "" + msgid "Please select {{categoryName}}" msgstr "" @@ -178,15 +187,18 @@ msgstr "Hủy" msgid "Metadata error. see log for details" msgstr "Lỗi siêu dữ liệu. xem nhật ký để biết chi tiết" +msgid "{{ stageName }} - Details" +msgstr "" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "" + msgid "Assigned user" msgstr "Người dùng được chỉ định" msgid "Search for user" msgstr "Tìm kiếm người dùng" -msgid "Complete event" -msgstr "Hoàn tất chương trình" - msgid "Basic info" msgstr "Thông tin cơ bản" @@ -930,6 +942,18 @@ msgstr "Không thể tải sự kiện" msgid "Organisation unit could not be loaded" msgstr "Không thể tải đơn vị" +msgid "Selected program" +msgstr "Chương trình đã chọn" + +msgid "Search {{uniqueAttrName}}" +msgstr "Tìm kiếm {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "Tìm kiếm theo thuộc tính" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "" + msgid "Possible duplicates found" msgstr "Có thể tìm thấy các bản sao" @@ -996,9 +1020,6 @@ msgstr "Tim kiêm {{name}}" msgid "Search by {{name}}" msgstr "" -msgid "Search by attributes" -msgstr "Tìm kiếm theo thuộc tính" - msgid "all programs" msgstr "" @@ -1054,12 +1075,6 @@ msgstr "" msgid "Results found" msgstr "Kết quả tìm thấy" -msgid "Selected program" -msgstr "Chương trình đã chọn" - -msgid "Search {{uniqueAttrName}}" -msgstr "Tìm kiếm {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "" @@ -1334,15 +1349,49 @@ msgstr "" msgid "Stages and Events" msgstr "" +msgid "New TEI Relationship" +msgstr "" + +msgid "Missing implementation step" +msgstr "" + +msgid "Go back without saving relationship" +msgstr "" + +msgid "New Relationship" +msgstr "" + +msgid "Link to an existing {{tetName}}" +msgstr "" + +msgid "An error occurred while adding the relationship" +msgstr "" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "" + +msgid "Type" +msgstr "Loại" + +msgid "Created date" +msgstr "" + +msgid "Program stage name" +msgstr "Tên giai đoạn chương trình" + msgid "Working list could not be loaded" msgstr "Không thể tải danh sách làm việc" msgid "Download as JSON" msgstr "Tải về dạng JSON" -msgid "Download as XML" -msgstr "Tải về dạng XML" - msgid "Download as CSV" msgstr "Tải xuống với CSV" diff --git a/i18n/zh.po b/i18n/zh.po index 3776db9b73..78121cdf22 100644 --- a/i18n/zh.po +++ b/i18n/zh.po @@ -1,14 +1,14 @@ # # Translators: -# Viktor Varland , 2021 # Philip Larsen Donnelly, 2022 +# Viktor Varland , 2023 # 晓东 林 <13981924470@126.com>, 2023 # easylin , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" "Last-Translator: easylin , 2023\n" "Language-Team: Chinese (https://app.transifex.com/hisp-uio/teams/100509/zh/)\n" @@ -156,6 +156,15 @@ msgstr "坐标" msgid "Enrollment" msgstr "报名" +msgid "Complete event" +msgstr "完成事件" + +msgid "{{ stageName }} - Basic info" +msgstr "{{ stageName }} - 基本信息" + +msgid "{{ stageName }} - Status" +msgstr "{{ stageName }} - 状态" + msgid "Please select {{categoryName}}" msgstr "请选择{{categoryName}}" @@ -174,15 +183,18 @@ msgstr "取消" msgid "Metadata error. see log for details" msgstr "元数据错误" +msgid "{{ stageName }} - Details" +msgstr "{{ stageName }} - 详细信息" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "{{ 阶段名称 }} - {{ 部分名称 }}" + msgid "Assigned user" msgstr "分配的用户" msgid "Search for user" msgstr "搜索用户 " -msgid "Complete event" -msgstr "完成事件" - msgid "Basic info" msgstr "基本信息" @@ -513,6 +525,9 @@ msgstr "开始输入搜索内容" msgid "suggestions could not be retrieved" msgstr "建议不能提取" +msgid "No results found" +msgstr "没有结果" + msgid "No items to display" msgstr "无条目可显示" @@ -919,12 +934,21 @@ msgstr "事件不能载入" msgid "Organisation unit could not be loaded" msgstr "机构不能载入" +msgid "Selected program" +msgstr "选择的项目" + +msgid "Search {{uniqueAttrName}}" +msgstr "搜索 {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "按属性搜索" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "无法检索元数据。请稍后再试。" + msgid "Possible duplicates found" msgstr "发现可能的重复" -msgid "No results found" -msgstr "没有结果" - msgid "An error occurred loading possible duplicates" msgstr "加载可能的重复项时发生错误" @@ -983,9 +1007,6 @@ msgstr "搜索{{name}}" msgid "Search by {{name}}" msgstr "按{{name}}搜索" -msgid "Search by attributes" -msgstr "按属性搜索" - msgid "all programs" msgstr "所有项目" @@ -1039,12 +1060,6 @@ msgstr "缺少搜索条件" msgid "Results found" msgstr "找到的结果" -msgid "Selected program" -msgstr "选择的项目" - -msgid "Search {{uniqueAttrName}}" -msgstr "搜索 {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "此项目中保存的列表" @@ -1319,15 +1334,49 @@ msgstr "{{ ScheduledEvents }} 已安排" msgid "Stages and Events" msgstr "阶段与活动" +msgid "New TEI Relationship" +msgstr "新加TEI 关系" + +msgid "Missing implementation step" +msgstr "缺少实施步骤" + +msgid "Go back without saving relationship" +msgstr "不保存关系返回" + +msgid "New Relationship" +msgstr "新建关系" + +msgid "Link to an existing {{tetName}}" +msgstr "链接到现有的 {{tetName}}" + +msgid "An error occurred while adding the relationship" +msgstr "添加关系时出错" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "加载关系时出现问题。请稍后再试。" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "{{trackedEntityTypeName}} 关系" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "要打开此关系,请等待保存完成" + +msgid "Type" +msgstr "类型" + +msgid "Created date" +msgstr "创建日期" + +msgid "Program stage name" +msgstr "项目阶段名称" + msgid "Working list could not be loaded" msgstr "工作列表不能载入" msgid "Download as JSON" msgstr "下载为JSON" -msgid "Download as XML" -msgstr "下载为xml" - msgid "Download as CSV" msgstr "获取CSV格式报表" diff --git a/i18n/zh_CN.po b/i18n/zh_CN.po index a489a00929..215b2766d8 100644 --- a/i18n/zh_CN.po +++ b/i18n/zh_CN.po @@ -1,14 +1,14 @@ # # Translators: -# easylin , 2023 # 晓东 林 <13981924470@126.com>, 2023 +# easylin , 2023 # msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2023-09-04T07:07:59.195Z\n" +"POT-Creation-Date: 2023-09-12T06:24:49.265Z\n" "PO-Revision-Date: 2019-06-27 07:31+0000\n" -"Last-Translator: 晓东 林 <13981924470@126.com>, 2023\n" +"Last-Translator: easylin , 2023\n" "Language-Team: Chinese (China) (https://app.transifex.com/hisp-uio/teams/100509/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,7 +77,7 @@ msgstr "错误" msgid "" "Plugins are not yet available - Please contact your system administrator" -msgstr "" +msgstr "插件尚不可用 - 请联系您的系统管理员" msgid "This value is validating" msgstr "值正在被验证" @@ -154,6 +154,15 @@ msgstr "坐标" msgid "Enrollment" msgstr "报名" +msgid "Complete event" +msgstr "完成事件" + +msgid "{{ stageName }} - Basic info" +msgstr "{{ stageName }} - 基本信息" + +msgid "{{ stageName }} - Status" +msgstr "{{ stageName }} - 状态" + msgid "Please select {{categoryName}}" msgstr "请选择{{categoryName}}" @@ -172,15 +181,18 @@ msgstr "取消" msgid "Metadata error. see log for details" msgstr "元数据错误" +msgid "{{ stageName }} - Details" +msgstr "{{ stageName }} - 详细信息" + +msgid "{{ stageName }} - {{ sectionName }}" +msgstr "{{ 阶段名称 }} - {{ 部分名称 }}" + msgid "Assigned user" msgstr "分配的用户" msgid "Search for user" msgstr "搜索用户 " -msgid "Complete event" -msgstr "完成事件" - msgid "Basic info" msgstr "基本信息" @@ -272,7 +284,7 @@ msgid "" msgstr "离开本页将丢掉你的新关系的选择" msgid "Yes, discard changes" -msgstr "" +msgstr "是的,放弃更改" msgid "No, cancel" msgstr "不,取消" @@ -371,7 +383,7 @@ msgstr "运作中" msgid "" "This event has unsaved changes. Leaving this page without saving will lose " "these changes. Are you sure you want to discard unsaved changes?" -msgstr "" +msgstr "此事件有未保存的更改。离开此页面而不保存将丢失这些更改。您确定要放弃未保存的更改吗?" msgid "No events to display" msgstr "无事件可显示" @@ -482,10 +494,10 @@ msgid "Select image" msgstr "选择图像" msgid "Type to filter options" -msgstr "" +msgstr "输入筛选选项" msgid "No match found" -msgstr "" +msgstr "未找到匹配项" msgid "Search" msgstr "搜索" @@ -511,6 +523,9 @@ msgstr "开始输入搜索内容" msgid "suggestions could not be retrieved" msgstr "建议不能提取" +msgid "No results found" +msgstr "没有结果" + msgid "No items to display" msgstr "无条目可显示" @@ -584,13 +599,13 @@ msgid "Write comment" msgstr "写备注" msgid "was blanked out and hidden by your last action" -msgstr "" +msgstr "被你的最后一个动作清空并隐藏" msgid "Notice" -msgstr "" +msgstr "注意" msgid "Close the notice" -msgstr "" +msgstr "关闭通知" msgid "Use new Enrollment dashboard for {{programName}}" msgstr "为 {{programName}} 使用新的注册仪表板" @@ -756,7 +771,7 @@ msgid "Refer" msgstr "转诊" msgid "You can't add any more {{ programStageName }} events" -msgstr "" +msgstr "您无法添加更多的 {{ programStageName }} 事件" msgid "Cancel without saving" msgstr "取消而不保存" @@ -820,7 +835,7 @@ msgid "New Enrollment in program{{escape}} {{programName}}" msgstr "项目{{escape}} {{programName}}的新报名" msgid "Save {{trackedEntityTypeName}}" -msgstr "" +msgstr "保存{{trackedEntityTypeName}}" msgid "Save {{trackedEntityName}}" msgstr "保存{{trackedEntityName}}" @@ -917,12 +932,21 @@ msgstr "事件不能载入" msgid "Organisation unit could not be loaded" msgstr "机构不能载入" +msgid "Selected program" +msgstr "选择的项目" + +msgid "Search {{uniqueAttrName}}" +msgstr "搜索 {{uniqueAttrName}}" + +msgid "Search by attributes" +msgstr "按属性搜索" + +msgid "Could not retrieve metadata. Please try again later." +msgstr "无法检索元数据。请稍后再试。" + msgid "Possible duplicates found" msgstr "发现可能的重复" -msgid "No results found" -msgstr "没有结果" - msgid "An error occurred loading possible duplicates" msgstr "加载可能的重复项时发生错误" @@ -981,9 +1005,6 @@ msgstr "搜索{{name}}" msgid "Search by {{name}}" msgstr "按{{name}}搜索" -msgid "Search by attributes" -msgstr "按属性搜索" - msgid "all programs" msgstr "所有程序" @@ -1037,12 +1058,6 @@ msgstr "缺少搜索条件" msgid "Results found" msgstr "找到的结果" -msgid "Selected program" -msgstr "选择的项目" - -msgid "Search {{uniqueAttrName}}" -msgstr "搜索 {{uniqueAttrName}}" - msgid "Saved lists in this program" msgstr "此程序中的保存列表" @@ -1130,7 +1145,7 @@ msgid "Mark for follow-up" msgstr "后续标记" msgid "Existing dates for auto-generated events will not be updated." -msgstr "" +msgstr "自动生成的事件的现有日期将不会更新。" msgid "Enrollment date" msgstr "报名日期" @@ -1317,15 +1332,49 @@ msgstr "{{scheduledEvents}} 已调度" msgid "Stages and Events" msgstr "阶段与活动" +msgid "New TEI Relationship" +msgstr "新的 TEI 关系" + +msgid "Missing implementation step" +msgstr "缺少实施步骤" + +msgid "Go back without saving relationship" +msgstr "不保存关系返回" + +msgid "New Relationship" +msgstr "新关系" + +msgid "Link to an existing {{tetName}}" +msgstr "链接到现有的 {{tetName}}" + +msgid "An error occurred while adding the relationship" +msgstr "添加关系时出错" + +msgid "" +"Something went wrong while loading relationships. Please try again later." +msgstr "加载关系时出现问题。请稍后再试。" + +msgid "{{trackedEntityTypeName}} relationships" +msgstr "{{trackedEntityTypeName}} 关系" + +msgid "To open this relationship, please wait until saving is complete" +msgstr "要打开此关系,请等待保存完成" + +msgid "Type" +msgstr "类型" + +msgid "Created date" +msgstr "创建日期" + +msgid "Program stage name" +msgstr "项目阶段名称" + msgid "Working list could not be loaded" msgstr "工作列表不能载入" msgid "Download as JSON" msgstr "下载为JSON" -msgid "Download as XML" -msgstr "下载为xml" - msgid "Download as CSV" msgstr "获取CSV格式报表" diff --git a/package.json b/package.json index 64230da5bf..7b8422d585 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "capture-app", "homepage": ".", - "version": "100.41.2", - "cacheVersion": "6", + "version": "100.44.4", + "cacheVersion": "7", "serverVersion": "38", "license": "BSD-3-Clause", "private": true, @@ -10,7 +10,7 @@ "packages/rules-engine" ], "dependencies": { - "@dhis2/rules-engine-javascript": "100.41.2", + "@dhis2/rules-engine-javascript": "100.44.4", "@dhis2/app-runtime": "^3.9.3", "@dhis2/d2-i18n": "^1.1.0", "@dhis2/d2-icons": "^1.0.1", @@ -93,17 +93,19 @@ "@babel/plugin-transform-flow-strip-types": "^7.16.7", "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", + "@badeball/cypress-cucumber-preprocessor": "17.2.1", + "@cypress/webpack-preprocessor": "^6.0.0", "@dhis2/cli-app-scripts": "^9.0.1", "@dhis2/cli-helpers-engine": "^3.2.1", "@dhis2/cli-style": "^10.4.1", "@dhis2/cli-utils-cypress": "^9.0.2", - "@dhis2/cypress-commands": "^8.0.6", - "@dhis2/cypress-plugins": "^9.0.2", + "@dhis2/cypress-commands": "^10.0.3", + "@dhis2/cypress-plugins": "^10.0.3", "@typescript-eslint/eslint-plugin": "^4.33.0", "@vercel/ncc": "^0.34.0", + "babel-plugin-module-resolver": "^5.0.0", "concurrently": "^7.0.0", - "cypress": "^7", - "cypress-cucumber-preprocessor": "^4", + "cypress": "12", "docdash": "^1.2.0", "dotenv": "^16.0.3", "enzyme": "^3.11.0", diff --git a/packages/rules-engine/package.json b/packages/rules-engine/package.json index 88cc2da714..c47a4f11d8 100644 --- a/packages/rules-engine/package.json +++ b/packages/rules-engine/package.json @@ -1,6 +1,6 @@ { "name": "@dhis2/rules-engine-javascript", - "version": "100.41.2", + "version": "100.44.4", "license": "BSD-3-Clause", "main": "./build/cjs/index.js", "scripts": { diff --git a/packages/rules-engine/src/rulesEngine.types.js b/packages/rules-engine/src/rulesEngine.types.js index decb2154a0..3f1d422303 100644 --- a/packages/rules-engine/src/rulesEngine.types.js +++ b/packages/rules-engine/src/rulesEngine.types.js @@ -141,12 +141,12 @@ export type OrgUnitGroup = $ReadOnly<{| code: string, |}>; -export type OrgUnit = $ReadOnly<{| +export type OrgUnit = $ReadOnly<{ id: string, name: string, code: string, groups: Array, -|}>; +}>; export type RulesEngineInput = {| programRulesContainer: ProgramRulesContainer, diff --git a/packages/rules-engine/src/services/VariableService/variableService.types.js b/packages/rules-engine/src/services/VariableService/variableService.types.js index 5529c58a43..0a0b7e65d2 100644 --- a/packages/rules-engine/src/services/VariableService/variableService.types.js +++ b/packages/rules-engine/src/services/VariableService/variableService.types.js @@ -20,7 +20,6 @@ type EventMain = { +programStageId?: string, +programStageName?: string, +orgUnitId?: string, - +orgUnitName?: string, +trackedEntityInstanceId?: string, +enrollmentId?: string, +enrollmentStatus?: string, diff --git a/scripts/startAppForCypress.js b/scripts/startAppForCypress.js index 819aebef8d..3b0607f109 100644 --- a/scripts/startAppForCypress.js +++ b/scripts/startAppForCypress.js @@ -45,6 +45,8 @@ const env = Object acc.REACT_APP_DHIS2_BASE_URL = allEnvVariables[key]; } else if (key.toUpperCase() === 'CYPRESS_DHIS2APIVERSION') { acc.REACT_APP_DHIS2_API_VERSION = allEnvVariables[key]; + } else if (key.toUpperCase() === 'NODE_OPTIONS') { + acc[key] = allEnvVariables[key]; } return acc; }, { BROWSER: 'none' }); diff --git a/src/components/App/AppPages.component.js b/src/components/App/AppPages.component.js index 2203baf361..0af49adb99 100644 --- a/src/components/App/AppPages.component.js +++ b/src/components/App/AppPages.component.js @@ -9,17 +9,21 @@ import { EnrollmentPage } from 'capture-core/components/Pages/Enrollment'; import { StageEventListPage } from 'capture-core/components/Pages/StageEvent'; import { EnrollmentEditEventPage } from 'capture-core/components/Pages/EnrollmentEditEvent'; import { EnrollmentAddEventPage } from 'capture-core/components/Pages/EnrollmentAddEvent'; +import { ReactQueryDevtools } from 'react-query/devtools'; export const AppPages = () => ( - - - - - - - - - - - + <> + + + + + + + + + + + + + ); diff --git a/src/core_modules/capture-core/components/CardList/CardListItem.component.js b/src/core_modules/capture-core/components/CardList/CardListItem.component.js index d2542d3c1b..328926bdb8 100644 --- a/src/core_modules/capture-core/components/CardList/CardListItem.component.js +++ b/src/core_modules/capture-core/components/CardList/CardListItem.component.js @@ -13,6 +13,7 @@ import { searchScopes } from '../SearchBox'; import { enrollmentTypes } from './CardList.constants'; import { ListEntry } from './ListEntry.component'; import { dataElementTypes, getTrackerProgramThrowIfNotFound } from '../../metaData'; +import { useOrgUnitName } from '../../metadataRetrieval/orgUnitName'; import type { ListItem, RenderCustomCardActions } from './CardList.types'; @@ -96,24 +97,24 @@ const deriveEnrollmentType = return enrollmentTypes.DONT_SHOW_TAG; }; -const deriveEnrollmentOrgUnitAndDate = (enrollments, enrollmentType, currentProgramId): {orgUnitName?: string, enrolledAt?: string} => { +const deriveEnrollmentOrgUnitIdAndDate = (enrollments, enrollmentType, currentProgramId): {orgUnitId?: string, enrolledAt?: string} => { if (!enrollments?.length) { return {}; } if (!currentProgramId && enrollments.length) { - const { orgUnitName, enrolledAt } = enrollments[0]; + const { orgUnit: orgUnitId, enrolledAt } = enrollments[0]; return { - orgUnitName, + orgUnitId, enrolledAt, }; } - const { orgUnitName, enrolledAt } = + const { orgUnit: orgUnitId, enrolledAt } = enrollments .filter(({ program }) => program === currentProgramId) .filter(({ status }) => status === enrollmentType) .sort((a, b) => moment.utc(a.lastUpdated).diff(moment.utc(b.lastUpdated)))[0] || {}; - return { orgUnitName, enrolledAt }; + return { orgUnitId, enrolledAt }; }; const deriveProgramFromEnrollment = (enrollments, currentSearchScopeType) => { @@ -137,7 +138,8 @@ const CardListItemIndex = ({ }: Props) => { const enrollments = item.tei ? item.tei.enrollments : []; const enrollmentType = deriveEnrollmentType(enrollments, currentProgramId); - const { orgUnitName, enrolledAt } = deriveEnrollmentOrgUnitAndDate(enrollments, enrollmentType, currentProgramId); + const { orgUnitId, enrolledAt } = deriveEnrollmentOrgUnitIdAndDate(enrollments, enrollmentType, currentProgramId); + const { displayName: orgUnitName } = useOrgUnitName(orgUnitId); const program = enrollments && enrollments.length ? deriveProgramFromEnrollment(enrollments, currentSearchScopeType) : undefined; diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js index 623a16abce..2da9aba5b1 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.container.js @@ -5,29 +5,38 @@ import { useSelector } from 'react-redux'; import { EnrollmentRegistrationEntryComponent } from './EnrollmentRegistrationEntry.component'; import type { OwnProps } from './EnrollmentRegistrationEntry.types'; import { useLifecycle } from './hooks'; -import { useCurrentOrgUnitInfo } from '../../../hooks/useCurrentOrgUnitInfo'; -import { useRulesEngineOrgUnit } from '../../../hooks'; +import { useCoreOrgUnit } from '../../../metadataRetrieval/coreOrgUnit'; import { dataEntryHasChanges } from '../../DataEntry/common/dataEntryHasChanges'; +import { + useBuildEnrollmentPayload, +} from './hooks/useBuildEnrollmentPayload'; export const EnrollmentRegistrationEntry: ComponentType = ({ selectedScopeId, id, saveButtonText, trackedEntityInstanceAttributes, + orgUnitId, + teiId, onSave, ...passOnProps }) => { - const orgUnitId = useCurrentOrgUnitInfo().id; - const { orgUnit, error } = useRulesEngineOrgUnit(orgUnitId); + const { orgUnit, error } = useCoreOrgUnit(orgUnitId); const { - teiId, ready, skipDuplicateCheck, firstStageMetaData, formId, enrollmentMetadata, formFoundation, - } = useLifecycle(selectedScopeId, id, trackedEntityInstanceAttributes, orgUnit); + } = useLifecycle(selectedScopeId, id, trackedEntityInstanceAttributes, orgUnit, teiId, selectedScopeId); + const { buildTeiWithEnrollment } = useBuildEnrollmentPayload({ + programId: selectedScopeId, + dataEntryId: id, + orgUnitId, + teiId, + trackedEntityTypeId: enrollmentMetadata?.trackedEntityType?.id, + }); const isUserInteractionInProgress: boolean = useSelector( state => @@ -41,10 +50,16 @@ export const EnrollmentRegistrationEntry: ComponentType = ({ const isSavingInProgress = useSelector(({ possibleDuplicates, newPage }) => possibleDuplicates.isLoading || possibleDuplicates.isUpdating || !!newPage.uid); + if (error) { return error.errorComponent; } + const onSaveWithEnrollment = () => { + const teiWithEnrollment = buildTeiWithEnrollment(); + onSave(teiWithEnrollment); + }; + return ( = ({ orgUnit={orgUnit} isUserInteractionInProgress={isUserInteractionInProgress} isSavingInProgress={isSavingInProgress} - onSave={() => onSave(formFoundation, firstStageMetaData)} + onSave={onSaveWithEnrollment} /> ); }; diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js index f1924c00b3..d58a42aff2 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types.js @@ -8,8 +8,32 @@ import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMess import type { InputAttribute } from './hooks/useFormValues'; import { RenderFoundation, ProgramStage } from '../../../metaData'; +export type EnrollmentPayload = {| + trackedEntity: string, + trackedEntityType: string, + orgUnit: string, + geometry: any, + enrollments: [ + {| + occurredAt: string, + orgUnit: string, + program: string, + status: string, + enrolledAt: string, + events: Array<{ + orgUnit: string, + }>, + attributes: Array<{ + attribute: string, + value: any, + }>, + |} + ] +|} + export type OwnProps = $ReadOnly<{| id: string, + orgUnitId: string, selectedScopeId: string, fieldOptions?: Object, onSave: SaveForDuplicateCheck, diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js new file mode 100644 index 0000000000..95778162b0 --- /dev/null +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useBuildEnrollmentPayload.js @@ -0,0 +1,188 @@ +// @flow +import { useSelector } from 'react-redux'; +import { getDataEntryKey } from '../../../DataEntry/common/getDataEntryKey'; +import { + getTrackerProgramThrowIfNotFound, + Section, +} from '../../../../metaData'; +import type { RenderFoundation } from '../../../../metaData'; +import { convertClientToServer, convertFormToClient } from '../../../../converters'; +import { + convertDataEntryValuesToClientValues, +} from '../../../DataEntry/common/convertDataEntryValuesToClientValues'; +import { capitalizeFirstLetter } from '../../../../../capture-core-utils/string'; +import { generateUID } from '../../../../utils/uid/generateUID'; +import { + useBuildFirstStageRegistration, +} from './useBuildFirstStageRegistration'; +import { + useMetadataForRegistrationForm, +} from '../../common/TEIAndEnrollment/useMetadataForRegistrationForm'; +import { + useMergeFormFoundationsIfApplicable, +} from './useMergeFormFoundationsIfApplicable'; +import { + deriveAutoGenerateEvents, + deriveFirstStageDuringRegistrationEvent, +} from '../../../Pages/New/RegistrationDataEntry/helpers'; +import { FEATURETYPE } from '../../../../constants'; +import type { EnrollmentPayload } from '../EnrollmentRegistrationEntry.types'; + +type DataEntryReduxConverterProps = { + programId: string; + dataEntryId: string; + itemId?: string; + orgUnitId: string; + teiId: ?string; + trackedEntityTypeId: string; +}; + +function getClientValuesForFormData(formValues: Object, formFoundation: RenderFoundation) { + const clientValues = formFoundation.convertValues(formValues, convertFormToClient); + return clientValues; +} + +function getServerValuesForMainValues( + values: Object, + meta: Object, + formFoundation: RenderFoundation, +) { + const clientValues = convertDataEntryValuesToClientValues( + values, + meta, + formFoundation, + ) || {}; + + // potientally run this through a server to client converter for enrollment, the same way as for event + const serverValues = Object + .keys(clientValues) + .reduce((acc, key) => { + const value = clientValues[key]; + const type = meta[key].type; + acc[key] = convertClientToServer(value, type); + return acc; + }, {}); + + return serverValues; +} + +function getPossibleTetFeatureTypeKey(serverValues: Object) { + return Object + .keys(serverValues) + .find(key => key.startsWith('FEATURETYPE_')); +} + +function buildGeometryProp(key: string, serverValues: Object) { + if (!serverValues[key]) { + return undefined; + } + const type = capitalizeFirstLetter(key.replace('FEATURETYPE_', '').toLocaleLowerCase()); + return { + type, + coordinates: serverValues[key], + }; +} + +const geometryType = formValuesKey => Object.values(FEATURETYPE).find(geometryKey => geometryKey === formValuesKey); + +const deriveAttributesFromFormValues = (formValues = {}) => + Object.keys(formValues) + .filter(key => !geometryType(key)) + .map<{ attribute: string, value: ?any }>(key => ({ attribute: key, value: formValues[key] })); + +export const useBuildEnrollmentPayload = ({ + programId, + dataEntryId, + itemId = 'newEnrollment', + orgUnitId, + teiId, + trackedEntityTypeId, +}: DataEntryReduxConverterProps) => { + const dataEntryKey = getDataEntryKey(dataEntryId, itemId); + const formValues = useSelector(({ formsValues }) => formsValues[dataEntryKey]); + const dataEntryFieldValues = useSelector(({ dataEntriesFieldsValue }) => dataEntriesFieldsValue[dataEntryKey]); + const dataEntryFieldsMeta = useSelector(({ dataEntriesFieldsMeta }) => dataEntriesFieldsMeta[dataEntryKey]); + const { formFoundation: scopeFormFoundation } = useMetadataForRegistrationForm({ selectedScopeId: programId }); + const { firstStageMetaData } = useBuildFirstStageRegistration(programId); + const { formFoundation } = useMergeFormFoundationsIfApplicable(scopeFormFoundation, firstStageMetaData); + + const buildTeiWithEnrollment = (): EnrollmentPayload => { + if (!formFoundation) throw Error('form foundation object not found'); + const firstStage = firstStageMetaData && firstStageMetaData.stage; + const clientValues = getClientValuesForFormData(formValues, formFoundation); + const serverValuesForFormValues = formFoundation.convertAndGroupBySection(clientValues, convertClientToServer); + const serverValuesForMainValues = getServerValuesForMainValues( + dataEntryFieldValues, + dataEntryFieldsMeta, + formFoundation, + ); + const { enrolledAt, occurredAt } = serverValuesForMainValues; + + const { stages } = getTrackerProgramThrowIfNotFound(programId); + + const attributeCategoryOptionsId = 'attributeCategoryOptions'; + const attributeCategoryOptions = Object.keys(serverValuesForMainValues) + .filter(key => key.startsWith(attributeCategoryOptionsId)) + .reduce((acc, key) => { + const categoryId = key.split('-')[1]; + acc[categoryId] = serverValuesForMainValues[key]; + return acc; + }, {}); + + const formServerValues = serverValuesForFormValues[Section.groups.ENROLLMENT]; + const currentEventValues = serverValuesForFormValues[Section.groups.EVENT]; + + + const firstStageDuringRegistrationEvent = deriveFirstStageDuringRegistrationEvent({ + firstStageMetadata: firstStage, + programId, + orgUnitId, + currentEventValues, + fieldsValue: dataEntryFieldValues, + attributeCategoryOptions, + }); + + const autoGenerateEvents = deriveAutoGenerateEvents({ + firstStageMetadata: firstStage, + stages, + enrolledAt, + occurredAt, + programId, + orgUnitId, + attributeCategoryOptions, + }); + + const allEventsToBeCreated = firstStageDuringRegistrationEvent + ? [firstStageDuringRegistrationEvent, ...autoGenerateEvents] + : autoGenerateEvents; + + const enrollment = { + program: programId, + status: 'ACTIVE', + orgUnit: orgUnitId, + occurredAt, + enrolledAt, + attributes: deriveAttributesFromFormValues(formServerValues), + events: allEventsToBeCreated, + }; + + const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(serverValuesForFormValues); + let geometry; + if (tetFeatureTypeKey) { + geometry = buildGeometryProp(tetFeatureTypeKey, serverValuesForFormValues); + delete serverValuesForFormValues[tetFeatureTypeKey]; + } + + return { + trackedEntity: teiId || generateUID(), + orgUnit: orgUnitId, + trackedEntityType: trackedEntityTypeId, + geometry, + enrollments: [enrollment], + }; + }; + + return { + buildTeiWithEnrollment, + }; +}; diff --git a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useLifecycle.js b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useLifecycle.js index 0c4a280bf6..4a84d7ccb7 100644 --- a/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useLifecycle.js +++ b/src/core_modules/capture-core/components/DataEntries/EnrollmentRegistrationEntry/hooks/useLifecycle.js @@ -4,7 +4,6 @@ import { useEffect, useRef } from 'react'; import type { OrgUnit } from '@dhis2/rules-engine-javascript'; import { startNewEnrollmentDataEntryInitialisation } from '../EnrollmentRegistrationEntry.actions'; import { scopeTypes, getProgramThrowIfNotFound } from '../../../../metaData'; -import { useLocationQuery } from '../../../../utils/routing'; import { useScopeInfo } from '../../../../hooks/useScopeInfo'; import { useFormValues } from './index'; import type { InputAttribute } from './useFormValues'; @@ -18,8 +17,9 @@ export const useLifecycle = ( dataEntryId: string, trackedEntityInstanceAttributes?: Array, orgUnit: ?OrgUnit, + teiId: ?string, + programId: string, ) => { - const { teiId, programId } = useLocationQuery(); const dataEntryReadyRef = useRef(false); const dispatch = useDispatch(); const program = programId && getProgramThrowIfNotFound(programId); diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js index 1a81f5f219..44910d1616 100644 --- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js +++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry/Assignee/Assignee.component.js @@ -7,7 +7,7 @@ import { UserField } from '../../../../../FormFields/UserField/UserField.compone const getStyles = () => ({ container: { display: 'flex', - alignItems: 'center', + alignItems: 'baseline', padding: '8px 8px 8px 12px', }, containerVertical: { diff --git a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/NewEventDataEntryWrapper.component.js b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/NewEventDataEntryWrapper.component.js index 97503017e5..14e74ad609 100644 --- a/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/NewEventDataEntryWrapper.component.js +++ b/src/core_modules/capture-core/components/DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/NewEventDataEntryWrapper.component.js @@ -7,7 +7,7 @@ import { DataEntry } from './DataEntry/DataEntry.container'; import { EventsList } from './RecentlyAddedEventsList/RecentlyAddedEventsList.container'; import { useScopeTitleText } from '../../../../hooks/useScopeTitleText'; import { useCurrentProgramInfo } from '../../../../hooks/useCurrentProgramInfo'; -import { useRulesEngineOrgUnit } from '../../../../hooks/useRulesEngineOrgUnit'; +import { useCoreOrgUnit } from '../../../../metadataRetrieval/coreOrgUnit'; import { useLocationQuery } from '../../../../utils/routing'; import { useRulesEngine } from './useRulesEngine'; import type { PlainProps } from './NewEventDataEntryWrapper.types'; @@ -48,7 +48,7 @@ const NewEventDataEntryWrapperPlain = ({ }: PlainProps) => { const { id: programId } = useCurrentProgramInfo(); const orgUnitId = useLocationQuery().orgUnitId; - const { orgUnit, error } = useRulesEngineOrgUnit(orgUnitId); + const { orgUnit, error } = useCoreOrgUnit(orgUnitId); const rulesReady = useRulesEngine({ programId, orgUnit, formFoundation }); const titleText = useScopeTitleText(programId); diff --git a/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.component.js b/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.component.js index 1a8e11bd4c..3e2ea0d8a9 100644 --- a/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.component.js +++ b/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.component.js @@ -8,7 +8,8 @@ import { useHistory } from 'react-router-dom'; import { useScopeInfo } from '../../../hooks/useScopeInfo'; import { scopeTypes } from '../../../metaData'; import { TrackedEntityInstanceDataEntry } from '../TrackedEntityInstance'; -import { useCurrentOrgUnitInfo } from '../../../hooks/useCurrentOrgUnitInfo'; +import { useCurrentOrgUnitId } from '../../../hooks/useCurrentOrgUnitId'; +import { useOrgUnitName } from '../../../metadataRetrieval/orgUnitName'; import type { Props, PlainProps } from './TeiRegistrationEntry.types'; import { DiscardDialog } from '../../Dialogs/DiscardDialog.component'; import { withSaveHandler } from '../../DataEntry'; @@ -54,7 +55,8 @@ const TeiRegistrationEntryPlain = const [showWarning, setShowWarning] = useState(false); const { scopeType } = useScopeInfo(selectedScopeId); const { formId, formFoundation } = useMetadataForRegistrationForm({ selectedScopeId }); - const orgUnit = useCurrentOrgUnitInfo(); + const orgUnitId = useCurrentOrgUnitId(); + const { displayName: orgUnitName } = useOrgUnitName(orgUnitId); const handleOnCancel = () => { if (!isUserInteractionInProgress) { @@ -68,9 +70,9 @@ const TeiRegistrationEntryPlain = const url = scopeType === scopeTypes.TRACKER_PROGRAM ? - buildUrlQueryString({ programId: selectedScopeId, orgUnitId: orgUnit.id }) + buildUrlQueryString({ programId: selectedScopeId, orgUnitId }) : - buildUrlQueryString({ orgUnitId: orgUnit.id }); + buildUrlQueryString({ orgUnitId }); return push(`/?${url}`); }; @@ -79,9 +81,8 @@ const TeiRegistrationEntryPlain = { scopeType === scopeTypes.TRACKED_ENTITY_TYPE && formId && <> - {/* $FlowFixMe */} - {translatedTextWithStylesForTei(trackedEntityName.toLowerCase(), orgUnit.name)} + {translatedTextWithStylesForTei(trackedEntityName.toLowerCase(), orgUnitName)} { +const useInitialiseTeiRegistration = (selectedScopeId, dataEntryId, orgUnitId) => { const dispatch = useDispatch(); const { scopeType, trackedEntityName } = useScopeInfo(selectedScopeId); - const { id: selectedOrgUnitId } = useCurrentOrgUnitInfo(); const { formId, formFoundation } = useMetadataForRegistrationForm({ selectedScopeId }); const formValues = useFormValuesFromSearchTerms(); const registrationFormReady = !!formId; @@ -24,18 +23,18 @@ const useInitialiseTeiRegistration = (selectedScopeId, dataEntryId) => { if (registrationFormReady && scopeType === scopeTypes.TRACKED_ENTITY_TYPE) { dispatch( startNewTeiDataEntryInitialisation( - { selectedOrgUnitId, selectedScopeId, dataEntryId, formFoundation, formValues }, + { selectedOrgUnitId: orgUnitId, selectedScopeId, dataEntryId, formFoundation, formValues }, )); } }, [ scopeType, dataEntryId, selectedScopeId, - selectedOrgUnitId, registrationFormReady, formFoundation, formValues, dispatch, + orgUnitId, ]); return { @@ -44,13 +43,18 @@ const useInitialiseTeiRegistration = (selectedScopeId, dataEntryId) => { }; -export const TeiRegistrationEntry: ComponentType = ({ selectedScopeId, id, ...rest }) => { - const { trackedEntityName } = useInitialiseTeiRegistration(selectedScopeId, id); +export const TeiRegistrationEntry: ComponentType = ({ selectedScopeId, id, orgUnitId, onSave, ...rest }) => { + const { trackedEntityName } = useInitialiseTeiRegistration(selectedScopeId, id, orgUnitId); const ready = useSelector(({ dataEntries }) => (!!dataEntries[id])); const dataEntry = useSelector(({ dataEntries }) => (dataEntries[id])); const { registrationMetaData: teiRegistrationMetadata, } = useMetadataForRegistrationForm({ selectedScopeId }); + const { buildTeiWithoutEnrollment } = useBuildTeiPayload({ + trackedEntityTypeId: selectedScopeId, + dataEntryId: id, + orgUnitId, + }); const dataEntryKey = useMemo(() => { if (dataEntry) { @@ -68,14 +72,21 @@ export const TeiRegistrationEntry: ComponentType = ({ selectedScopeId, return null; } + const onSaveWithoutEnrollment = () => { + const teiPayload = buildTeiWithoutEnrollment(); + onSave(teiPayload); + }; + return ( ); diff --git a/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.types.js b/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.types.js index d73d49c473..43afd9adc8 100644 --- a/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.types.js +++ b/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/TeiRegistrationEntry.types.js @@ -2,23 +2,27 @@ import type { Node } from 'react'; import type { RegistrationFormMetadata } from '../common/TEIAndEnrollment/useMetadataForRegistrationForm/types'; import type { RenderCustomCardActions } from '../../CardList'; -import type { SaveForDuplicateCheck } from '../common/TEIAndEnrollment/DuplicateCheckOnSave'; import type { ExistingUniqueValueDialogActionsComponent } from '../withErrorMessagePostProcessor'; +import type { + TeiPayload, +} from '../../Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; export type OwnProps = $ReadOnly<{| id: string, + orgUnitId: string, selectedScopeId: string, saveButtonText: string, fieldOptions?: Object, - onSave: SaveForDuplicateCheck, + onSave: (TeiPayload) => void, duplicatesReviewPageSize: number, isSavingInProgress?: boolean, renderDuplicatesCardActions?: RenderCustomCardActions, - renderDuplicatesDialogActions?: (onCancel: () => void, onSave: SaveForDuplicateCheck) => Node, + renderDuplicatesDialogActions?: (onCancel: () => void, onSave: (TeiPayload) => void) => Node, ExistingUniqueValueDialogActions: ExistingUniqueValueDialogActionsComponent, |}>; type ContainerProps = {| + orgUnitId: string, teiRegistrationMetadata: RegistrationFormMetadata, ready: boolean, trackedEntityName: string, @@ -37,9 +41,9 @@ type PropsAddedInHOC = {| |}; type PropsRemovedInHOC = {| renderDuplicatesCardActions?: RenderCustomCardActions, - renderDuplicatesDialogActions?: (onCancel: () => void, onSave: SaveForDuplicateCheck) => Node, + renderDuplicatesDialogActions?: (onCancel: () => void, onSave: (TeiPayload) => void) => Node, duplicatesReviewPageSize: number, - onSave: SaveForDuplicateCheck, + onSave: (TeiPayload) => void, |}; export type PlainProps = {| diff --git a/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/hooks/useBuildTeiPayload.js b/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/hooks/useBuildTeiPayload.js new file mode 100644 index 0000000000..0c4cd6da0d --- /dev/null +++ b/src/core_modules/capture-core/components/DataEntries/TeiRegistrationEntry/hooks/useBuildTeiPayload.js @@ -0,0 +1,81 @@ +// @flow +import { useSelector } from 'react-redux'; +import { useMetadataForRegistrationForm } from '../../common/TEIAndEnrollment/useMetadataForRegistrationForm'; +import type { RenderFoundation } from '../../../../metaData'; +import { convertClientToServer, convertFormToClient } from '../../../../converters'; +import { capitalizeFirstLetter } from '../../../../../capture-core-utils/string'; +import { generateUID } from '../../../../utils/uid/generateUID'; +import { getDataEntryKey } from '../../../DataEntry/common/getDataEntryKey'; +import type { + TeiPayload, +} from '../../../Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; + +type Props = { + trackedEntityTypeId: string, + dataEntryId: string, + orgUnitId: string, + itemId?: string, +}; + +function getClientValuesForFormData(formValues: Object, formFoundation: RenderFoundation) { + return formFoundation.convertValues(formValues, convertFormToClient); +} + +function getPossibleTetFeatureTypeKey(serverValues: Object) { + return Object + .keys(serverValues) + .find(key => key.startsWith('FEATURETYPE_')); +} + +function buildGeometryProp(key: string, serverValues: Object) { + if (!serverValues[key]) { + return undefined; + } + const type = capitalizeFirstLetter(key.replace('FEATURETYPE_', '').toLocaleLowerCase()); + return { + type, + coordinates: serverValues[key], + }; +} + +export const useBuildTeiPayload = ({ + trackedEntityTypeId, + dataEntryId, + itemId = 'newTei', + orgUnitId, +}: Props) => { + const dataEntryKey = getDataEntryKey(dataEntryId, itemId); + const { formFoundation } = useMetadataForRegistrationForm({ selectedScopeId: trackedEntityTypeId }); + const formValues = useSelector(({ formsValues }) => formsValues[dataEntryKey]); + + const buildTeiWithoutEnrollment = (): TeiPayload => { + if (!formFoundation) throw Error('form foundation object not found'); + const clientValues = getClientValuesForFormData(formValues, formFoundation); + const serverValuesForFormValues = formFoundation.convertValues(clientValues, convertClientToServer); + + // $FlowFixMe + const attributes = Object.keys(serverValuesForFormValues) + .map(key => ({ + attribute: key, + value: serverValuesForFormValues[key], + })); + + const tetFeatureTypeKey = getPossibleTetFeatureTypeKey(serverValuesForFormValues); + let geometry; + if (tetFeatureTypeKey) { + geometry = buildGeometryProp(tetFeatureTypeKey, serverValuesForFormValues); + delete serverValuesForFormValues[tetFeatureTypeKey]; + } + + return { + attributes, + trackedEntity: generateUID(), + orgUnit: orgUnitId, + trackedEntityType: trackedEntityTypeId, + geometry, + enrollments: [], + }; + }; + + return { buildTeiWithoutEnrollment }; +}; diff --git a/src/core_modules/capture-core/components/DataEntries/TrackedEntityInstance/TrackedEntityInstanceDataEntry.component.js b/src/core_modules/capture-core/components/DataEntries/TrackedEntityInstance/TrackedEntityInstanceDataEntry.component.js index 21a4b64e15..fad0f62845 100644 --- a/src/core_modules/capture-core/components/DataEntries/TrackedEntityInstance/TrackedEntityInstanceDataEntry.component.js +++ b/src/core_modules/capture-core/components/DataEntries/TrackedEntityInstance/TrackedEntityInstanceDataEntry.component.js @@ -42,7 +42,7 @@ class PreTeiDataEntryPure extends React.PureComponent { } type PreTeiDataEntryProps = { - orgUnit: Object, + orgUnitId: string, trackedEntityTypeId: string, onUpdateField: Function, onStartAsyncUpdateField: Function, @@ -52,17 +52,17 @@ type PreTeiDataEntryProps = { export class PreTeiDataEntry extends React.Component { getValidationContext = () => { - const { orgUnit, onGetUnsavedAttributeValues, trackedEntityTypeId } = this.props; + const { orgUnitId, onGetUnsavedAttributeValues, trackedEntityTypeId } = this.props; return { trackedEntityTypeId, - orgUnitId: orgUnit.id, + orgUnitId, onGetUnsavedAttributeValues, }; } render() { const { - orgUnit, + orgUnitId, trackedEntityTypeId, onUpdateField, onStartAsyncUpdateField, diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js index a727db9ec2..f55dc39c0e 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/types/duplicateCheckOnSave.types.js @@ -1,7 +1,7 @@ // @flow -import { ProgramStage, RenderFoundation } from '../../../../../../metaData'; +import type { EnrollmentPayload } from '../../../../EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; +import type { TeiPayload } from '../../../../../Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; export type SaveForDuplicateCheck = ( - formFoundation?: RenderFoundation, - firstStageMetaData?: { stage: ProgramStage }, + teiWithEnrollment: EnrollmentPayload | TeiPayload, ) => void; diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/useDuplicateCheckerOnSave.types.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/useDuplicateCheckerOnSave.types.js index 98525d23d5..d74476bfee 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/useDuplicateCheckerOnSave.types.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/useDuplicateCheckerOnSave.types.js @@ -1,9 +1,8 @@ // @flow import { type InputSearchGroup } from '../../../../../metaData'; -import type { SaveForDuplicateCheck } from './types'; export type Input = {| - onSave: SaveForDuplicateCheck, + onSave: () => void, hasDuplicate: ?boolean, onResetPossibleDuplicates: () => void, onReviewDuplicates: (duplicatesReviewPageSize: number) => void, diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/withDuplicateCheckOnSave.types.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/withDuplicateCheckOnSave.types.js index 87a3d4cffc..560dd0e9a2 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/withDuplicateCheckOnSave.types.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/DuplicateCheckOnSave/withDuplicateCheckOnSave.types.js @@ -2,16 +2,15 @@ import type { Node } from 'react'; import type { Enrollment, TeiRegistration } from '../../../../../metaData'; import type { RenderCustomCardActions } from '../../../../CardList'; -import type { SaveForDuplicateCheck } from './types'; export type Props = { id: string, selectedScopeId: string, - onSave: SaveForDuplicateCheck, + onSave: () => void, enrollmentMetadata?: Enrollment, teiRegistrationMetadata?: TeiRegistration, duplicatesReviewPageSize: number, renderDuplicatesCardActions?: RenderCustomCardActions, - renderDuplicatesDialogActions?: (onCancel: () => void, onSave: SaveForDuplicateCheck) => Node, + renderDuplicatesDialogActions?: (onCancel: () => void, onSave: () => void) => Node, skipDuplicateCheck: ?boolean, }; diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useEnrollmentFormFoundation.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useEnrollmentFormFoundation.js index 49a7b484be..0238087811 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useEnrollmentFormFoundation.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useEnrollmentFormFoundation.js @@ -30,6 +30,7 @@ export const useEnrollmentFormFoundation = ({ locale, }: Props) => { const { data: enrollment, isLoading, error } = useIndexedDBQuery( + // $FlowFixMe - QueryKey can be undefined ['enrollmentForm', program?.id], () => buildEnrollmentForm({ // $FlowFixMe - Flow does not understand that the values are not null here diff --git a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useTrackedEntityTypeCollection.js b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useTrackedEntityTypeCollection.js index ae3d4c3e5a..05a173cfc8 100644 --- a/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useTrackedEntityTypeCollection.js +++ b/src/core_modules/capture-core/components/DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm/hooks/useTrackedEntityTypeCollection.js @@ -26,6 +26,7 @@ export const useTrackedEntityTypeCollection = ({ locale, }: Props): ReturnValues => { const { data: trackedEntityAttributes } = useIndexedDBQuery( + // $FlowFixMe - QueryKey can be undefined ['trackedEntityAttributes', trackedEntityType?.id], () => getTrackedEntityAttributes( trackedEntityType @@ -40,6 +41,7 @@ export const useTrackedEntityTypeCollection = ({ ); const { data: trackedEntityTypeCollection } = useIndexedDBQuery( + // $FlowFixMe - QueryKey can be undefined ['trackedEntityTypeCollection', trackedEntityType?.id], () => buildTrackedEntityTypeCollection({ // $FlowFixMe diff --git a/src/core_modules/capture-core/components/FeedbackBar/FeedbackBar.component.js b/src/core_modules/capture-core/components/FeedbackBar/FeedbackBar.component.js index 2f8f50b16a..c6b1fe0a49 100644 --- a/src/core_modules/capture-core/components/FeedbackBar/FeedbackBar.component.js +++ b/src/core_modules/capture-core/components/FeedbackBar/FeedbackBar.component.js @@ -6,14 +6,14 @@ import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogTitle from '@material-ui/core/DialogTitle'; import { withStyles } from '@material-ui/core/styles'; -import IconButton from '@material-ui/core/IconButton'; +import { IconButton } from 'capture-ui'; import { IconCross24, Button } from '@dhis2/ui'; import i18n from '@dhis2/d2-i18n'; import isDefined from 'd2-utilizr/lib/isDefined'; -const styles = theme => ({ +const styles = () => ({ closeButton: { - height: theme.spacing.unit * 4, + marginTop: '5px', }, actionContainer: { paddingRight: 2, @@ -58,7 +58,7 @@ class Index extends React.Component { const { feedback, classes } = this.props; return ( - + <> { (() => { if (!feedback.action) { @@ -75,15 +75,12 @@ class Index extends React.Component { })() } - + ); } diff --git a/src/core_modules/capture-core/components/FormFields/File/D2File.component.js b/src/core_modules/capture-core/components/FormFields/File/D2File.component.js index a3c449e82a..0b87faba5f 100644 --- a/src/core_modules/capture-core/components/FormFields/File/D2File.component.js +++ b/src/core_modules/capture-core/components/FormFields/File/D2File.component.js @@ -28,6 +28,10 @@ type Props = { mutate: (data: any) => Promise } +type State = { + fileSelectorOpen: boolean, +}; + const styles = theme => ({ horizontalContainer: { display: 'flex', @@ -74,10 +78,18 @@ const styles = theme => ({ }, }); -class D2FilePlain extends Component { +class D2FilePlain extends Component { hiddenFileSelectorRef: any; + fileSelectorOpen: boolean; + constructor(props: Props) { + super(props); + this.state = { + fileSelectorOpen: false, + }; + } handleFileChange = (e: Object) => { + this.setState((state) => { state.fileSelectorOpen = false; }); e.preventDefault(); const file = e.target.files[0]; e.target.value = null; @@ -100,12 +112,23 @@ class D2FilePlain extends Component { } handleButtonClick = () => { this.hiddenFileSelectorRef.click(); + this.setState((state) => { state.fileSelectorOpen = true; }); + } + + handleCancel = () => { + this.setState((state) => { state.fileSelectorOpen = false; }); } handleRemoveClick = () => { this.props.onBlur(null); } + handleBlur = () => { + if (!this.state.fileSelectorOpen) { + this.props.onBlur(this.getFileUrl()); + } + } + getFileUrl = () => { const value = this.props.value; if (value) { @@ -122,7 +145,7 @@ class D2FilePlain extends Component { const containerClass = isVertical ? classes.verticalContainer : classes.horizontalContainer; const selectedFileTextContainerClass = isVertical ? classes.verticalSelectedFileTextContainer : classes.horizontalSelectedFileTextContainer; return ( -
+
{ this.hiddenFileSelectorRef = hiddenFileSelector; }} onChange={e => this.handleFileChange(e)} + onCancel={this.handleCancel} // eslint-disable-line react/no-unknown-property /> { (() => { diff --git a/src/core_modules/capture-core/components/FormFields/Image/D2Image.component.js b/src/core_modules/capture-core/components/FormFields/Image/D2Image.component.js index 5f9458930b..d9682c56b5 100644 --- a/src/core_modules/capture-core/components/FormFields/Image/D2Image.component.js +++ b/src/core_modules/capture-core/components/FormFields/Image/D2Image.component.js @@ -27,6 +27,10 @@ type Props = { mutate: (data: any) => Promise } +type State = { + imageSelectorOpen: boolean, +}; + const styles = theme => ({ horizontalContainer: { display: 'flex', @@ -72,10 +76,18 @@ const styles = theme => ({ }, }); -class D2ImagePlain extends Component { +class D2ImagePlain extends Component { hiddenimageSelectorRef: any; + imageSelectorOpen: boolean; + constructor(props: Props) { + super(props); + this.state = { + imageSelectorOpen: false, + }; + } handleImageChange = (e: Object) => { + this.setState((state) => { state.imageSelectorOpen = false; }); e.preventDefault(); const image = e.target.files[0]; e.target.value = null; @@ -98,13 +110,24 @@ class D2ImagePlain extends Component { } handleButtonClick = () => { this.hiddenimageSelectorRef.click(); + this.setState((state) => { state.imageSelectorOpen = true; }); + } + + handleCancel = () => { + this.setState((state) => { state.imageSelectorOpen = false; }); } handleRemoveClick = () => { this.props.onBlur(null); } - getimageUrl = () => { + handleBlur = () => { + if (!this.state.imageSelectorOpen) { + this.props.onBlur(this.getImageUrl()); + } + } + + getImageUrl = () => { const value = this.props.value; if (value) { return value.url || inMemoryFileStore.get(value.value); @@ -116,13 +139,13 @@ class D2ImagePlain extends Component { const { value, classes, asyncUIState, orientation, disabled } = this.props; const isVertical = orientation === orientations.VERTICAL; const isUploading = asyncUIState && asyncUIState.loading; - const imageUrl = this.getimageUrl(); + const imageUrl = this.getImageUrl(); // $FlowFixMe[prop-missing] automated comment const containerClass = isVertical ? classes.verticalContainer : classes.horizontalContainer; // $FlowFixMe[prop-missing] automated comment const selectedImageTextContainerClass = isVertical ? classes.verticalSelectedImageTextContainer : classes.horizontalSelectedImageTextContainer; return ( -
+
{ this.hiddenimageSelectorRef = hiddenimageSelector; }} onChange={e => this.handleImageChange(e)} + onCancel={this.handleCancel} // eslint-disable-line react/no-unknown-property /> { (() => { diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.component.js b/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.component.js index 1057e0ee76..6cb7c2db71 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.component.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.component.js @@ -39,22 +39,27 @@ const MultiSelectFieldComponentPlain = (props: Props) => { onSelect(multiSelectSelected.join(MULTI_TEXT_SEPARATOR)); }; + const handleBlur = () => { + onBlur(selected ? selected.join(MULTI_TEXT_SEPARATOR) : null); + }; + return ( - - {options.map(option => ( - - ))} - +
+ + {options.map(option => ( + + ))} + +
); }; diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.types.js b/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.types.js index 286daf7be7..c708a8b3a7 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.types.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/MultiSelectField.types.js @@ -11,7 +11,7 @@ type MultiSelectOptionConfig = { export type Props = { onSelect: (value?: string) => void, onFocus: () => void, - onBlur: () => void, + onBlur: (value: ?string) => void, options: Array, value?: string, translations: { diff --git a/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/withFocusHandler.js b/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/withFocusHandler.js index 378623435f..cdbf756da5 100644 --- a/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/withFocusHandler.js +++ b/src/core_modules/capture-core/components/FormFields/New/Fields/MultiSelectField/withFocusHandler.js @@ -7,7 +7,7 @@ type Props = { onSetFocus: () => void, onRemoveFocus: () => void, inFocus: boolean, - onBlur?: ?(event: SyntheticEvent) => void, + onBlur?: (value: ?string) => void, onFocus: () => void, classes: { inputWrapperFocused: string, @@ -15,10 +15,11 @@ type Props = { } }; -export const withFocusHandler = () => (InnerCompnent: React.ComponentType) => +export const withFocusHandler = () => (InnerComponent: React.ComponentType) => class FocusHandlerHOC extends React.Component { - handleRemoveFocus = () => { + handleRemoveFocus = (value: string) => { this.props.onRemoveFocus(); + this.props.onBlur && this.props.onBlur(value); } handleFocus = () => { @@ -40,7 +41,7 @@ export const withFocusHandler = () => (InnerCompnent: React.ComponentType) className={classNames(defaultClasses.inputWrapper, inputWrapper)} > {/* $FlowFixMe[cannot-spread-inexact] automated comment */} - ({ type Props = { onSelectClick: (selectedOrgUnit: Object) => void, + onBlur: (selectedOrgUnit: Object) => void, selected?: ?string, maxTreeHeight?: ?number, disabled?: ?boolean, @@ -56,6 +57,7 @@ type Props = { const OrgUnitFieldPlain = (props: Props) => { const { onSelectClick, + onBlur, classes, selected, maxTreeHeight, @@ -136,10 +138,15 @@ const OrgUnitFieldPlain = (props: Props) => { setSearchText(event.currentTarget.value); }; + const handleBlur = () => { + onBlur && onBlur(null); + }; + const styles = maxTreeHeight ? { maxHeight: maxTreeHeight, overflowY: 'auto' } : null; return (
{ ); } + onBlur = () => { + this.props.onSelect(this.props.value); + } + render() { const { options, @@ -220,6 +224,7 @@ class OptionsSelectVirtualizedPlain extends React.Component { >
{/* $FlowFixMe[cannot-spread-inexact] automated comment */} { focusSelectedInput.current = true; }; + const handleBlur = () => { + // $FlowExpectedError + onSet(value); + }; + if (value) { return ( { } return ( -
+
({ + noMatchFound: { + color: colors.red600, + fontSize: theme.typography.pxToRem(14), + }, +}); + type Props = { onSet: (user: User) => void, inputWrapperClasses: Object, @@ -18,6 +27,7 @@ type Props = { inputPlaceholderText?: ?string, useUpwardList?: ?boolean, querySingleResource: QuerySingleResource, + ...CssClasses, }; type State = { @@ -26,6 +36,7 @@ type State = { suggestionsError?: ?string, highlightedSuggestion?: ?User, inputKey: number, + noMatch: boolean, }; const exitBehaviours = { @@ -46,6 +57,7 @@ class UserSearchPlain extends React.Component { suggestions: [], searchValue: '', inputKey: 0, + noMatch: false, }; this.domNames = { @@ -82,6 +94,7 @@ class UserSearchPlain extends React.Component { suggestions, highlightedSuggestion: undefined, searchValue, + noMatch: suggestions.length === 0, }); } @@ -90,6 +103,7 @@ class UserSearchPlain extends React.Component { suggestions: [], highlightedSuggestion: undefined, searchValue: '', + noMatch: false, }); } @@ -133,11 +147,14 @@ class UserSearchPlain extends React.Component { id: au.id, name: au.displayName, username: au.username, + firstName: au.firstName, + surname: au.surname, })); }); handleInputChange = (value: string) => { this.cancelablePromise && this.cancelablePromise.cancel(); + this.setState({ noMatch: false }); if (value.length > 1) { const searchPromise = this.search(value); @@ -294,6 +311,15 @@ class UserSearchPlain extends React.Component { ); } + renderNoMatchFound() { + const { noMatch } = this.state; + const { classes } = this.props; + + return noMatch ? ( + {i18n.t('No results found')} + ) : null; + } + render() { return (
@@ -301,6 +327,7 @@ class UserSearchPlain extends React.Component { value={this.domNames} > {this.renderInput()} + {this.renderNoMatchFound()} {this.renderSuggestions()}
@@ -308,4 +335,4 @@ class UserSearchPlain extends React.Component { } } -export const UserSearch = withApiUtils(UserSearchPlain); +export const UserSearch = withStyles(getStyles)(withApiUtils(UserSearchPlain)); diff --git a/src/core_modules/capture-core/components/FormFields/UserField/index.js b/src/core_modules/capture-core/components/FormFields/UserField/index.js index 3e88ffe311..e534c4387a 100644 --- a/src/core_modules/capture-core/components/FormFields/UserField/index.js +++ b/src/core_modules/capture-core/components/FormFields/UserField/index.js @@ -1,2 +1,3 @@ // @flow export { UserField } from './UserField.component'; +export type { User as UserFormField } from './types'; diff --git a/src/core_modules/capture-core/components/FormFields/UserField/types.js b/src/core_modules/capture-core/components/FormFields/UserField/types.js index 0695b283f1..c8d34e711a 100644 --- a/src/core_modules/capture-core/components/FormFields/UserField/types.js +++ b/src/core_modules/capture-core/components/FormFields/UserField/types.js @@ -3,4 +3,6 @@ export type User = { id: string, username: string, name: string, + firstName: string, + surname: string, }; diff --git a/src/core_modules/capture-core/components/ListView/ColumnSelector/ColumnSelector.component.js b/src/core_modules/capture-core/components/ListView/ColumnSelector/ColumnSelector.component.js index 44ae08d186..6974ef1048 100644 --- a/src/core_modules/capture-core/components/ListView/ColumnSelector/ColumnSelector.component.js +++ b/src/core_modules/capture-core/components/ListView/ColumnSelector/ColumnSelector.component.js @@ -1,6 +1,6 @@ // @flow import * as React from 'react'; -import { IconButton } from '@material-ui/core'; +import { IconButton } from 'capture-ui'; import { IconSettings24, Tooltip } from '@dhis2/ui'; import i18n from '@dhis2/d2-i18n'; import { ColumnSelectorDialog } from './ColumnSelectorDialog.component'; diff --git a/src/core_modules/capture-core/components/ListView/Main/ListViewMain.component.js b/src/core_modules/capture-core/components/ListView/Main/ListViewMain.component.js index 313059ade4..87c71afbee 100644 --- a/src/core_modules/capture-core/components/ListView/Main/ListViewMain.component.js +++ b/src/core_modules/capture-core/components/ListView/Main/ListViewMain.component.js @@ -25,6 +25,7 @@ const getStyles = (theme: Theme) => ({ }, topBarButtonContainer: { display: 'flex', + gap: '6px', alignItems: 'center', }, paginationContainer: { diff --git a/src/core_modules/capture-core/components/ListView/Menu/ListViewMenu.component.js b/src/core_modules/capture-core/components/ListView/Menu/ListViewMenu.component.js index 78a455f997..dd8d628a4d 100644 --- a/src/core_modules/capture-core/components/ListView/Menu/ListViewMenu.component.js +++ b/src/core_modules/capture-core/components/ListView/Menu/ListViewMenu.component.js @@ -1,8 +1,9 @@ // @flow import React, { useCallback, memo, type ComponentType } from 'react'; +import { IconButton } from 'capture-ui'; import { withStyles } from '@material-ui/core/styles'; import { Divider, IconMore24 } from '@dhis2/ui'; -import { IconButton, Paper, MenuList, MenuItem } from '@material-ui/core'; +import { Paper, MenuList, MenuItem } from '@material-ui/core'; import { MenuPopper } from '../../Popper/Popper.component'; import type { Props } from './listViewMenu.types'; @@ -32,7 +33,7 @@ const getStyles = () => ({ const ListViewMenuPlain = ({ customMenuContents = [], classes }: Props) => { const renderPopperAction = useCallback((togglePopper: Function) => ( diff --git a/src/core_modules/capture-core/components/LockedSelector/LockedSelector.actions.js b/src/core_modules/capture-core/components/LockedSelector/LockedSelector.actions.js index a91ec8b952..d7e395e289 100644 --- a/src/core_modules/capture-core/components/LockedSelector/LockedSelector.actions.js +++ b/src/core_modules/capture-core/components/LockedSelector/LockedSelector.actions.js @@ -1,5 +1,6 @@ // @flow import { actionCreator } from '../../actions/actions.utils'; +import type { CoreOrgUnit } from '../../metadataRetrieval/coreOrgUnit'; export const lockedSelectorActionTypes = { LOADING_START: 'LockedSelector.Loading', @@ -17,7 +18,7 @@ export const lockedSelectorActionTypes = { export const updateSelectionsFromUrl = (data: Object) => actionCreator(lockedSelectorActionTypes.FROM_URL_UPDATE)(data); export const validSelectionsFromUrl = () => actionCreator(lockedSelectorActionTypes.FROM_URL_CURRENT_SELECTIONS_VALID)(); export const invalidSelectionsFromUrl = (error: string) => actionCreator(lockedSelectorActionTypes.FROM_URL_CURRENT_SELECTIONS_INVALID)({ error }); -export const setCurrentOrgUnitBasedOnUrl = (orgUnit: Object) => actionCreator(lockedSelectorActionTypes.FETCH_ORG_UNIT_SUCCESS)(orgUnit); +export const setCurrentOrgUnitBasedOnUrl = (orgUnit: CoreOrgUnit) => actionCreator(lockedSelectorActionTypes.FETCH_ORG_UNIT_SUCCESS)(orgUnit); export const startLoading = () => actionCreator(lockedSelectorActionTypes.LOADING_START)(); export const completeUrlUpdate = () => actionCreator(lockedSelectorActionTypes.FROM_URL_UPDATE_COMPLETE)(); export const errorRetrievingOrgUnitBasedOnUrl = (error: string) => actionCreator(lockedSelectorActionTypes.FETCH_ORG_UNIT_ERROR)({ error }); diff --git a/src/core_modules/capture-core/components/LockedSelector/LockedSelector.epics.js b/src/core_modules/capture-core/components/LockedSelector/LockedSelector.epics.js index ba5a8d7032..ad4329db43 100644 --- a/src/core_modules/capture-core/components/LockedSelector/LockedSelector.epics.js +++ b/src/core_modules/capture-core/components/LockedSelector/LockedSelector.epics.js @@ -1,8 +1,8 @@ // @flow import i18n from '@dhis2/d2-i18n'; import { ofType } from 'redux-observable'; -import { catchError, filter, flatMap, map, startWith, switchMap } from 'rxjs/operators'; -import { from, of } from 'rxjs'; +import { filter, map, concatMap } from 'rxjs/operators'; +import { of } from 'rxjs'; import { lockedSelectorActionTypes, invalidSelectionsFromUrl, @@ -16,32 +16,25 @@ import { import { programCollection } from '../../metaDataMemoryStores'; import { getLocationPathname, pageFetchesOrgUnitUsingTheOldWay } from '../../utils/url'; import { getLocationQuery } from '../../utils/routing'; +import { getCoreOrgUnit } from '../../metadataRetrieval/coreOrgUnit'; -const orgUnitsQuery = id => ({ resource: 'organisationUnits', id }); - -export const getOrgUnitDataBasedOnUrlUpdateEpic = ( - action$: InputObservable, - store: ReduxStore, - { querySingleResource }: ApiUtils) => +export const getOrgUnitDataBasedOnUrlUpdateEpic = (action$: InputObservable, store: ReduxStore) => action$.pipe( ofType(lockedSelectorActionTypes.FROM_URL_UPDATE), filter(action => action.payload.nextProps.orgUnitId), - switchMap((action) => { + concatMap((action) => { const { organisationUnits } = store.value; const { orgUnitId } = action.payload.nextProps; if (organisationUnits[orgUnitId]) { return of(completeUrlUpdate()); } - return from(querySingleResource(orgUnitsQuery(action.payload.nextProps.orgUnitId))) - .pipe( - flatMap(response => - of(setCurrentOrgUnitBasedOnUrl({ id: response.id, name: response.displayName, code: response.code }))), - catchError(() => - of(errorRetrievingOrgUnitBasedOnUrl(i18n.t('Could not get organisation unit')))), - startWith(startLoading()), - ); - }, - )); + return of(startLoading(), getCoreOrgUnit({ + orgUnitId, + onSuccess: setCurrentOrgUnitBasedOnUrl, + onError: () => errorRetrievingOrgUnitBasedOnUrl(i18n.t('Could not get organisation unit')), + })); + }), + ); export const setOrgUnitDataEmptyBasedOnUrlUpdateEpic = (action$: InputObservable) => action$.pipe( @@ -76,4 +69,3 @@ export const validateSelectionsBasedOnUrlUpdateEpic = (action$: InputObservable) return validSelectionsFromUrl(); })); - diff --git a/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.actions.js b/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.actions.js index 260ad0c4ae..806b384742 100644 --- a/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.actions.js +++ b/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.actions.js @@ -1,6 +1,7 @@ // @flow import { actionCreator } from '../../actions/actions.utils'; +import type { CoreOrgUnit } from '../../metadataRetrieval/coreOrgUnit'; export const actionTypes = { FETCH_ORG_UNIT: 'OrgUnitFetcher.FetchOrgUnit', @@ -11,6 +12,6 @@ export const actionTypes = { export const fetchOrgUnit = (orgUnitId: string) => actionCreator(actionTypes.FETCH_ORG_UNIT)({ orgUnitId }); -export const setCurrentOrgUnit = (orgUnit: Object) => actionCreator(actionTypes.FETCH_ORG_UNIT_SUCCESS)(orgUnit); +export const setCurrentOrgUnit = (orgUnit: CoreOrgUnit) => actionCreator(actionTypes.FETCH_ORG_UNIT_SUCCESS)(orgUnit); export const errorRetrievingOrgUnit = () => actionCreator(actionTypes.FETCH_ORG_UNIT_FAILURE)(); diff --git a/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.component.js b/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.component.js index 51f7bb0d70..199c96f6ce 100644 --- a/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.component.js +++ b/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.component.js @@ -6,13 +6,7 @@ import { fetchOrgUnit } from './OrgUnitFetcher.actions'; export const OrgUnitFetcher = (({ orgUnitId, children, error }: Object) => { const dispatch = useDispatch(); - const { orgUnit } = useSelector( - ({ - organisationUnits, - }) => ({ - orgUnit: organisationUnits[orgUnitId], - }), - ); + const orgUnit = useSelector(({ organisationUnits }) => organisationUnits[orgUnitId]); useEffect(() => { if (!orgUnit && orgUnitId) { diff --git a/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.epics.js b/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.epics.js index 1c36e942d1..f82ba69dc0 100644 --- a/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.epics.js +++ b/src/core_modules/capture-core/components/OrgUnitFetcher/OrgUnitFetcher.epics.js @@ -1,17 +1,14 @@ // @flow import { ofType } from 'redux-observable'; -import { catchError, map, switchMap } from 'rxjs/operators'; -import { from, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { getCoreOrgUnit } from '../../metadataRetrieval/coreOrgUnit'; import { actionTypes, setCurrentOrgUnit, errorRetrievingOrgUnit } from './OrgUnitFetcher.actions'; - -const orgUnitsQuery = id => ({ resource: `organisationUnits/${id}` }); - -export const orgUnitFetcherEpic = (action$: InputObservable, _: ReduxStore, { querySingleResource }: ApiUtils, -) => action$.pipe( +export const orgUnitFetcherEpic = (action$: InputObservable) => action$.pipe( ofType(actionTypes.FETCH_ORG_UNIT), - switchMap(({ payload: { orgUnitId } }) => from(querySingleResource(orgUnitsQuery(orgUnitId))) - .pipe(map(({ id, displayName: name }) => setCurrentOrgUnit({ id, name })))) - , - catchError(() => of(errorRetrievingOrgUnit())), + map(({ payload: { orgUnitId } }) => getCoreOrgUnit({ + orgUnitId, + onSuccess: orgUnit => setCurrentOrgUnit(orgUnit), + onError: errorRetrievingOrgUnit, + })), ); diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.epics.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.epics.js index 99857025f9..6d905e0188 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.epics.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPage.epics.js @@ -207,3 +207,4 @@ export const openEnrollmentPageEpic = (action$: InputObservable, store: ReduxSto }, ), ); + diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.component.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.component.js index 0759fcd558..b59ad5a3a5 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.component.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.component.js @@ -1,5 +1,5 @@ // @flow -import React, { type ComponentType } from 'react'; +import React, { type ComponentType, useState, useCallback } from 'react'; import withStyles from '@material-ui/core/styles/withStyles'; import { spacersNum, spacers, colors } from '@dhis2/ui'; import i18n from '@dhis2/d2-i18n'; @@ -13,8 +13,15 @@ import { WidgetError } from '../../../WidgetErrorAndWarning/WidgetError'; import { WidgetIndicator } from '../../../WidgetIndicator'; import { WidgetEnrollmentComment } from '../../../WidgetEnrollmentComment'; import { EnrollmentQuickActions } from './EnrollmentQuickActions'; +import { + TrackedEntityRelationshipsWrapper, +} from '../../common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper'; +import { AddRelationshipRefWrapper } from '../../EnrollmentEditEvent/AddRelationshipRefWrapper'; const getStyles = () => ({ + container: { + position: 'relative', + }, columns: { display: 'flex', }, @@ -59,68 +66,96 @@ export const EnrollmentPageDefaultPlain = ({ hideWidgets, classes, onEventClick, + onLinkedRecordClick, onUpdateTeiAttributeValues, onUpdateEnrollmentDate, onUpdateIncidentDate, onEnrollmentError, ruleEffects, -}: PlainProps) => ( - <> -
{i18n.t('Enrollment Dashboard')}
-
-
- - -
-
- - - - {!hideWidgets.indicator && ( - - )} - {!hideWidgets.feedback && ( - - )} - - {enrollmentId !== 'AUTO' && } +}: PlainProps) => { + const [mainContentVisible, setMainContentVisibility] = useState(true); + const [addRelationShipContainerElement, setAddRelationshipContainerElement] = + useState(undefined); + + const toggleVisibility = useCallback(() => setMainContentVisibility(current => !current), []); + + return ( + <> + +
+
{i18n.t('Enrollment Dashboard')}
+
+
+ + +
+
+ {addRelationShipContainerElement && + {}} + onOpenAddRelationship={toggleVisibility} + onCloseAddRelationship={toggleVisibility} + teiId={teiId} + onLinkedRecordClick={onLinkedRecordClick} + /> + } + + + + {!hideWidgets.indicator && ( + + )} + {!hideWidgets.feedback && ( + + )} + + {enrollmentId !== 'AUTO' && } +
+
-
- -); + + ); +}; export const EnrollmentPageDefaultComponent: ComponentType = withStyles( diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.js index 2e7b8da1bc..9e37366cad 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.js @@ -19,7 +19,7 @@ import { updateTeiDisplayName, } from '../EnrollmentPage.actions'; import { useTrackerProgram } from '../../../../hooks/useTrackerProgram'; -import { useRulesEngineOrgUnit } from '../../../../hooks/useRulesEngineOrgUnit'; +import { useCoreOrgUnit } from '../../../../metadataRetrieval/coreOrgUnit'; import { EnrollmentPageDefaultComponent } from './EnrollmentPageDefault.component'; import { useProgramMetadata, @@ -28,12 +28,14 @@ import { } from './hooks'; import { buildUrlQueryString, useLocationQuery } from '../../../../utils/routing'; import { useFilteredWidgetData } from './hooks/useFilteredWidgetData'; +import { useLinkedRecordClick } from '../../common/TEIRelationshipsWidget'; export const EnrollmentPageDefault = () => { const history = useHistory(); const dispatch = useDispatch(); const { enrollmentId, programId, teiId, orgUnitId } = useLocationQuery(); - const { orgUnit, error } = useRulesEngineOrgUnit(orgUnitId); + const { orgUnit, error } = useCoreOrgUnit(orgUnitId); + const { onLinkedRecordClick } = useLinkedRecordClick(); const program = useTrackerProgram(programId); const { @@ -102,6 +104,7 @@ export const EnrollmentPageDefault = () => { }; const onEnrollmentError = message => dispatch(showEnrollmentError({ message })); + if (error) { return error.errorComponent; } @@ -122,6 +125,7 @@ export const EnrollmentPageDefault = () => { widgetEffects={outputEffects} hideWidgets={hideWidgets} onEventClick={onEventClick} + onLinkedRecordClick={onLinkedRecordClick} onUpdateTeiAttributeValues={onUpdateTeiAttributeValues} onUpdateEnrollmentDate={onUpdateEnrollmentDate} onUpdateIncidentDate={onUpdateIncidentDate} diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.types.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.types.js index 4e88f4a39b..143b9b8c28 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.types.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.types.js @@ -1,12 +1,13 @@ // @flow import { typeof effectActions } from '@dhis2/rules-engine-javascript'; -import type { Program } from 'capture-core/metaData'; +import type { TrackerProgram } from 'capture-core/metaData'; import type { Stage } from 'capture-core/components/WidgetStagesAndEvents/types/common.types'; import type { WidgetEffects, HideWidgets } from '../../common/EnrollmentOverviewDomain'; import type { Event } from '../../common/EnrollmentOverviewDomain/useCommonEnrollmentDomainData'; +import type { LinkedRecordClick } from '../../../WidgetsRelationship/WidgetTrackedEntityRelationship'; export type Props = {| - program: Program, + program: TrackerProgram, enrollmentId: string, teiId: string, events: ?Array, @@ -20,6 +21,7 @@ export type Props = {| onCreateNew: (stageId: string) => void, onEventClick: (eventId: string) => void, onUpdateTeiAttributeValues: (attributes: Array<{ [key: string]: string }>, teiDisplayName: string) => void, + onLinkedRecordClick: LinkedRecordClick, onUpdateEnrollmentDate: (enrollmentDate: string) => void, onUpdateIncidentDate: (incidentDate: string) => void, onEnrollmentError: (message: string) => void, diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/hooks/useProgramStages.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/hooks/useProgramStages.js index 3c8b50285a..7d692e9867 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/hooks/useProgramStages.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/hooks/useProgramStages.js @@ -1,11 +1,12 @@ // @flow +import { useMemo } from 'react'; import log from 'loglevel'; import { errorCreator } from 'capture-core-utils'; import i18n from '@dhis2/d2-i18n'; import type { apiProgramStage } from 'capture-core/metaDataStoreLoaders/programs/quickStoreOperations/types'; import { Program } from '../../../../../metaData'; -export const useProgramStages = (program: Program, programStages?: Array) => { +export const useProgramStages = (program: Program, programStages?: Array) => useMemo(() => { const stages = []; if (program && programStages) { program.stages.forEach((item) => { @@ -48,4 +49,4 @@ export const useProgramStages = (program: Program, programStages?: Array (
-
{i18n.t('Enrollment{{escape}} New Event', { escape: ':' })}
- {(() => { - if (pageFailure) { +}: Props) => ( +
+
{i18n.t('Enrollment{{escape}} New Event', { escape: ':' })}
+ {(() => { + if (pageFailure) { + return ( +
+ {i18n.t('There was an error loading the page')} +
+ ); + } else if (!orgUnitId) { + return ( + + {i18n.t('Choose a registering unit to start reporting')} + + ); + } else if (!ready) { + return null; + } + return (
- {i18n.t('There was an error loading the page')} -
- ); - } else if (!orgUnitId) { - return ( - - {i18n.t('Choose a registering unit to start reporting')} - - ); - } else if (!ready) { - return null; - } - - return ( -
-
-
-
- {!stageId ? - +
+
+ {!stageId ? + + : + + } +
+
+
+ + + {!hideWidgets.feedback && ( + - : - - } -
-
-
- - - {!hideWidgets.feedback && ( - - )} - {!hideWidgets.indicator && ( - - )} - - +
-
- ); - })()} -
); + ); + })()} +
); export const EnrollmentAddEventPageDefaultComponent: ComponentType<$Diff> = withStyles(styles)(EnrollmentAddEventPagePain); diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.container.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.container.js index 1bd16b7a80..d0356732ac 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.container.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.container.js @@ -10,7 +10,7 @@ import { useCommonEnrollmentDomainData, useRuleEffects } from '../../common/Enro import type { Props } from './ProgramStageSelector.types'; import { useProgramFromIndexedDB } from '../../../../utils/cachedDataHooks/useProgramFromIndexedDB'; import { useLocationQuery, buildUrlQueryString } from '../../../../utils/routing'; -import { useRulesEngineOrgUnit } from '../../../../hooks/useRulesEngineOrgUnit'; +import { useCoreOrgUnit } from '../../../../metadataRetrieval/coreOrgUnit'; import { useTrackerProgram } from '../../../../hooks/useTrackerProgram'; @@ -24,7 +24,7 @@ export const ProgramStageSelector = ({ programId, orgUnitId, teiId, enrollmentId isError: programError, } = useProgramFromIndexedDB(programId); - const { orgUnit } = useRulesEngineOrgUnit(orgUnitId); + const { orgUnit } = useCoreOrgUnit(orgUnitId); const programRules = useTrackerProgram(programId); const ruleEffects = useRuleEffects({ diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/AddRelationshipRefWrapper/AddRelationshipRefWrapper.component.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/AddRelationshipRefWrapper/AddRelationshipRefWrapper.component.js new file mode 100644 index 0000000000..7235bb1a14 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/AddRelationshipRefWrapper/AddRelationshipRefWrapper.component.js @@ -0,0 +1,21 @@ +// @flow +import React, { useEffect, useRef } from 'react'; + +type Props = { + setRelationshipRef: (HTMLDivElement) => void, +} + +export const AddRelationshipRefWrapper = ({ setRelationshipRef }: Props) => { + const renderRelationshipRef = useRef(undefined); + + // Extracting the logic to separate component because of the OrgUnitFetcher + useEffect(() => { + if (renderRelationshipRef.current) { + setRelationshipRef(renderRelationshipRef.current); + } + }, [setRelationshipRef]); + + return ( +
+ ); +}; diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/AddRelationshipRefWrapper/index.js b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/AddRelationshipRefWrapper/index.js new file mode 100644 index 0000000000..7400ed7ff3 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/AddRelationshipRefWrapper/index.js @@ -0,0 +1,3 @@ +// @flow + +export { AddRelationshipRefWrapper } from './AddRelationshipRefWrapper.component'; 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 1add434b21..6137c49b32 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 @@ -1,5 +1,5 @@ // @flow -import React from 'react'; +import React, { useCallback, useState } from 'react'; import type { ComponentType } from 'react'; import i18n from '@dhis2/d2-i18n'; import { spacersNum } from '@dhis2/ui'; @@ -18,12 +18,19 @@ import { IncompleteSelectionsMessage } from '../../IncompleteSelectionsMessage'; import { WidgetEventComment } from '../../WidgetEventComment'; import { OrgUnitFetcher } from '../../OrgUnitFetcher'; import { TopBar } from './TopBar.container'; +import { + TrackedEntityRelationshipsWrapper, +} from '../common/TEIRelationshipsWidget/TrackedEntityRelationshipsWrapper'; +import { AddRelationshipRefWrapper } from './AddRelationshipRefWrapper'; import { NoticeBox } from '../../NoticeBox'; const styles = ({ typography }) => ({ page: { margin: spacersNum.dp16, }, + addRelationshipContainer: { + margin: spacersNum.dp16, + }, columns: { display: 'flex', }, @@ -52,6 +59,7 @@ const EnrollmentEditEventPagePain = ({ programStage, teiId, enrollmentId, + trackedEntityTypeId, programId, enrollmentsAsOptions, trackedEntityName, @@ -62,6 +70,7 @@ const EnrollmentEditEventPagePain = ({ onAddNew, classes, onGoBack, + onLinkedRecordClick, orgUnitId, eventDate, scheduleDate, @@ -71,85 +80,111 @@ const EnrollmentEditEventPagePain = ({ onEnrollmentSuccess, onCancelEditEvent, onHandleScheduleSave, -}: PlainProps) => ( - - -
-
- {mode === dataEntryKeys.VIEW - ? i18n.t('Enrollment{{escape}} View Event', { escape: ':' }) - : i18n.t('Enrollment{{escape}} Edit Event', { escape: ':' })} +}: PlainProps) => { + const [mainContentVisible, setMainContentVisible] = useState(true); + const [addRelationShipContainerElement, setAddRelationShipContainerElement] = useState(undefined); + + const toggleVisibility = useCallback(() => setMainContentVisible(current => !current), []); + + return ( + + +
+
-
-
- {pageStatus === pageStatuses.DEFAULT && programStage && ( - +
+ {mode === dataEntryKeys.VIEW + ? i18n.t('Enrollment{{escape}} View Event', { escape: ':' }) + : i18n.t('Enrollment{{escape}} Edit Event', { escape: ':' })} +
+
+
+ {pageStatus === pageStatuses.DEFAULT && programStage && ( + + )} + {pageStatus === pageStatuses.MISSING_DATA && ( + {i18n.t('The enrollment event data could not be found')} + )} + {pageStatus === pageStatuses.WITHOUT_ORG_UNIT_SELECTED && ( + + {i18n.t('Choose a registering unit to start reporting')} + + )} +
+
+ + + + {!hideWidgets.feedback && ( + + )} + {!hideWidgets.indicator && ( + + )} + {addRelationShipContainerElement && + {}} + onLinkedRecordClick={onLinkedRecordClick} + /> + } + + - )} - {pageStatus === pageStatuses.MISSING_DATA && ( - {i18n.t('The enrollment event data could not be found')} - )} - {pageStatus === pageStatuses.WITHOUT_ORG_UNIT_SELECTED && ( - - {i18n.t('Choose a registering unit to start reporting')} - - )} -
-
- - - - {!hideWidgets.feedback && ( - - )} - {!hideWidgets.indicator && ( - - )} - - +
+
- -
-
-); + + ); +}; export const EnrollmentEditEventPageComponent: ComponentType<$Diff> = withStyles(styles)(EnrollmentEditEventPagePain); 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 b6a27e92bf..82ee3b9360 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 @@ -21,6 +21,7 @@ import { useEvent } from './hooks'; import type { Props } from './EnrollmentEditEventPage.types'; import { LoadingMaskForPage } from '../../LoadingMasks'; import { cleanUpDataEntry } from '../../DataEntry'; +import { useLinkedRecordClick } from '../common/TEIRelationshipsWidget'; import { pageKeys } from '../../App/withAppUrlSync'; import { withErrorMessageHandler } from '../../../HOC'; @@ -90,6 +91,8 @@ const EnrollmentEditEventPageWithContextPlain = ({ const history = useHistory(); const dispatch = useDispatch(); + const { onLinkedRecordClick } = useLinkedRecordClick(); + useEffect(() => () => { dispatch(cleanUpDataEntry(dataEntryIds.ENROLLMENT_EVENT)); }, [dispatch]); @@ -116,6 +119,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ } }, [initMode, enrollmentId, eventId, orgUnitId, history]); + const { enrollment: enrollmentSite } = useCommonEnrollmentDomainData(teiId, enrollmentId, programId); const onGoBack = () => history.push(`/enrollment?${buildUrlQueryString({ enrollmentId })}`); @@ -123,10 +127,9 @@ const EnrollmentEditEventPageWithContextPlain = ({ dispatch(updateEnrollmentEvents(eventId, eventData)); history.push(`enrollment?${buildUrlQueryString({ enrollmentId })}`); }; - const enrollmentSite = useCommonEnrollmentDomainData(teiId, enrollmentId, programId).enrollment; const { teiDisplayName } = useTeiDisplayName(teiId, programId); // $FlowFixMe - const trackedEntityName = program?.trackedEntityType?.name; + const { name: trackedEntityName, id: trackedEntityTypeId } = program?.trackedEntityType; const enrollmentsAsOptions = buildEnrollmentsAsOptions([enrollmentSite || {}], programId); const event = enrollmentSite?.events?.find(item => item.event === eventId); const eventDate = getEventDate(event); @@ -135,6 +138,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ const dataEntryKey = `${dataEntryIds.ENROLLMENT_EVENT}-${currentPageMode}`; const outputEffects = useWidgetDataFromStore(dataEntryKey); + const pageStatus = getPageStatus({ orgUnitId, enrollmentSite, @@ -154,6 +158,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ hideWidgets={hideWidgets} teiId={teiId} enrollmentId={enrollmentId} + trackedEntityTypeId={trackedEntityTypeId} enrollmentsAsOptions={enrollmentsAsOptions} teiDisplayName={teiDisplayName} trackedEntityName={trackedEntityName} @@ -162,6 +167,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ onAddNew={onAddNew} orgUnitId={orgUnitId} eventDate={eventDate} + onLinkedRecordClick={onLinkedRecordClick} onEnrollmentError={onEnrollmentError} onEnrollmentSuccess={onEnrollmentSuccess} eventStatus={event?.status} 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 8e16b09325..8f269fed00 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 @@ -1,6 +1,7 @@ // @flow import type { ProgramStage } from '../../../metaData'; import type { WidgetEffects, HideWidgets } from '../common/EnrollmentOverviewDomain'; +import type { LinkedRecordClick } from '../../WidgetsRelationship/WidgetTrackedEntityRelationship'; export type PlainProps = {| programStage: ?ProgramStage, @@ -9,6 +10,7 @@ export type PlainProps = {| teiId: string, enrollmentId: string, programId: string, + trackedEntityTypeId: string, mode: string, orgUnitId: string, trackedEntityName: string, @@ -19,6 +21,7 @@ export type PlainProps = {| onDelete: () => void, onAddNew: () => void, onGoBack: () => void, + onLinkedRecordClick: LinkedRecordClick, onEnrollmentError: (message: string) => void, onEnrollmentSuccess: () => void, onCancelEditEvent: (isScheduled: boolean) => void, 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 5589098844..f791f50b93 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,6 +39,7 @@ 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}} ', { diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js index e716f71741..6beb555bc6 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.actions.js @@ -1,7 +1,12 @@ // @flow -import type { ProgramStage, RenderFoundation } from '../../../../metaData'; import { actionCreator } from '../../../../actions/actions.utils'; import { effectMethods } from '../../../../trackerOffline'; +import type { + EnrollmentPayload, +} from '../../../DataEntries/EnrollmentRegistrationEntry/EnrollmentRegistrationEntry.types'; +import type { + TeiPayload, +} from '../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; export const registrationFormActionTypes = { NEW_TRACKED_ENTITY_INSTANCE_SAVE_START: 'StartSavingNewTrackedEntityInstance', @@ -16,8 +21,8 @@ export const registrationFormActionTypes = { }; // without enrollment -export const startSavingNewTrackedEntityInstance = (formFoundation: RenderFoundation) => - actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_SAVE_START)({ formFoundation }); +export const startSavingNewTrackedEntityInstance = (teiPayload: TeiPayload) => + actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_SAVE_START)({ teiPayload }); export const saveNewTrackedEntityInstance = (candidateForRegistration: any) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_SAVE)( @@ -41,11 +46,9 @@ export const saveNewTrackedEntityInstance = (candidateForRegistration: any) => ); // with enrollment -export const startSavingNewTrackedEntityInstanceWithEnrollment = (formFoundation: RenderFoundation, teiId: string, uid: string, firstStage?: ProgramStage) => +export const startSavingNewTrackedEntityInstanceWithEnrollment = (enrollmentPayload: EnrollmentPayload, uid: string) => actionCreator(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START)({ - formFoundation, - teiId, - firstStage, + enrollmentPayload, uid, }); diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.component.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.component.js index cf14636450..af18875d9d 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.component.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.component.js @@ -15,9 +15,7 @@ import { ResultsPageSizeContext } from '../../shared-contexts'; import { navigateToEnrollmentOverview } from '../../../../actions/navigateToEnrollmentOverview/navigateToEnrollmentOverview.actions'; import { useLocationQuery } from '../../../../utils/routing'; import { EnrollmentRegistrationEntryWrapper } from '../EnrollmentRegistrationEntryWrapper.component'; -import { - useMetadataForRegistrationForm, -} from '../../../DataEntries/common/TEIAndEnrollment/useMetadataForRegistrationForm'; +import { useCurrentOrgUnitId } from '../../../../hooks/useCurrentOrgUnitId'; const getStyles = ({ typography }) => ({ container: { @@ -99,7 +97,7 @@ const RegistrationDataEntryPlain = ({ const { resultsPageSize } = useContext(ResultsPageSizeContext); const { scopeType, programName, trackedEntityName } = useScopeInfo(selectedScopeId); const titleText = useScopeTitleText(selectedScopeId); - const { formFoundation } = useMetadataForRegistrationForm({ selectedScopeId }); + const currentOrgUnitId = useCurrentOrgUnitId(); const handleRegistrationScopeSelection = (id) => { setScopeId(id); @@ -178,10 +176,10 @@ const RegistrationDataEntryPlain = ({ - onSaveWithEnrollment(customFormFoundation, firstStageMetaData?.stage) - } + onSave={onSaveWithEnrollment} saveButtonText={(trackedEntityTypeNameLC: string) => i18n.t('Save {{trackedEntityTypeName}}', { trackedEntityTypeName: trackedEntityTypeNameLC, interpolation: { escapeValue: false }, @@ -233,11 +231,12 @@ const RegistrationDataEntryPlain = ({ onSaveWithoutEnrollment(formFoundation)} + onSave={onSaveWithoutEnrollment} duplicatesReviewPageSize={resultsPageSize} renderDuplicatesDialogActions={renderDuplicatesDialogActions} renderDuplicatesCardActions={renderDuplicatesCardActions} diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js index 41f5a864d5..8aeda04f1f 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.container.js @@ -26,15 +26,15 @@ export const RegistrationDataEntry: ComponentType = ({ const { teiId } = useLocationQuery(); const dispatchOnSaveWithoutEnrollment = useCallback( - (formFoundation) => { dispatch(startSavingNewTrackedEntityInstance(formFoundation)); }, + (teiPayload) => { dispatch(startSavingNewTrackedEntityInstance(teiPayload)); }, [dispatch]); const dispatchOnSaveWithEnrollment = useCallback( - (formFoundation, firstStage) => { + (enrollmentPayload) => { const uid = uuid(); - dispatch(startSavingNewTrackedEntityInstanceWithEnrollment(formFoundation, teiId, uid, firstStage)); + dispatch(startSavingNewTrackedEntityInstanceWithEnrollment(enrollmentPayload, uid)); }, - [dispatch, teiId]); + [dispatch]); const dataEntryIsReady = useSelector(({ dataEntries }) => (!!dataEntries[dataEntryId])); diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js index 004adb75f7..be02acbcb5 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/RegistrationDataEntry.epics.js @@ -1,59 +1,31 @@ // @flow import { ofType } from 'redux-observable'; -import { pipe } from 'capture-core-utils'; import { flatMap, map } from 'rxjs/operators'; import { of, EMPTY } from 'rxjs'; -import { FEATURETYPE, dataEntryKeys } from 'capture-core/constants'; +import { dataEntryKeys } from 'capture-core/constants'; import { registrationFormActionTypes, saveNewTrackedEntityInstance, saveNewTrackedEntityInstanceWithEnrollment, } from './RegistrationDataEntry.actions'; -import { getTrackerProgramThrowIfNotFound, dataElementTypes, Section } from '../../../../metaData'; +import { getTrackerProgramThrowIfNotFound } from '../../../../metaData'; import { navigateToEnrollmentOverview, } from '../../../../actions/navigateToEnrollmentOverview/navigateToEnrollmentOverview.actions'; -import { convertFormToClient, convertClientToServer } from '../../../../converters'; import { buildUrlQueryString, shouldUseNewDashboard } from '../../../../utils/routing'; import { - deriveAutoGenerateEvents, - deriveFirstStageDuringRegistrationEvent, getStageWithOpenAfterEnrollment, - standardGeoJson, PAGES, } from './helpers'; -const convertFn = pipe(convertFormToClient, convertClientToServer); - -const geometryType = formValuesKey => Object.values(FEATURETYPE).find(geometryKey => geometryKey === formValuesKey); - -const deriveAttributesFromFormValues = (formValues = {}) => - Object.keys(formValues) - .filter(key => !geometryType(key)) - .map(key => ({ attribute: key, value: formValues[key] })); - -const deriveGeometryFromFormValues = (formValues = {}) => - Object.keys(formValues) - .filter(key => geometryType(key)) - .reduce((acc, currentKey) => (standardGeoJson(formValues[currentKey])), undefined); - -export const startSavingNewTrackedEntityInstanceEpic: Epic = (action$: InputObservable, store: ReduxStore) => +export const startSavingNewTrackedEntityInstanceEpic: Epic = (action$: InputObservable) => action$.pipe( ofType(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_SAVE_START), map((action) => { - const { currentSelections: { orgUnitId, trackedEntityTypeId }, formsValues } = store.value; - const values = formsValues['newPageDataEntryId-newTei']; - const formFoundation = action.payload?.formFoundation; - const formServerValues = formFoundation?.convertValues(values, convertFn); + const { teiPayload } = action.payload; return saveNewTrackedEntityInstance( { - trackedEntities: [{ - attributes: deriveAttributesFromFormValues(formServerValues), - geometry: deriveGeometryFromFormValues(values), - enrollments: [], - orgUnit: orgUnitId, - trackedEntityType: trackedEntityTypeId, - }], + trackedEntities: [teiPayload], }); }), ); @@ -80,78 +52,26 @@ export const startSavingNewTrackedEntityInstanceWithEnrollmentEpic: Epic = ( action$.pipe( ofType(registrationFormActionTypes.NEW_TRACKED_ENTITY_INSTANCE_WITH_ENROLLMENT_SAVE_START), map((action) => { - const formId = 'newPageDataEntryId-newEnrollment'; - const { currentSelections: { orgUnitId, programId }, formsValues, dataEntriesFieldsValue } = store.value; + const { currentSelections: { programId } } = store.value; const { dataStore, userDataStore, temp } = store.value.useNewDashboard; - const { formFoundation, teiId: trackedEntity, firstStage: firstStageMetadata, uid } = action.payload; - const fieldsValue = dataEntriesFieldsValue[formId] || {}; - const { occurredAt, enrolledAt, geometry } = fieldsValue; - const attributeCategoryOptionsId = 'attributeCategoryOptions'; - const attributeCategoryOptions = Object.keys(fieldsValue) - .filter(key => key.startsWith(attributeCategoryOptionsId)) - .reduce((acc, key) => { - const categoryId = key.split('-')[1]; - acc[categoryId] = fieldsValue[key]; - return acc; - }, {}); - const { trackedEntityType, stages } = getTrackerProgramThrowIfNotFound(programId); - const currentFormData = formsValues[formId] || {}; + const { enrollmentPayload, uid } = action.payload; + const { stages, useFirstStageDuringRegistration } = getTrackerProgramThrowIfNotFound(programId); + const shouldRedirect = shouldUseNewDashboard(userDataStore, dataStore, temp, programId); const { stageWithOpenAfterEnrollment, redirectTo } = getStageWithOpenAfterEnrollment( stages, - firstStageMetadata, + useFirstStageDuringRegistration, shouldRedirect, ); - const convertedValues = formFoundation.convertAndGroupBySection(currentFormData, convertFn); - const formServerValues = convertedValues[Section.groups.ENROLLMENT]; - const currentEventValues = convertedValues[Section.groups.EVENT]; - - const firstStageDuringRegistrationEvent = deriveFirstStageDuringRegistrationEvent({ - firstStageMetadata, - programId, - orgUnitId, - currentEventValues, - fieldsValue, - attributeCategoryOptions, - }); - const autoGenerateEvents = deriveAutoGenerateEvents({ - stages, - enrolledAt, - occurredAt, - programId, - orgUnitId, - firstStageMetadata, - attributeCategoryOptions, - }); - const allEventsToBeCreated = firstStageDuringRegistrationEvent - ? [firstStageDuringRegistrationEvent, ...autoGenerateEvents] - : autoGenerateEvents; - const eventIndex = allEventsToBeCreated.findIndex( + const eventIndex = enrollmentPayload.enrollments[0]?.events.findIndex( eventsToBeCreated => eventsToBeCreated.programStage === stageWithOpenAfterEnrollment?.id, ); return saveNewTrackedEntityInstanceWithEnrollment({ candidateForRegistration: { trackedEntities: [ - { - geometry: deriveGeometryFromFormValues(currentFormData), - enrollments: [ - { - geometry: standardGeoJson(geometry), - occurredAt: convertFn(occurredAt, dataElementTypes.DATE), - enrolledAt: convertFn(enrolledAt, dataElementTypes.DATE), - program: programId, - orgUnit: orgUnitId, - attributes: deriveAttributesFromFormValues(formServerValues), - status: 'ACTIVE', - events: allEventsToBeCreated, - }, - ], - orgUnit: orgUnitId, - trackedEntityType: trackedEntityType.id, - ...(trackedEntity && { trackedEntity }), - }, + enrollmentPayload, ], }, redirectTo, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js index a38c30e4e5..e36a6e10e6 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveAutoGenerateEvents.js @@ -24,7 +24,7 @@ export const deriveAutoGenerateEvents = ({ occurredAt: string, programId: string, orgUnitId: string, - firstStageMetadata: ProgramStage, + firstStageMetadata: ?ProgramStage, attributeCategoryOptions: { [categoryId: string]: string } | string, }) => { // in case we have a program that does not have an incident date (occurredAt), such as Malaria case diagnosis, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js index bf40f45135..2e4f952f92 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/deriveFirstStageDuringRegistrationEvent.js @@ -16,7 +16,7 @@ export const deriveFirstStageDuringRegistrationEvent = ({ fieldsValue, attributeCategoryOptions, }: { - firstStageMetadata: ProgramStage, + firstStageMetadata: ?ProgramStage, programId: string, orgUnitId: string, currentEventValues?: { [id: string]: any }, diff --git a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js index 26bd03daad..23d9d1a610 100644 --- a/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js +++ b/src/core_modules/capture-core/components/Pages/New/RegistrationDataEntry/helpers/getStageWithOpenAfterEnrollment.js @@ -12,7 +12,7 @@ export const PAGES = { // when the event will not be created redirect to enrollmentEventNew export const getStageWithOpenAfterEnrollment = ( stages: Map, - firstStageMetadata: ProgramStage, + useFirstStageDuringRegistration: boolean, shouldRedirect: boolean, ) => { const stagesArray = [...stages.values()]; @@ -22,8 +22,8 @@ export const getStageWithOpenAfterEnrollment = ( if (shouldRedirect && firstStageWithOpenAfterEnrollment) { // event will be created during first stage registration if ( - firstStageMetadata && - firstStageMetadata.id === firstStageWithOpenAfterEnrollment.id + useFirstStageDuringRegistration + && stagesArray[0].id === firstStageWithOpenAfterEnrollment.id ) { return PAGES.enrollmentEventEdit; } diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js index 0de7c46415..19d78a8c31 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js @@ -6,6 +6,7 @@ import { DATA_ENTRY_ID } from '../../registerTei.const'; import enrollmentClasses from './enrollment.module.css'; import { EnrollmentRegistrationEntry } from '../../../../../DataEntries'; import type { Props } from './dataEntryEnrollment.types'; +import { useLocationQuery } from '../../../../../../utils/routing'; const NewEnrollmentRelationshipPlain = ({ @@ -17,13 +18,15 @@ const NewEnrollmentRelationshipPlain = renderDuplicatesCardActions, ExistingUniqueValueDialogActions, }: Props) => { + const { orgUnitId, teiId } = useLocationQuery(); const fieldOptions = { theme, fieldLabelMediaBasedClass: enrollmentClasses.fieldLabelMediaBased }; - return ( i18n.t('Save new {{trackedEntityTypeName}} and link', { trackedEntityTypeName, diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.component.js index a7f1b1bea7..2f04955d2a 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.component.js @@ -6,38 +6,41 @@ import { DATA_ENTRY_ID } from '../../registerTei.const'; import teiClasses from './trackedEntityInstance.module.css'; import { TeiRegistrationEntry } from '../../../../../DataEntries'; import type { Props } from './dataEntryTrackedEntityInstance.types'; +import { useCurrentOrgUnitId } from '../../../../../../hooks/useCurrentOrgUnitId'; const RelationshipTrackedEntityInstancePlain = - ({ - theme, - onSave, - teiRegistrationMetadata = {}, - duplicatesReviewPageSize, - renderDuplicatesDialogActions, - renderDuplicatesCardActions, - ExistingUniqueValueDialogActions, - }: Props) => { - const fieldOptions = { theme, fieldLabelMediaBasedClass: teiClasses.fieldLabelMediaBased }; - const { trackedEntityType } = teiRegistrationMetadata || {}; - const trackedEntityTypeNameLC = trackedEntityType.name.toLocaleLowerCase(); + ({ + theme, + onSave, + teiRegistrationMetadata = {}, + duplicatesReviewPageSize, + renderDuplicatesDialogActions, + renderDuplicatesCardActions, + ExistingUniqueValueDialogActions, + }: Props) => { + const orgUnitId = useCurrentOrgUnitId(); + const fieldOptions = { theme, fieldLabelMediaBasedClass: teiClasses.fieldLabelMediaBased }; + const { trackedEntityType } = teiRegistrationMetadata || {}; + const trackedEntityTypeNameLC = trackedEntityType.name.toLocaleLowerCase(); - return ( - // $FlowFixMe - flow error will be resolved when rewriting relationship metadata fetching - - ); - }; + return ( + // $FlowFixMe - flow error will be resolved when rewriting relationship metadata fetching + + ); + }; export const RelationshipTrackedEntityInstance = withTheme()(RelationshipTrackedEntityInstancePlain); diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js index a3ac0f0ae5..20f5463e75 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js @@ -3,16 +3,18 @@ import type { Node } from 'react'; import type { TeiRegistration } from '../../../../../../metaData'; import type { RenderCustomCardActions } from '../../../../../CardList'; import type { - SaveForEnrollmentAndTeiRegistration, ExistingUniqueValueDialogActionsComponent, } from '../../../../../DataEntries'; +import type { + TeiPayload, +} from '../../../../common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types'; export type Props = {| theme: Theme, - onSave: SaveForEnrollmentAndTeiRegistration, + onSave: (TeiPayload) => void, teiRegistrationMetadata?: TeiRegistration, duplicatesReviewPageSize: number, renderDuplicatesCardActions?: RenderCustomCardActions, - renderDuplicatesDialogActions?: (onCancel: () => void, onSave: SaveForEnrollmentAndTeiRegistration) => Node, + renderDuplicatesDialogActions?: (onCancel: () => void, onSave: (TeiPayload) => void) => Node, ExistingUniqueValueDialogActions: ExistingUniqueValueDialogActionsComponent, |}; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.container.js b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.container.js index 991425a334..cf9de6695b 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.container.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/RegisterTei/RegisterTei.container.js @@ -34,6 +34,7 @@ export const RegisterTei = ({ onLink, onSave, onGetUnsavedAttributeValues }: Own trackedEntityName={trackedEntityName} newRelationshipProgramId={newRelationshipProgramId} error={error} - />); + /> + ); }; diff --git a/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js b/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js index b748347358..3cced06ccf 100644 --- a/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js +++ b/src/core_modules/capture-core/components/Pages/NewRelationship/TeiRelationship/SearchResults/TeiRelationshipSearchResults.component.js @@ -13,6 +13,7 @@ import { SearchResultsHeader } from '../../../../SearchResultsHeader'; import { type SearchGroup } from '../../../../../metaData'; import { ResultsPageSizeContext } from '../../../shared-contexts'; import type { ListItem } from '../../../../CardList/CardList.types'; +import { convertClientValuesToServer } from '../../../../../converters/helpers/clientToServer'; const SearchResultsPager = withNavigation()(Pagination); @@ -77,7 +78,8 @@ class TeiRelationshipSearchResultsPlain extends React.Component { } onAddRelationship = (item) => { - this.props.onAddRelationship(item.id, item.values); + const serverValues = convertClientValuesToServer(item.values, this.props.searchGroup.searchForm); + this.props.onAddRelationship(item.id, serverValues); } renderResults = () => { 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 4416248dcf..31da7bdbd4 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 @@ -11,7 +11,7 @@ import { ViewEventSectionHeader } from '../Section/ViewEventSectionHeader.compon import { EditEventDataEntry } from '../../../WidgetEventEdit/EditEventDataEntry/EditEventDataEntry.container'; import { ViewEventDataEntry } from '../../../WidgetEventEdit/ViewEventDataEntry/ViewEventDataEntry.container'; import type { ProgramStage } from '../../../../metaData'; -import { useRulesEngineOrgUnit } from '../../../../hooks/useRulesEngineOrgUnit'; +import { useCoreOrgUnit } from '../../../../metadataRetrieval/coreOrgUnit'; import { NoticeBox } from '../../../NoticeBox'; const getStyles = () => ({ @@ -60,7 +60,7 @@ const EventDetailsSectionPlain = (props: Props) => { eventAccess, ...passOnProps } = props; const orgUnitId = useSelector(({ viewEventPage }) => viewEventPage.loadedValues?.orgUnit?.id); - const { orgUnit, error } = useRulesEngineOrgUnit(orgUnitId); + const { orgUnit, error } = useCoreOrgUnit(orgUnitId); if (error) { return error.errorComponent; diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/epics/viewEvent.epics.js b/src/core_modules/capture-core/components/Pages/ViewEvent/epics/viewEvent.epics.js index 91c84cced6..b39483080b 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/epics/viewEvent.epics.js +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/epics/viewEvent.epics.js @@ -4,8 +4,7 @@ import { ofType } from 'redux-observable'; import { map, switchMap } from 'rxjs/operators'; import i18n from '@dhis2/d2-i18n'; import { errorCreator } from 'capture-core-utils'; -import { getRulesEngineOrgUnit } from 'capture-core/rules/getRulesEngineOrgUnit'; -import { getAssociatedOrgUnitGroups } from 'capture-core/MetaDataStoreUtils/getAssociatedOrgUnitGroups'; +import { getCoreOrgUnit } from 'capture-core/metadataRetrieval/coreOrgUnit'; import { isSelectionsEqual } from '../../../App/isSelectionsEqual'; import { getErrorMessageAndDetails } from '../../../../utils/errors/getErrorMessageAndDetails'; @@ -39,18 +38,24 @@ export const getEventOpeningFromEventListEpic = ( action$.pipe( ofType(eventWorkingListsActionTypes.VIEW_EVENT_PAGE_OPEN), switchMap(({ payload: { eventId } }) => getEvent(eventId, absoluteApiPath, querySingleResource) - .then(eventContainer => - (eventContainer - ? Promise.all( - [eventContainer, getRulesEngineOrgUnit(eventContainer.event.orgUnitId, querySingleResource)], - ) - : [])) - .then(([eventContainer, orgUnit]) => { + .then((eventContainer) => { if (!eventContainer) { return openViewEventPageFailed( i18n.t('Event could not be loaded. Are you sure it exists?')); } - return startOpenEventForView(eventContainer, orgUnit); + return getCoreOrgUnit({ + orgUnitId: eventContainer.event.orgUnitId, + onSuccess: orgUnit => startOpenEventForView(eventContainer, orgUnit), + onError: (error) => { + const { message, details } = getErrorMessageAndDetails(error); + log.error( + errorCreator( + message || + i18n.t('Organisation unit could not be loaded'))(details)); + return openViewEventPageFailed( + i18n.t('Could not get organisation unit')); + }, + }); }) .catch((error) => { const { message, details } = getErrorMessageAndDetails(error); @@ -95,28 +100,22 @@ export const getEventFromUrlEpic = ( }); })); -export const getOrgUnitOnUrlUpdateEpic = (action$: InputObservable, _: ReduxStore, { querySingleResource }: ApiUtils) => +export const getOrgUnitOnUrlUpdateEpic = (action$: InputObservable) => action$.pipe( ofType(viewEventActionTypes.EVENT_FROM_URL_RETRIEVED), - switchMap((action) => { + map((action) => { const eventContainer = action.payload.eventContainer; - // change from organisationUnitGroups -> groups - return Promise.all( - [ - querySingleResource({ resource: `organisationUnits/${eventContainer.event.orgUnitId}` }), - getAssociatedOrgUnitGroups(eventContainer.event.orgUnitId), - ]) - .then(([orgUnit, groups]) => { - orgUnit.groups = groups; - return orgUnitRetrievedOnUrlUpdate(orgUnit, eventContainer); - }) - .catch((error) => { + return getCoreOrgUnit({ + orgUnitId: eventContainer.event.orgUnitId, + onSuccess: orgUnit => orgUnitRetrievedOnUrlUpdate(orgUnit, eventContainer), + onError: (error) => { const { message, details } = getErrorMessageAndDetails(error); log.error(errorCreator( message || i18n.t('Organisation unit could not be loaded'))(details)); return orgUnitCouldNotBeRetrievedOnUrlUpdate(eventContainer); - }); + }, + }); })); export const openViewPageLocationChangeEpic = (action$: InputObservable, _: ReduxStore, { history }: ApiUtils) => diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useCommonEnrollmentDomainData/useCommonEnrollmentDomainData.types.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useCommonEnrollmentDomainData/useCommonEnrollmentDomainData.types.js index fb48b2a555..7ca431aa31 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useCommonEnrollmentDomainData/useCommonEnrollmentDomainData.types.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useCommonEnrollmentDomainData/useCommonEnrollmentDomainData.types.js @@ -1,5 +1,4 @@ // @flow - export type DataValue = { dataElement: string, value: string, @@ -15,7 +14,6 @@ export type Event = {| occurredAt: string, updatedAt: string, orgUnit: string, - orgUnitName: string, program: string, programStage: string, status: 'ACTIVE' | 'VISITED' | 'COMPLETED' | 'SCHEDULE' | 'OVERDUE' | 'SKIPPED', @@ -35,7 +33,6 @@ export type EnrollmentData = {| updatedAt: string, updatedAtClient: string, orgUnit: string, - orgUnitName: string, program: string, status: string, storedBy: string, @@ -49,6 +46,7 @@ export type AttributeValue = {| value: string, |}; + export type Output = {| error?: any, enrollment?: EnrollmentData, diff --git a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useRuleEffects/useRuleEffects.js b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useRuleEffects/useRuleEffects.js index 74f6a96735..d8ff56e1b7 100644 --- a/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useRuleEffects/useRuleEffects.js +++ b/src/core_modules/capture-core/components/Pages/common/EnrollmentOverviewDomain/useRuleEffects/useRuleEffects.js @@ -33,7 +33,6 @@ const useEventsData = (enrollment, program) => { programId: event.program, programStageId: event.programStage, orgUnitId: event.orgUnit, - orgUnitName: event.orgUnitName, trackedEntityInstanceId: event.trackedEntityInstance, enrollmentId: event.enrollment, enrollmentStatus: event.enrollmentStatus, diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js new file mode 100644 index 0000000000..2dbf0c7a20 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.component.js @@ -0,0 +1,42 @@ +// @flow +import React from 'react'; +import i18n from '@dhis2/d2-i18n'; +import { withTheme } from '@material-ui/core/styles'; +import { DATA_ENTRY_ID } from '../../registerTei.const'; +import enrollmentClasses from './enrollment.module.css'; +import { EnrollmentRegistrationEntry } from '../../../../../../DataEntries'; +import type { Props } from './dataEntryEnrollment.types'; + +const NewEnrollmentRelationshipPlain = + ({ + theme, + onSave, + programId, + orgUnitId, + duplicatesReviewPageSize, + renderDuplicatesDialogActions, + renderDuplicatesCardActions, + ExistingUniqueValueDialogActions, + }: Props) => { + const fieldOptions = { theme, fieldLabelMediaBasedClass: enrollmentClasses.fieldLabelMediaBased }; + + return ( + i18n.t('Save new {{trackedEntityTypeName}} and link', { + trackedEntityTypeName, + interpolation: { escapeValue: false }, + })} + onSave={onSave} + duplicatesReviewPageSize={duplicatesReviewPageSize} + renderDuplicatesDialogActions={renderDuplicatesDialogActions} + renderDuplicatesCardActions={renderDuplicatesCardActions} + ExistingUniqueValueDialogActions={ExistingUniqueValueDialogActions} + /> + ); + }; + +export const NewEnrollmentRelationship = withTheme()(NewEnrollmentRelationshipPlain); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.container.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.container.js new file mode 100644 index 0000000000..507e09e61b --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/DataEntryEnrollment.container.js @@ -0,0 +1,23 @@ +// @flow +import { connect } from 'react-redux'; +import { makeEnrollmentMetadataSelector } from './enrollment.selectors'; +import { NewEnrollmentRelationship } from './DataEntryEnrollment.component'; + +const makeMapStateToProps = () => { + const enrollmentMetadataSelector = makeEnrollmentMetadataSelector(); + + const mapStateToProps = (state: ReduxState) => { + const enrollmentMetadata = enrollmentMetadataSelector(state); + + return { + enrollmentMetadata, + programId: state.newRelationshipRegisterTei.programId, + orgUnitId: state.newRelationshipRegisterTei.orgUnit.id, + }; + }; + // $FlowFixMe[not-an-object] automated comment + return mapStateToProps; +}; + +// $FlowFixMe +export const DataEntryEnrollment = connect(makeMapStateToProps, () => ({}))(NewEnrollmentRelationship); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/dataEntryEnrollment.types.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/dataEntryEnrollment.types.js new file mode 100644 index 0000000000..de3f6cee6f --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/dataEntryEnrollment.types.js @@ -0,0 +1,20 @@ +// @flow +import type { Node } from 'react'; +import type { Enrollment } from '../../../../../../../metaData'; +import type { RenderCustomCardActions } from '../../../../../../CardList'; +import type { + SaveForEnrollmentAndTeiRegistration, + ExistingUniqueValueDialogActionsComponent, +} from '../../../../../../DataEntries'; + +export type Props = {| + theme: Theme, + programId: string, + orgUnitId: string, + enrollmentMetadata?: Enrollment, + onSave: SaveForEnrollmentAndTeiRegistration, + duplicatesReviewPageSize: number, + renderDuplicatesCardActions?: RenderCustomCardActions, + renderDuplicatesDialogActions?: (onCancel: () => void, onSave: SaveForEnrollmentAndTeiRegistration) => Node, + ExistingUniqueValueDialogActions: ExistingUniqueValueDialogActionsComponent, +|}; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/enrollment.module.css b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/enrollment.module.css new file mode 100644 index 0000000000..953ddeae59 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/enrollment.module.css @@ -0,0 +1,11 @@ +@media screen and (max-width: 811px) and (min-width: 564px) { + .fieldLabelMediaBased { + padding-top: 0px !important; + } +} + +@media screen and (max-width: 451px) { + .fieldLabelMediaBased { + padding-top: 0px !important; + } +} diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/enrollment.selectors.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/enrollment.selectors.js new file mode 100644 index 0000000000..fd9331f6a6 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/enrollment.selectors.js @@ -0,0 +1,23 @@ +// @flow +import { createSelector } from 'reselect'; +import type { TrackerProgram } from '../../../../../../../metaData'; +import { getProgramFromProgramIdThrowIfNotFound } from '../../../../../../../metaData'; + +const programIdSelector = state => state.newRelationshipRegisterTei.programId; + +// $FlowFixMe +export const makeEnrollmentMetadataSelector = () => createSelector( + programIdSelector, + (programId: string) => { + let program: TrackerProgram; + try { + // $FlowFixMe[incompatible-type] automated comment + program = getProgramFromProgramIdThrowIfNotFound(programId); + } catch (error) { + return null; + } + + // $FlowFixMe + return program.enrollment; + }, +); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/index.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/index.js new file mode 100644 index 0000000000..0ecadd4fab --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/Enrollment/index.js @@ -0,0 +1,2 @@ +// @flow +export { DataEntryEnrollment } from './DataEntryEnrollment.container'; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.actions.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.actions.js new file mode 100644 index 0000000000..8cb9a9b5fa --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.actions.js @@ -0,0 +1,14 @@ +// @flow +import { actionCreator } from '../../../../../../actions/actions.utils'; + +export const actionTypes = { + DATA_ENTRY_OPEN: 'NewRelationshipRegisterTeiDataEntryOpen', + DATA_ENTRY_OPEN_CANCELLED: 'NewRelationshopRegisterTeiDataEntryOpenCancelled', + DATA_ENTRY_OPEN_FAILED: 'NewRelationshopRegisterTeiDataEntryOpenFailed', +}; + +export const openDataEntry = () => + actionCreator(actionTypes.DATA_ENTRY_OPEN)(); + +export const openDataEntryFailed = (errorMessage: string) => + actionCreator(actionTypes.DATA_ENTRY_OPEN_FAILED)({ errorMessage }); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.component.js new file mode 100644 index 0000000000..292a1d1530 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.component.js @@ -0,0 +1,37 @@ +// @flow +import * as React from 'react'; +import { DataEntryEnrollment } from './Enrollment'; +import { DataEntryTrackedEntityInstance } from './TrackedEntityInstance'; + +type Props = { + showDataEntry: boolean, + programId: string, + onSaveWithoutEnrollment: () => void, + onSaveWithEnrollment: () => void, +}; + +export class RegisterTeiDataEntryComponent extends React.Component { + render() { + const { showDataEntry, programId, onSaveWithoutEnrollment, onSaveWithEnrollment, ...passOnProps } = this.props; + + if (!showDataEntry) { + return null; + } + + if (programId) { + return ( + + ); + } + + return ( + + ); + } +} diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.container.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.container.js new file mode 100644 index 0000000000..e5e7d50e76 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/RegisterTeiDataEntry.container.js @@ -0,0 +1,20 @@ +// @flow +import { compose } from 'redux'; +import { connect } from 'react-redux'; +import { RegisterTeiDataEntryComponent } from './RegisterTeiDataEntry.component'; +import { withErrorMessageHandler } from '../../../../../../HOC/withErrorMessageHandler'; + +const mapStateToProps = (state: ReduxState) => ({ + showDataEntry: state.newRelationshipRegisterTei.orgUnit, + error: state.newRelationshipRegisterTei.dataEntryError, + programId: state.newRelationshipRegisterTei.programId, +}); + +const mapDispatchToProps = () => ({}); + +export const RegisterTeiDataEntry = + compose( + // $FlowFixMe[missing-annot] automated comment + connect(mapStateToProps, mapDispatchToProps), + withErrorMessageHandler(), + )(RegisterTeiDataEntryComponent); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.js new file mode 100644 index 0000000000..c455e3616d --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/DataEntryTrackedEntityInstance.js @@ -0,0 +1,52 @@ +// @flow +import React from 'react'; +import i18n from '@dhis2/d2-i18n'; +import { withTheme } from '@material-ui/core'; +import { DATA_ENTRY_ID } from '../../registerTei.const'; +import teiClasses from './trackedEntityInstance.module.css'; +import { TeiRegistrationEntry } from '../../../../../../DataEntries'; +import type { Props } from './dataEntryTrackedEntityInstance.types'; +import { getTeiRegistrationMetadata } from './tei.selectors'; +import { useLocationQuery } from '../../../../../../../utils/routing'; + +const RelationshipTrackedEntityInstancePlain = + ({ + theme, + onSave, + trackedEntityTypeId, + duplicatesReviewPageSize, + renderDuplicatesDialogActions, + renderDuplicatesCardActions, + ExistingUniqueValueDialogActions, + }: Props) => { + const { orgUnitId } = useLocationQuery(); + const fieldOptions = { theme, fieldLabelMediaBasedClass: teiClasses.fieldLabelMediaBased }; + const teiRegistrationMetadata = getTeiRegistrationMetadata(trackedEntityTypeId); + const { trackedEntityType } = teiRegistrationMetadata || {}; + const trackedEntityTypeNameLC = trackedEntityType.name.toLocaleLowerCase(); + + if (!teiRegistrationMetadata && !teiRegistrationMetadata?.form) { + return null; + } + + return ( + // $FlowFixMe - flow error will be resolved when rewriting relationship metadata fetching + + ); + }; + +export const DataEntryTrackedEntityInstance = withTheme()(RelationshipTrackedEntityInstancePlain); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js new file mode 100644 index 0000000000..b4f9617052 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/dataEntryTrackedEntityInstance.types.js @@ -0,0 +1,30 @@ +// @flow +import type { Node } from 'react'; +import type { TeiRegistration } from '../../../../../../../metaData'; +import type { RenderCustomCardActions } from '../../../../../../CardList'; +import type { + ExistingUniqueValueDialogActionsComponent, +} from '../../../../../../DataEntries'; + +export type TeiPayload = {| + trackedEntity: string, + trackedEntityType: string, + enrollments: [], + orgUnit: string, + geometry: ?{ coordinates: any, type: any }, + attributes: Array<{| + attribute: string, + value: any, + |}>, +|} + +export type Props = {| + theme: Theme, + trackedEntityTypeId: string, + onSave: TeiPayload => void, + teiRegistrationMetadata?: TeiRegistration, + duplicatesReviewPageSize: number, + renderDuplicatesCardActions?: RenderCustomCardActions, + renderDuplicatesDialogActions?: (onCancel: () => void, onSave: (TeiPayload) => void) => Node, + ExistingUniqueValueDialogActions: ExistingUniqueValueDialogActionsComponent, +|}; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js new file mode 100644 index 0000000000..5bb8975389 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/index.js @@ -0,0 +1,2 @@ +// @flow +export { DataEntryTrackedEntityInstance } from './DataEntryTrackedEntityInstance'; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/tei.selectors.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/tei.selectors.js new file mode 100644 index 0000000000..2363e24584 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/tei.selectors.js @@ -0,0 +1,18 @@ +// @flow +import log from 'loglevel'; +import { errorCreator } from 'capture-core-utils'; +import type { TrackedEntityType } from '../../../../../../../metaData'; +import { getTrackedEntityTypeThrowIfNotFound } from '../../../../../../../metaData'; + +// $FlowFixMe +export const getTeiRegistrationMetadata = (TETypeId: string) => { + let TEType: TrackedEntityType; + try { + TEType = getTrackedEntityTypeThrowIfNotFound(TETypeId); + } catch (error) { + log.error(errorCreator('Could not get TrackedEntityType for id')({ TETypeId })); + return null; + } + + return TEType.teiRegistration; +}; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/trackedEntityInstance.module.css b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/trackedEntityInstance.module.css new file mode 100644 index 0000000000..8d899a1886 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/DataEntry/TrackedEntityInstance/trackedEntityInstance.module.css @@ -0,0 +1,11 @@ +@media screen and (max-width: 811px) and (min-width: 564px) { + .fieldLabelMediaBased { + padding-top: 0px !important; + } +} + +@media screen and (max-width: 451px) { + .fieldLabelMediaBased { + padding-top: 0px !important; + } +} \ No newline at end of file diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.component.js new file mode 100644 index 0000000000..9a9a7070af --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.component.js @@ -0,0 +1,130 @@ +// @flow +import React, { type ComponentType, useContext, useCallback } from 'react'; +import { compose } from 'redux'; +import { withStyles } from '@material-ui/core/styles'; +import i18n from '@dhis2/d2-i18n'; +import { Button } from '@dhis2/ui'; +import { RegisterTeiDataEntry } from './DataEntry/RegisterTeiDataEntry.container'; +import { RegistrationSection } from './RegistrationSection'; +import { DataEntryWidgetOutput } from '../../../../DataEntryWidgetOutput/DataEntryWidgetOutput.container'; +import { ResultsPageSizeContext } from '../../../shared-contexts'; +import type { ComponentProps } from './RegisterTei.types'; +import { withErrorMessageHandler } from '../../../../../HOC'; + +const getStyles = () => ({ + container: { + display: 'flex', + flexWrap: 'wrap', + }, + leftContainer: { + flexGrow: 10, + flexBasis: 0, + margin: 8, + }, +}); + +const CardListButton = (({ teiId, values, handleOnClick }) => ( + +)); + +const DialogButtons = ({ onCancel, onSave, trackedEntityName }) => ( + <> + +
+ +
+ +); + +const RegisterTeiPlain = ({ + dataEntryId, + onLink, + onSaveWithoutEnrollment, + onSaveWithEnrollment, + onGetUnsavedAttributeValues, + trackedEntityName, + trackedEntityTypeId, + selectedScopeId, + classes, +}: ComponentProps) => { + const { resultsPageSize } = useContext(ResultsPageSizeContext); + + const renderDuplicatesCardActions = useCallback(({ item }) => ( + + ), [onLink]); + + const renderDuplicatesDialogActions = useCallback((onCancel, onSaveArgument) => ( + + ), [trackedEntityName]); + + const ExistingUniqueValueDialogActions = useCallback(({ teiId, attributeValues }) => ( + + ), [onLink]); + + return ( +
+
+ + +
+ + + } + /> +
+ ); +}; + +export const RegisterTeiComponent: ComponentType<$Diff> = + compose( + withErrorMessageHandler(), + withStyles(getStyles), + )(RegisterTeiPlain); diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.container.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.container.js new file mode 100644 index 0000000000..773d0c2778 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.container.js @@ -0,0 +1,34 @@ +// @flow +import React from 'react'; +import { useSelector } from 'react-redux'; +import { RegisterTeiComponent } from './RegisterTei.component'; +import type { ContainerProps } from './RegisterTei.types'; +import { useScopeInfo } from '../../../../../hooks'; + +export const RegisterTei = ({ + onLink, + onSave, + onGetUnsavedAttributeValues, + trackedEntityTypeId, + suggestedProgramId, +}: ContainerProps) => { + const dataEntryId = 'relationship'; + const error = useSelector(({ newRelationshipRegisterTei }) => (newRelationshipRegisterTei.error)); + const selectedScopeId = suggestedProgramId || trackedEntityTypeId; + const { trackedEntityName } = useScopeInfo(selectedScopeId); + + return ( + + ); +}; + diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.types.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.types.js new file mode 100644 index 0000000000..0de2b570ba --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegisterTei.types.js @@ -0,0 +1,23 @@ +// @flow +export type SharedProps = {| + onLink: (teiId: string, values: Object) => void, + onGetUnsavedAttributeValues?: ?Function, + trackedEntityTypeId: string, +|}; + +export type ContainerProps = {| + suggestedProgramId: string, + onSave: (teiPayload: Object) => void, + ...SharedProps, +|}; + +export type ComponentProps = {| + selectedScopeId: string, + error: string, + dataEntryId: string, + trackedEntityName: ?string, + onSaveWithEnrollment: () => void, + onSaveWithoutEnrollment: () => void, + ...SharedProps, + ...CssClasses, +|}; diff --git a/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/ProgramSelector/ComposedProgramSelector.component.js b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/ProgramSelector/ComposedProgramSelector.component.js new file mode 100644 index 0000000000..f815b36498 --- /dev/null +++ b/src/core_modules/capture-core/components/Pages/common/TEIRelationshipsWidget/RegisterTei/RegistrationSection/ProgramSelector/ComposedProgramSelector.component.js @@ -0,0 +1,157 @@ +// @flow +import * as React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import i18n from '@dhis2/d2-i18n'; +import { LinkButton } from '../../../../../../Buttons/LinkButton.component'; +import { ProgramFilterer } from '../../../../../../ProgramFilterer'; +import type { Program } from '../../../../../../../metaData'; +import { TrackerProgram } from '../../../../../../../metaData'; +import { + VirtualizedSelectField, + withSelectTranslations, + withFocusSaver, + withDefaultFieldContainer, + withLabel, + withFilterProps, +} from '../../../../../../FormFields/New'; +import { NonBundledDhis2Icon } from '../../../../../../NonBundledDhis2Icon'; + +const getStyles = (theme: Theme) => ({ + iconContainer: { + display: 'flex', + alignItems: 'center', + paddingRight: 5, + }, + icon: { + width: 22, + height: 22, + borderRadius: 2, + }, + isFilteredContainer: { + fontSize: 12, + color: theme.palette.grey.dark, + paddingTop: 5, + }, + isFilteredLink: { + paddingLeft: 2, + backgroundColor: 'inherit', + }, +}); + +type Option = { + label: string, + value: string, + iconLeft?: ?React.Node, +}; + +type Props = { + orgUnitIds: ?Array, + value: string, + trackedEntityTypeId: string, + classes: Object, + onUpdateSelectedProgram: (programId: string) => void, + onClearFilter: () => void, +}; + +class ProgramSelector extends React.Component { + baseLineFilter = (program: Program) => { + const { trackedEntityTypeId } = this.props; + + const isValid = program instanceof TrackerProgram && + program.trackedEntityType.id === trackedEntityTypeId && + program.access.data.write; + + return isValid; + } + + getOptionsFromPrograms = (programs: Array): Array