Skip to content

Commit

Permalink
fix: [DHIS2-16318] enrollment page url management (#3522)
Browse files Browse the repository at this point in the history
  • Loading branch information
superskip authored Feb 27, 2024
1 parent 561b29d commit 0abec43
Show file tree
Hide file tree
Showing 12 changed files with 614 additions and 281 deletions.
2 changes: 0 additions & 2 deletions cypress/e2e/EnrollmentPage/BreakingTheGlass.feature
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
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 the data store is clean
Expand Down
4 changes: 4 additions & 0 deletions cypress/e2e/EnrollmentPage/EnrollmentPageNavigation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Feature: User interacts with Enrollment page
Given you enter enrollment page by typing: #/enrollment?programId=IpHINAT79UW&orgUnitId=DiszpKrYNg8&teiId=pybd813kIWx&enrollmentId=AUTO
Then you should be redirect to #/enrollment?enrollmentId=FS085BEkJo2&orgUnitId=DiszpKrYNg8&programId=IpHINAT79UW&teiId=pybd813kIWx

Scenario: Auto select orgunit when opening enrollment dashboard
Given you enter enrollment page by typing: #/enrollment?enrollmentId=avqvQMtX8DG&orgUnitId=DiszpKrYNg8&programId=IpHINAT79UW&teiId=btoHJM9byeF
Then you should be redirect to #/enrollment?enrollmentId=avqvQMtX8DG&orgUnitId=NnGUNkc5Zq8&programId=IpHINAT79UW&teiId=btoHJM9byeF

Scenario: Reset tei
Given you land on the enrollment page by having typed only the enrollmentId in the url
When you reset the tei selection
Expand Down
11 changes: 7 additions & 4 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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-02-09T11:22:44.980Z\n"
"PO-Revision-Date: 2024-02-09T11:22:44.980Z\n"
"POT-Creation-Date: 2024-02-14T10:01:26.732Z\n"
"PO-Revision-Date: 2024-02-14T10:01:26.732Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -662,11 +662,14 @@ msgstr "Stop using new Enrollment dashboard for {{programName}}"
msgid "Opt out for {{programName}}"
msgstr "Opt out for {{programName}}"

msgid "Enrollment with id \"{{enrollmentId}}\" does not exist"
msgstr "Enrollment with id \"{{enrollmentId}}\" does not exist"

msgid "Tracked entity instance with id \"{{teiId}}\" does not exist"
msgstr "Tracked entity instance with id \"{{teiId}}\" does not exist"

msgid "There is an error while opening this enrollment. Please enter a valid url."
msgstr "There is an error while opening this enrollment. Please enter a valid url."
msgid "Program with id \"{{programId}}\" does not exist"
msgstr "Program with id \"{{programId}}\" does not exist"

msgid "An error occurred while fetching enrollments. Please enter a valid url."
msgstr "An error occurred while fetching enrollments. Please enter a valid url."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,137 @@
import { actionCreator } from '../../../actions/actions.utils';

export const enrollmentPageActionTypes = {
INFORMATION_FETCH: 'EnrollmentPage.Fetch',
INFORMATION_USING_TEI_ID_FETCH: 'EnrollmentPage.StartFetchingUsingTeiId',
INFORMATION_USING_ENROLLMENT_ID_FETCH: 'EnrollmentPage.StartFetchingUsingEnrollmentId',
INFORMATION_LOADING_FETCH: 'EnrollmentPage.LoadingOnFetching',
INFORMATION_ERROR_FETCH: 'EnrollmentPage.ErrorOnFetching',
INFORMATION_SUCCESS_FETCH: 'EnrollmentPage.SuccessOnFetching',

ENROLLMENTS_FETCH: 'EnrollmentPage.EnrollmentFetch',
ENROLLMENTS_ERROR_FETCH: 'EnrollmentPage.EnrollmentFetchFailure',
ENROLLMENTS_SUCCESS_FETCH: 'EnrollmentPage.EnrollmentFetchSuccess',

PAGE_OPEN: 'EnrollmentPage.Open',
PAGE_CLEAN: 'EnrollmentPage.CleanOnUnmount',
CUSTOM_PROGRAM_RESET: 'EnrollmentPage.CustomProgramReset',
PAGE_CLOSE: 'EnrollmentPage.Close',

PROCESS_ENROLLMENT_ID: 'EnrollmentPage.EnrollmentUrlIdUpdated',
RESET_ENROLLMENT_ID: 'EnrollmentPage.ResetEnrollmentId',
FETCH_ENROLLMENT_ID: 'EnrollmentPage.FetchEnrollmentId',
VERIFY_ENROLLMENT_ID_SUCCESS: 'EnrollmentPage.VerifyEnrollmentIdSuccess',
FETCH_ENROLLMENT_ID_SUCCESS: 'EnrollmentPage.FetchEnrollmentIdSuccess',
FETCH_ENROLLMENT_ID_ERROR: 'EnrollmentPage.FetchEnrollmentIdError',

PROCESS_TEI_ID: 'EnrollmentPage.TeiUrlIdUpdated',
RESET_TEI_ID: 'EnrollmentPage.ResetTeiId',
FETCH_TEI: 'EnrollmentPage.FetchTei',
VERIFY_FETCH_TEI_SUCCESS: 'EnrollmentPage.VerifyFetchTeiSuccess',
FETCH_TEI_SUCCESS: 'EnrollmentPage.FetchTeiSuccess',
FETCH_TEI_ERROR: 'EnrollmentPage.FetchTeiError',

PROCESS_PROGRAM_ID: 'EnrollmentPage.ProgramUrlIdUpdated',
COMMIT_TRACKER_PROGRAM_ID: 'EnrollmentPage.CommitTrackerProgramId',
COMMIT_NON_TRACKER_PROGRAM_ID: 'EnrollmentPage.CommitNonTrackerProgramId',
PROGRAM_ID_ERROR: 'EnrollmentPage.ProgramIdError',

FETCH_ENROLLMENTS: 'EnrollmentPage.FetchEnrollments',
VERIFY_FETCHED_ENROLLMENTS: 'EnrollmentPage.VerifyFetchedEnrollments',
FETCH_ENROLLMENTS_ERROR: 'EnrollmentPage.FetchEnrollmentsError',
FETCH_ENROLLMENTS_SUCCESS: 'EnrollmentPage.FetchEnrollmentsSuccess',

DEFAULT_VIEW: 'EnrollmentPage.DefaultView',
LOADING_VIEW: 'EnrollmentPage.LoadingView',
MISSING_MESSAGE_VIEW: 'EnrollmentPage.MissingMessageView',
ERROR_VIEW: 'EnrollmentPage.ErrorView',
CLEAR_ERROR_VIEW: 'EnrollmentPage.ClearErrorView',

DELETE_ENROLLMENT: 'EnrollmentPage.DeleteEnrollment',
UPDATE_TEI_DISPLAY_NAME: 'EnrollmentPage.UpdateTeiDisplayName',
UPDATE_ENROLLMENT_DATE: 'EnrollmentPage.UpdateEnrollmentDate',
};

export const fetchEnrollmentPageInformation = () =>
actionCreator(enrollmentPageActionTypes.INFORMATION_FETCH)();

export const startFetchingTeiFromTeiId = () =>
actionCreator(enrollmentPageActionTypes.INFORMATION_USING_TEI_ID_FETCH)();
type IdSuite = {
teiId?: ?string,
programId?: ?string,
};

export const startFetchingTeiFromEnrollmentId = () =>
actionCreator(enrollmentPageActionTypes.INFORMATION_USING_ENROLLMENT_ID_FETCH)();
export const openEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.PAGE_OPEN)();

