From 1b4d3d381795e904b02f81ec14406e96c8cdd84d Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Sat, 10 Aug 2024 12:42:20 +0200 Subject: [PATCH 1/5] feat: stages&events list --- i18n/en.pot | 13 ++-- .../Stages/Stage/Stage.component.js | 29 ++++---- .../StageCreateNewButton.js | 72 +++++++++++++++++++ .../Stage/StageCreateNewButton/index.js | 3 + .../StageDetail/StageDetail.component.js | 44 +++--------- 5 files changed, 107 insertions(+), 54 deletions(-) create mode 100644 src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js create mode 100644 src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/index.js diff --git a/i18n/en.pot b/i18n/en.pot index 53ce989a6f..81ca45b48f 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-08-08T11:49:13.423Z\n" -"PO-Revision-Date: 2024-08-08T11:49:13.423Z\n" +"POT-Creation-Date: 2024-08-10T10:42:21.141Z\n" +"PO-Revision-Date: 2024-08-10T10:42:21.141Z\n" msgid "Choose one or more dates..." msgstr "Choose one or more dates..." @@ -1499,6 +1499,12 @@ msgstr "Report date" msgid "Please select a valid event" msgstr "Please select a valid event" +msgid "You do not have access to create events in this stage" +msgstr "You do not have access to create events in this stage" + +msgid "This stage can only have one event" +msgstr "This stage can only have one event" + msgid "New {{ eventName }} event" msgstr "New {{ eventName }} event" @@ -1514,9 +1520,6 @@ msgstr "Reset list" msgid "Go to full {{ eventName }}" msgstr "Go to full {{ eventName }}" -msgid "This stage can only have one event" -msgstr "This stage can only have one event" - msgid "Events could not be retrieved. Please try again later." msgstr "Events could not be retrieved. Please try again later." diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js index d76690c7eb..46c7c7ae31 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js @@ -1,13 +1,13 @@ // @flow import React, { type ComponentType, useState, useCallback } from 'react'; import cx from 'classnames'; -import i18n from '@dhis2/d2-i18n'; import { withStyles } from '@material-ui/core'; -import { spacersNum, colors, IconAdd16, Button } from '@dhis2/ui'; +import { spacersNum, colors } from '@dhis2/ui'; import { StageOverview } from './StageOverview'; import type { Props } from './stage.types'; import { Widget } from '../../../Widget'; import { StageDetail } from './StageDetail/StageDetail.component'; +import { StageCreateNewButton } from './StageCreateNewButton'; const styles = { overview: { @@ -15,7 +15,7 @@ const styles = { marginRight: spacersNum.dp16, borderTop: `1px solid ${colors.grey300}`, }, - button: { + buttonContainer: { margin: `0 ${spacersNum.dp16}px ${spacersNum.dp16}px ${spacersNum.dp16}px`, }, buttonRow: { @@ -67,19 +67,16 @@ export const StagePlain = ({ stage, events, classes, className, onCreateNew, rul hiddenProgramStage={preventAddingNewEvents} {...passOnProps} /> : ( - +
+ +
)} diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js new file mode 100644 index 0000000000..497f8bca84 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js @@ -0,0 +1,72 @@ +// @flow +import React, { useMemo } from 'react'; +import { Button, IconAdd16 } from '@dhis2/ui'; +import i18n from '@dhis2/d2-i18n'; +import { ConditionalTooltip } from '../../../../Tooltips/ConditionalTooltip'; + +type Props = { + onCreateNew: (stageId: string) => void, + stageWriteAccess: ?boolean, + eventCount: number, + repeatable: ?boolean, + preventAddingEventActionInEffect: ?boolean, + eventName: string, +} + +export const StageCreateNewButton = ({ + onCreateNew, + stageWriteAccess, + eventCount, + repeatable, + preventAddingEventActionInEffect, + eventName, +}: Props) => { + const isDisabled = useMemo(() => { + if (!stageWriteAccess) { + return true; + } + if (!repeatable && eventCount > 0) { + return true; + } + return !!preventAddingEventActionInEffect; + }, [eventCount, preventAddingEventActionInEffect, repeatable, stageWriteAccess]); + const tooltipContent = useMemo(() => { + if (!stageWriteAccess) { + return i18n.t('You do not have access to create events in this stage', { + programStageName: eventName, + interpolation: { escapeValue: false }, + }); + } + if (preventAddingEventActionInEffect) { + return i18n.t("You can't add any more {{ programStageName }} events", { + programStageName: eventName, + interpolation: { escapeValue: false }, + }); + } + if (!repeatable && eventCount > 0) { + return i18n.t('This stage can only have one event'); + } + return ''; + }, [eventCount, eventName, preventAddingEventActionInEffect, repeatable, stageWriteAccess]); + + return ( + + + + ); +}; diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/index.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/index.js new file mode 100644 index 0000000000..d5cb9dcad9 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/index.js @@ -0,0 +1,3 @@ +// @flow + +export { StageCreateNewButton } from './StageCreateNewButton'; diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js index 055befd20d..644936ef57 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/StageDetail.component.js @@ -13,11 +13,10 @@ import { colors, DataTableCell, DataTableColumnHeader, Button, - IconAdd16, Tooltip, } from '@dhis2/ui'; -import { ConditionalTooltip } from 'capture-core/components/Tooltips/ConditionalTooltip'; import { sortDataFromEvent } from './hooks/sortFuntions'; +import { StageCreateNewButton } from '../StageCreateNewButton'; import { useComputeDataFromEvent, useComputeHeaderColumn, formatRowForView } from './hooks/useEventList'; import { DEFAULT_NUMBER_OF_ROW, SORT_DIRECTION } from './hooks/constants'; import { getProgramAndStageForProgram } from '../../../../../metaData/helpers'; @@ -218,37 +217,16 @@ const StageDetailPlain = (props: Props) => { onClick={handleViewAll} >{i18n.t('Go to full {{ eventName }}', { eventName, interpolation: { escapeValue: false } })} : null); - const renderCreateNewButton = () => { - const shouldDisableCreateNew = (!repeatable && events.length > 0) || hiddenProgramStage; - - const tooltipContent = hiddenProgramStage - ? i18n.t("You can't add any more {{ programStageName }} events", { - programStageName: eventName, - interpolation: { escapeValue: false }, - }) - : i18n.t('This stage can only have one event'); - - return ( - - - - ); - }; + const renderCreateNewButton = () => ( + + ); return ( From 92560b74c56b73cab3968cd0b9ac67fbe4015d9e Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Sat, 10 Aug 2024 14:00:41 +0200 Subject: [PATCH 2/5] feat: quick actions & stage selector --- .../EnrollmentQuickActions.component.js | 43 ++++++++++--------- .../ProgramStageSelector.component.js | 2 +- .../ProgramStageSelector.container.js | 1 + 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentQuickActions/EnrollmentQuickActions.component.js b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentQuickActions/EnrollmentQuickActions.component.js index e5621d7449..55dabda823 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentQuickActions/EnrollmentQuickActions.component.js +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentQuickActions/EnrollmentQuickActions.component.js @@ -42,6 +42,7 @@ const EnrollmentQuickActionsComponent = ({ stages, events, ruleEffects, classes () => stagesWithEventCount.every( programStage => + (!programStage.dataAccess.write) || (!programStage.repeatable && programStage.eventCount > 0) || hiddenProgramStageRuleEffects?.find(ruleEffect => ruleEffect.id === programStage.id), ), @@ -61,27 +62,28 @@ const EnrollmentQuickActionsComponent = ({ stages, events, ruleEffects, classes onClose={() => setOpen(false)} onOpen={() => setOpen(true)} > - {ready &&
- } - label={i18n.t('New Event')} - onClickAction={() => onNavigationFromQuickActions(tabMode.REPORT)} - dataTest={'quick-action-button-report'} - disable={noStageAvailable} - /> + {ready && ( +
+ } + label={i18n.t('New Event')} + onClickAction={() => onNavigationFromQuickActions(tabMode.REPORT)} + dataTest={'quick-action-button-report'} + disable={noStageAvailable} + /> - } - label={i18n.t('Schedule an event')} - onClickAction={() => onNavigationFromQuickActions(tabMode.SCHEDULE)} - dataTest={'quick-action-button-schedule'} - disable={noStageAvailable} - /> + } + label={i18n.t('Schedule an event')} + onClickAction={() => onNavigationFromQuickActions(tabMode.SCHEDULE)} + dataTest={'quick-action-button-schedule'} + disable={noStageAvailable} + /> - {/* DHIS2-13016: Should hide Make referral until the feature is developped + {/* DHIS2-13016: Should hide Make referral until the feature is developped } label={i18n.t('Make referral')} @@ -89,7 +91,8 @@ const EnrollmentQuickActionsComponent = ({ stages, events, ruleEffects, classes dataTest={'quick-action-button-refer'} disable={noStageAvailable} /> */} -
} +
+ )} ); }; diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.component.js b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.component.js index 01967b1f21..1ab8895d04 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.component.js +++ b/src/core_modules/capture-core/components/Pages/EnrollmentAddEvent/ProgramStageSelector/ProgramStageSelector.component.js @@ -27,7 +27,7 @@ const ProgramStageSelectorComponentPlain = ({ programStages, onSelectProgramStag
{programStages.map((programStage) => { const disableStage = - (!programStage.repeatable && programStage.eventCount > 0) || programStage.hiddenProgramStage; + !programStage.dataAccess.write || (!programStage.repeatable && programStage.eventCount > 0) || programStage.hiddenProgramStage; return (
!programLoading && program?.programStages?.reduce((accStage, currentStage) => { accStage.push({ id: currentStage.id, + dataAccess: currentStage.access.data, eventCount: (enrollment?.events ?.filter(event => event.programStage === currentStage.id) ?.length From a2697d01493b7575c2f250acdac1680aa9cfab28 Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Sun, 11 Aug 2024 20:00:00 +0200 Subject: [PATCH 3/5] fix: tests --- .../WidgetStagesAndEvents/Stages/Stage/Stage.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js index 46c7c7ae31..6013697b8c 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/Stage.component.js @@ -69,7 +69,7 @@ export const StagePlain = ({ stage, events, classes, className, onCreateNew, rul /> : (
onCreateNew(id)} stageWriteAccess={stage.dataAccess.write} eventCount={events.length} repeatable={repeatable} From 8c771d048e8b07a9b85eecb4443a6b7e9f83e59c Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Tue, 13 Aug 2024 10:39:10 +0200 Subject: [PATCH 4/5] chore: add cypress tests --- .../ProgramStageSelector.feature | 5 +++ .../ProgramStageSelector.js | 4 ++ .../EnrollmentQuickActions.feature | 5 +++ .../EnrollmentQuickActions.js | 6 +++ .../StagesAndEventsWidget.feature | 6 +++ .../StageCreateNewButton.js | 41 ++++++++++--------- 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.feature b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.feature index fa7b797041..6bbb53cf33 100644 --- a/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.feature +++ b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.feature @@ -8,3 +8,8 @@ Feature: User is able to select program stage when navigating to EnrollmentEvent Scenario: The stage-button should be disabled when non-repeatable & event > 0 Given you land on the EnrollmentEventNew-page without a stageId Then the stage-button should be disabled + + @user:trackerAutoTestRestricted + Scenario: The stage-button should be disabled when no data write access + Given you open the enrollment page by typing #enrollmentEventNew?enrollmentId=WKPoiZxZxNG&orgUnitId=DiszpKrYNg8&programId=WSGAb5XwJ3Y&teiId=PgmUFEQYZdt + Then the stage-button should be disabled diff --git a/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.js b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.js index 6c8eac1dac..4fb525e343 100644 --- a/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.js +++ b/cypress/e2e/EnrollmentAddEventPage/ProgramStageSelector/ProgramStageSelector.js @@ -1,5 +1,9 @@ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; +Given(/^you open the enrollment page by typing (.*)$/, url => + cy.visit(url), +); + 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/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.feature b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.feature index c010914d88..ea20b12fea 100644 --- a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.feature +++ b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.feature @@ -13,3 +13,8 @@ Feature: User interacts with the quick actions-menu When you click the schedule event-button Then you should be navigated to the schedule tab + @user:trackerAutoTestRestricted + Scenario: The create new quick actions button should be disabled if no available stages + Given you open the enrollment page by typing #enrollment?enrollmentId=WKPoiZxZxNG&orgUnitId=DiszpKrYNg8&programId=WSGAb5XwJ3Y&teiId=PgmUFEQYZdt + Then the quick action buttons should be disabled + diff --git a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js index 23ff019cf3..b750047cac 100644 --- a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js +++ b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js @@ -31,3 +31,9 @@ Then('the buttons should be disabled', () => { .should('be.disabled'); }); }); + +Then('the quick action buttons should be disabled', () => { + cy.get('[data-test="quick-action-button-container"]') + .find('button') + .should('be.disabled'); +}); diff --git a/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature index e21de7e069..012bf45a27 100644 --- a/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature +++ b/cypress/e2e/EnrollmentPage/StagesAndEventsWidget/StagesAndEventsWidget.feature @@ -1,5 +1,10 @@ Feature: User interacts with Stages and Events Widget + @user:trackerAutoTestRestricted + Scenario: Create new event button is disabled if no data write access + Given you open the enrollment page by typing #enrollment?enrollmentId=WKPoiZxZxNG&orgUnitId=DiszpKrYNg8&programId=WSGAb5XwJ3Y&teiId=PgmUFEQYZdt + Then you should see the disabled button New Previous deliveries event + Scenario: User can view program stages Given you open the enrollment page Then the program stages should be displayed @@ -65,3 +70,4 @@ Feature: User interacts with Stages and Events Widget Scenario: Program stage is hidden if no data read access And you open the enrollment page by typing #enrollment?enrollmentId=iNEq9d22Nyp&orgUnitId=DiszpKrYNg8&programId=WSGAb5XwJ3Y&teiId=k4ODejBytgv Then the Care at birth program stage should be hidden + diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js index 497f8bca84..b5e8e176c8 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageCreateNewButton/StageCreateNewButton.js @@ -21,32 +21,35 @@ export const StageCreateNewButton = ({ preventAddingEventActionInEffect, eventName, }: Props) => { - const isDisabled = useMemo(() => { + const { isDisabled, tooltipContent } = useMemo(() => { if (!stageWriteAccess) { - return true; - } - if (!repeatable && eventCount > 0) { - return true; - } - return !!preventAddingEventActionInEffect; - }, [eventCount, preventAddingEventActionInEffect, repeatable, stageWriteAccess]); - const tooltipContent = useMemo(() => { - if (!stageWriteAccess) { - return i18n.t('You do not have access to create events in this stage', { - programStageName: eventName, - interpolation: { escapeValue: false }, + return ({ + isDisabled: true, + tooltipContent: i18n.t('You do not have access to create events in this stage', { + programStageName: eventName, + interpolation: { escapeValue: false }, + }), }); } if (preventAddingEventActionInEffect) { - return i18n.t("You can't add any more {{ programStageName }} events", { - programStageName: eventName, - interpolation: { escapeValue: false }, - }); + return { + isDisabled: true, + tooltipContent: i18n.t("You can't add any more {{ programStageName }} events", { + programStageName: eventName, + interpolation: { escapeValue: false }, + }), + }; } if (!repeatable && eventCount > 0) { - return i18n.t('This stage can only have one event'); + return { + isDisabled: true, + tooltipContent: i18n.t('This stage can only have one event'), + }; } - return ''; + return { + isDisabled: false, + tooltipContent: '', + }; }, [eventCount, eventName, preventAddingEventActionInEffect, repeatable, stageWriteAccess]); return ( From 2ed2d8f992a6ee6f7308422db7901263d98a9d97 Mon Sep 17 00:00:00 2001 From: eirikhaugstulen Date: Tue, 13 Aug 2024 11:34:39 +0200 Subject: [PATCH 5/5] chore: add cypress tests --- .../EnrollmentQuickActions/EnrollmentQuickActions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js index b750047cac..a450af97d9 100644 --- a/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js +++ b/cypress/e2e/EnrollmentPage/EnrollmentQuickActions/EnrollmentQuickActions.js @@ -1,5 +1,9 @@ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'; +Given(/^you open the enrollment page by typing (.*)$/, url => + cy.visit(url), +); + 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"]')