export const showLoadingViewOnEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.INFORMATION_LOADING_FETCH)();
export const closeEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.PAGE_CLOSE)();

export const showDefaultViewOnEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.DEFAULT_VIEW)();
// enrollmentId
export const changedEnrollmentId = (enrollmentId: ?string) =>
actionCreator(enrollmentPageActionTypes.PROCESS_ENROLLMENT_ID)(enrollmentId);

export const showMissingMessageViewOnEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.MISSING_MESSAGE_VIEW)();
export const resetEnrollmentId = (payload: IdSuite) =>
actionCreator(enrollmentPageActionTypes.RESET_ENROLLMENT_ID)(payload);

export const showErrorViewOnEnrollmentPage = ({ error }: { error: string }) =>
actionCreator(enrollmentPageActionTypes.INFORMATION_ERROR_FETCH)({ error });
export const fetchEnrollmentId = (enrollmentId: string) =>
actionCreator(enrollmentPageActionTypes.FETCH_ENROLLMENT_ID)({ enrollmentId });

export const verifyEnrollmentIdSuccess = ({ enrollmentId, trackedEntity, program }: Object) =>
actionCreator(enrollmentPageActionTypes.VERIFY_ENROLLMENT_ID_SUCCESS)({ enrollmentId, teiId: trackedEntity, programId: program });

export const fetchEnrollmentIdSuccess = (payload: IdSuite) =>
actionCreator(enrollmentPageActionTypes.FETCH_ENROLLMENT_ID_SUCCESS)(payload);

export const fetchEnrollmentIdError = (enrollmentId: string) =>
actionCreator(enrollmentPageActionTypes.FETCH_ENROLLMENT_ID_ERROR)({ enrollmentId });

// teiId
export const changedTeiId = (payload: IdSuite) =>
actionCreator(enrollmentPageActionTypes.PROCESS_TEI_ID)(payload);

export const resetTeiId = () =>
actionCreator(enrollmentPageActionTypes.RESET_TEI_ID)();

export const fetchTei = (payload: IdSuite) =>
actionCreator(enrollmentPageActionTypes.FETCH_TEI)(payload);

export const verifyFetchTeiSuccess = (payload: { ...IdSuite, teiDisplayName: string, tetId: string }) =>
actionCreator(enrollmentPageActionTypes.VERIFY_FETCH_TEI_SUCCESS)(payload);

export const successfulFetchingEnrollmentPageInformationFromUrl = ({ teiDisplayName, tetId }: Object) =>
actionCreator(enrollmentPageActionTypes.INFORMATION_SUCCESS_FETCH)(
{ teiDisplayName, tetId });
export const fetchTeiSuccess = (payload: { ...IdSuite, teiDisplayName: string, tetId: string }) =>
actionCreator(enrollmentPageActionTypes.FETCH_TEI_SUCCESS)(payload);

export const fetchTeiError = (teiId: string) =>
actionCreator(enrollmentPageActionTypes.FETCH_TEI_ERROR)({ teiId });

// programId
export const changedProgramId = (payload: IdSuite) =>
actionCreator(enrollmentPageActionTypes.PROCESS_PROGRAM_ID)(payload);

export const commitTrackerProgramId = (programId: string) =>
actionCreator(enrollmentPageActionTypes.COMMIT_TRACKER_PROGRAM_ID)({ programId });

export const commitNonTrackerProgramId = (programId: string) =>
actionCreator(enrollmentPageActionTypes.COMMIT_NON_TRACKER_PROGRAM_ID)({ programId });

export const programIdError = (programId: string) =>
actionCreator(enrollmentPageActionTypes.PROGRAM_ID_ERROR)({ programId });

// enrollments
export const fetchEnrollments = () =>
actionCreator(enrollmentPageActionTypes.ENROLLMENTS_FETCH)();
actionCreator(enrollmentPageActionTypes.FETCH_ENROLLMENTS)();

export const verifyFetchedEnrollments = ({ teiId, programId, action }: Object) =>
actionCreator(enrollmentPageActionTypes.VERIFY_FETCHED_ENROLLMENTS)({ teiId, programId, action });

export const updateEnrollmentAccessLevel = ({ programId, accessLevel }: { programId: string, accessLevel: string }) =>
actionCreator(enrollmentPageActionTypes.ENROLLMENTS_ERROR_FETCH)({ programId, accessLevel });
export const fetchEnrollmentsError = ({ accessLevel }: { accessLevel: string }) =>
actionCreator(enrollmentPageActionTypes.FETCH_ENROLLMENTS_ERROR)({ accessLevel });

export const saveEnrollments = ({ programId, enrollments }: any) =>
actionCreator(enrollmentPageActionTypes.ENROLLMENTS_SUCCESS_FETCH)({ programId, enrollments });
export const saveEnrollments = ({ enrollments }: any) =>
actionCreator(enrollmentPageActionTypes.FETCH_ENROLLMENTS_SUCCESS)({ enrollments });

export const openEnrollmentPage = ({ programId, orgUnitId, teiId, enrollmentId }: Object) =>
actionCreator(enrollmentPageActionTypes.PAGE_OPEN)({ programId, orgUnitId, teiId, enrollmentId });
// Page status
export const showDefaultViewOnEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.DEFAULT_VIEW)();

export const showLoadingViewOnEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.LOADING_VIEW)();

export const showMissingMessageViewOnEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.MISSING_MESSAGE_VIEW)();

export const showErrorViewOnEnrollmentPage = ({ error }: { error: string }) =>
actionCreator(enrollmentPageActionTypes.ERROR_VIEW)({ error });

export const cleanEnrollmentPage = () =>
actionCreator(enrollmentPageActionTypes.PAGE_CLEAN)();
export const clearErrorView = () =>
actionCreator(enrollmentPageActionTypes.CLEAR_ERROR_VIEW)();

// Mutations
export const deleteEnrollment = ({ enrollmentId }: { enrollmentId: string }) =>
actionCreator(enrollmentPageActionTypes.DELETE_ENROLLMENT)({
enrollmentId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export const enrollmentPageStatuses = {
MISSING_SELECTIONS: 'MISSING_SELECTIONS',
};

export const selectionStatus = {
READY: 'READY',
ERROR: 'ERROR',
LOADING: 'LOADING',
};

export const enrollmentAccessLevels = {
FULL_ACCESS: 'FULL_ACCESS',
LIMITED_ACCESS: 'LIMITED_ACCESS',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import React, { useEffect, useMemo } from 'react';
import type { ComponentType } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { EnrollmentPageComponent } from './EnrollmentPage.component';
import type { EnrollmentPageStatus } from './EnrollmentPage.types';
import {
cleanEnrollmentPage,
fetchEnrollmentPageInformation,
fetchEnrollments,
updateEnrollmentAccessLevel,
openEnrollmentPage,
closeEnrollmentPage,
changedEnrollmentId,
changedTeiId,
changedProgramId,
resetTeiId,
showDefaultViewOnEnrollmentPage,
showMissingMessageViewOnEnrollmentPage,
showLoadingViewOnEnrollmentPage,
} from './EnrollmentPage.actions';
import { scopeTypes } from '../../../metaData/helpers/constants';
import { useScopeInfo } from '../../../hooks/useScopeInfo';
import { useEnrollmentInfo } from './useEnrollmentInfo';
import { enrollmentPageStatuses, enrollmentAccessLevels } from './EnrollmentPage.constants';
import { enrollmentPageStatuses } from './EnrollmentPage.constants';
import { getScopeInfo } from '../../../metaData';
import {
buildEnrollmentsAsOptions,
Expand All @@ -30,7 +31,10 @@ const useComponentLifecycle = () => {

const { scopeType } = useScopeInfo(programId);
const { setEnrollmentId } = useSetEnrollmentId();
const { enrollmentAccessLevel, programId: enrollmentProgramId } = useSelector(({ enrollmentPage }) => enrollmentPage);
const {
enrollmentPageStatus,
programId: enrollmentProgramId,
} = useSelector(({ enrollmentPage }) => enrollmentPage);

const { programHasEnrollments, enrollmentsOnProgramContainEnrollmentId, autoEnrollmentId } = useEnrollmentInfo(enrollmentId, programId, teiId);
useEffect(() => {
Expand All @@ -39,7 +43,7 @@ const useComponentLifecycle = () => {
setEnrollmentId({ enrollmentId: autoEnrollmentId, shouldReplaceHistory: true });
} else if (selectedProgramIsTracker && programHasEnrollments && enrollmentsOnProgramContainEnrollmentId) {
dispatch(showDefaultViewOnEnrollmentPage());
} else if (programId === enrollmentProgramId) {
} else if (programId === enrollmentProgramId && enrollmentPageStatus !== enrollmentPageStatuses.LOADING) {
dispatch(showMissingMessageViewOnEnrollmentPage());
} else {
dispatch(showLoadingViewOnEnrollmentPage());
Expand All @@ -54,18 +58,18 @@ const useComponentLifecycle = () => {
scopeType,
enrollmentId,
autoEnrollmentId,
enrollmentAccessLevel,
enrollmentPageStatus,
enrollmentProgramId,
]);

useEffect(() => () => dispatch(cleanEnrollmentPage()), [dispatch, teiId]);
useEffect(() => () => dispatch(closeEnrollmentPage()), [dispatch]);
};

// dirty fix for scenarios where you deselect the program.
// This should be removed as part of fixing the url sync issue, https://jira.dhis2.org/browse/TECH-580
const useComputedEnrollmentPageStatus = () => {
const enrollmentPageStatus: EnrollmentPageStatus =
useSelector(({ enrollmentPage }) => enrollmentPage.enrollmentPageStatus);
const {
enrollmentPageStatus,
programId: reduxProgramId,
} = useSelector(({ enrollmentPage }) => enrollmentPage);

const { teiId, programId, enrollmentId } = useLocationQuery();
const { scopeType } = useScopeInfo(programId);
Expand All @@ -75,7 +79,7 @@ const useComputedEnrollmentPageStatus = () => {
return enrollmentPageStatuses.MISSING_SELECTIONS;
}
if (enrollmentPageStatus === enrollmentPageStatuses.DEFAULT &&
!(programId && teiId && enrollmentId)) {
!(programId && teiId && enrollmentId && programId === reduxProgramId)) {
return enrollmentPageStatuses.LOADING;
}
return enrollmentPageStatus;
Expand All @@ -85,6 +89,7 @@ const useComputedEnrollmentPageStatus = () => {
enrollmentId,
teiId,
programId,
reduxProgramId,
]);
};

Expand All @@ -98,25 +103,12 @@ export const EnrollmentPage: ComponentType<{||}> = () => {
const enrollmentsAsOptions = buildEnrollmentsAsOptions(enrollments, programId);

useEffect(() => {
dispatch(fetchEnrollmentPageInformation());
},
[
dispatch,
teiId,
]);
dispatch(openEnrollmentPage());
}, [dispatch]);

useEffect(() => {
programId ?
dispatch(fetchEnrollments()) :
dispatch(updateEnrollmentAccessLevel({
programId,
accessLevel: enrollmentAccessLevels.UNKNOWN_ACCESS,
}));
},
[
dispatch,
programId,
]);
useEffect(() => { dispatch(changedEnrollmentId(enrollmentId)); }, [dispatch, enrollmentId]);
useEffect(() => { dispatch(teiId ? changedTeiId({ teiId }) : resetTeiId()); }, [dispatch, teiId]);
useEffect(() => { dispatch(changedProgramId({ programId })); }, [dispatch, programId]);

const error: boolean =
useSelector(({ activePage }) => activePage.selectionsError && activePage.selectionsError.error);
Expand Down
Loading

0 comments on commit 0abec43

Please sign in to comment.