diff --git a/.cursor/rules/code-guidelines.mdc b/.cursor/rules/code-guidelines.mdc
new file mode 100644
index 0000000000..6304eca4fb
--- /dev/null
+++ b/.cursor/rules/code-guidelines.mdc
@@ -0,0 +1,22 @@
+---
+description: General code guidelines for bugs and features.
+globs: src/*.js
+alwaysApply: false
+---
+
+## Logging
+When logging things to the browser console please use the following format.
+
+```
+import log from 'loglevel';
+import { errorCreator } from 'capture-core-utils';
+
+log.error(
+ errorCreator('Message to be logged')(
+ {
+ // Any details
+ },
+ ),
+);
+
+```
\ No newline at end of file
diff --git a/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.component.js b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.component.js
new file mode 100644
index 0000000000..2c31e8f7ed
--- /dev/null
+++ b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.component.js
@@ -0,0 +1,61 @@
+// @flow
+import React from 'react';
+import { withStyles } from '@material-ui/core';
+import { colors, spacers } from '@dhis2/ui';
+import i18n from '@dhis2/d2-i18n';
+import { SingleEventRegistrationEntry } from '../DataEntries';
+import type { Props } from './EventRegistrationEntryWrapper.types';
+
+const getStyles = () => ({
+ container: {
+ marginBottom: spacers.dp12,
+ padding: spacers.dp16,
+ background: colors.white,
+ border: '1px solid',
+ borderColor: colors.grey400,
+ borderRadius: 3,
+ },
+ title: {
+ padding: '8px 0 0px 8px',
+ fontWeight: 500,
+ marginBottom: spacers.dp16,
+ },
+ flexContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ flexWrap: 'wrap',
+ },
+ flexItem: {
+ flex: 1,
+ minWidth: '500px',
+ },
+ noAccessContainer: {
+ padding: spacers.dp16,
+ },
+});
+
+const EventRegistrationEntryWrapperPlain = ({
+ classes,
+ selectedScopeId,
+ dataEntryId,
+ eventAccess,
+}: Props) => {
+ if (!eventAccess?.write) {
+ return (
+
+ {i18n.t('You don\'t have access to create an event in the current selections')}
+
+ );
+ }
+
+ return (
+
+ );
+};
+
+export const EventRegistrationEntryWrapperComponent = withStyles(getStyles)(EventRegistrationEntryWrapperPlain);
diff --git a/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.container.js b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.container.js
new file mode 100644
index 0000000000..6ecb28ad7d
--- /dev/null
+++ b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.container.js
@@ -0,0 +1,56 @@
+// @flow
+import { connect, useDispatch, useSelector } from 'react-redux';
+import * as React from 'react';
+import { useEffect, useRef } from 'react';
+import { compose } from 'redux';
+import { batchActions } from 'redux-batched-actions';
+import { EventRegistrationEntryWrapperComponent } from './EventRegistrationEntryWrapper.component';
+import { withLoadingIndicator } from '../../HOC/withLoadingIndicator';
+import type { ContainerProps, StateProps, ReduxState } from './EventRegistrationEntryWrapper.types';
+import { getOpenDataEntryActions } from '../DataEntries/SingleEventRegistrationEntry/DataEntryWrapper/DataEntry';
+import { useCategoryCombinations } from '../DataEntryDhis2Helpers/AOC/useCategoryCombinations';
+import { makeEventAccessSelector } from './selectors';
+import { itemId } from './constants';
+
+const makeMapStateToProps = () => {
+ const eventAccessSelector = makeEventAccessSelector();
+ return (state: ReduxState, ownProps: ContainerProps): StateProps => ({
+ selectedScopeId: ownProps.selectedScopeId,
+ dataEntryId: ownProps.dataEntryId,
+ orgUnitId: state.currentSelections.orgUnitId,
+ ready: state.dataEntries[ownProps.dataEntryId]?.itemId === itemId,
+ eventAccess: eventAccessSelector(state),
+ });
+};
+
+const openSingleEventDataEntry = (InnerComponent: React.ComponentType) => (
+ (props: ContainerProps) => {
+ const hasRun = useRef(false);
+ const { selectedScopeId } = props;
+ const dispatch = useDispatch();
+ const selectedCategories = useSelector((state: ReduxState) => state.currentSelections.categories);
+ const { isLoading, programCategory } = useCategoryCombinations(selectedScopeId);
+
+ useEffect(() => {
+ if (!isLoading && !hasRun.current) {
+ dispatch(
+ batchActions([
+ ...getOpenDataEntryActions(programCategory, selectedCategories),
+ ]),
+ );
+ hasRun.current = true;
+ }
+ }, [selectedCategories, dispatch, isLoading, programCategory]);
+
+ return (
+
+ );
+ });
+
+export const EventRegistrationEntryWrapper = compose(
+ openSingleEventDataEntry,
+ connect(makeMapStateToProps(), () => ({})),
+ withLoadingIndicator(),
+)(EventRegistrationEntryWrapperComponent);
diff --git a/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.types.js b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.types.js
new file mode 100644
index 0000000000..8804fb77d3
--- /dev/null
+++ b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/EventRegistrationEntryWrapper.types.js
@@ -0,0 +1,30 @@
+// @flow
+export type Props = {|
+ classes: Object,
+ selectedScopeId: string,
+ dataEntryId: string,
+ orgUnitId: string,
+ eventAccess?: {|
+ read: boolean,
+ write: boolean,
+ |},
+|};
+
+export type ContainerProps = {|
+ selectedScopeId: string,
+ dataEntryId: string,
+|};
+
+export type StateProps = {|
+ selectedScopeId: string,
+ orgUnitId: string,
+ dataEntryId: string,
+ eventAccess: {|
+ read: boolean,
+ write: boolean,
+ |},
+ ready: boolean,
+|};
+
+export type ReduxState = Object;
+export type MapStateToProps = (ReduxState, ContainerProps) => StateProps;
diff --git a/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/constants.js b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/constants.js
new file mode 100644
index 0000000000..5ee70343d8
--- /dev/null
+++ b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/constants.js
@@ -0,0 +1,5 @@
+// @flow
+
+// Constants duplicated from SingleEventRegistrationEntry
+export const dataEntryId = 'singleEvent';
+export const itemId = 'newEvent';
diff --git a/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/index.js b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/index.js
new file mode 100644
index 0000000000..c5a906b67a
--- /dev/null
+++ b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/index.js
@@ -0,0 +1,2 @@
+// @flow
+export { EventRegistrationEntryWrapper } from './EventRegistrationEntryWrapper.container';
diff --git a/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/selectors.js b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/selectors.js
new file mode 100644
index 0000000000..4ac674b951
--- /dev/null
+++ b/src/core_modules/capture-core/components/EventRegistrationEntryWrapper/selectors.js
@@ -0,0 +1,17 @@
+// @flow
+
+import { createSelector } from 'reselect';
+import { getProgramEventAccess } from '../../metaData';
+
+const programIdSelector = state => state.currentSelections.programId;
+const categoriesMetaSelector = state => state.currentSelections.categoriesMeta;
+const programStageIdSelector = state => state.currentSelections.stageId;
+
+// $FlowFixMe[missing-annot] automated comment
+export const makeEventAccessSelector = () => createSelector(
+ programIdSelector,
+ categoriesMetaSelector,
+ programStageIdSelector,
+ (programId: string, categoriesMeta: ?Object, programStageId: ?string) =>
+ programId && getProgramEventAccess(programId, programStageId, categoriesMeta),
+);
diff --git a/src/core_modules/capture-core/components/ListView/types/listView.types.js b/src/core_modules/capture-core/components/ListView/types/listView.types.js
index c367075b4e..2c5e13e059 100644
--- a/src/core_modules/capture-core/components/ListView/types/listView.types.js
+++ b/src/core_modules/capture-core/components/ListView/types/listView.types.js
@@ -104,6 +104,7 @@ export type InterfaceProps = $ReadOnly<{|
onSetColumnOrder: SetColumnOrder,
onSort: Sort,
onUpdateFilter: UpdateFilter,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
rowIdKey: string,
rowsPerPage: number,
sortById: string,
diff --git a/src/core_modules/capture-core/components/Pages/MainPage/EventWorkingListsInit/EventWorkingListsInit.container.type.js b/src/core_modules/capture-core/components/Pages/MainPage/EventWorkingListsInit/EventWorkingListsInit.container.type.js
index 87a92ad444..cd33f369b1 100644
--- a/src/core_modules/capture-core/components/Pages/MainPage/EventWorkingListsInit/EventWorkingListsInit.container.type.js
+++ b/src/core_modules/capture-core/components/Pages/MainPage/EventWorkingListsInit/EventWorkingListsInit.container.type.js
@@ -1,5 +1,7 @@
// @flow
export type Props = $ReadOnly<{|
programId: string,
- orgUnitId: string
+ orgUnitId: string,
+ selectedTemplateId?: string,
+ onChangeTemplate?: (selectedTemplateId?: string) => void
|}>;
diff --git a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js
index 45c7318ad0..ceaf9552ab 100644
--- a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js
+++ b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.component.js
@@ -12,10 +12,11 @@ import { WithoutCategorySelectedMessage } from './WithoutCategorySelectedMessage
import { withErrorMessageHandler, withLoadingIndicator } from '../../../HOC';
import { SearchBox } from '../../SearchBox';
import { TemplateSelector } from '../../TemplateSelector';
+import { NoSelectionsInfoBox } from './NoSelectionsInfoBox';
+import { EventRegistrationEntryWrapper } from '../../EventRegistrationEntryWrapper';
import {
InvalidCategoryCombinationForOrgUnitMessage,
} from './InvalidCategoryCombinationForOrgUnitMessage/InvalidCategoryCombinationForOrgUnitMessage';
-import { NoSelectionsInfoBox } from './NoSelectionsInfoBox';
const getStyles = () => ({
listContainer: {
@@ -56,9 +57,12 @@ const MainPagePlain = ({
const showMainPage = useMemo(() => {
const noProgramSelected = !programId;
const noOrgUnitSelected = !orgUnitId;
- const isEventProgram = !trackedEntityTypeId;
- return noProgramSelected || noOrgUnitSelected || isEventProgram || displayFrontPageList || selectedTemplateId;
- }, [programId, orgUnitId, trackedEntityTypeId, displayFrontPageList, selectedTemplateId]);
+
+ return noProgramSelected ||
+ noOrgUnitSelected ||
+ displayFrontPageList ||
+ selectedTemplateId;
+ }, [programId, orgUnitId, displayFrontPageList, selectedTemplateId]);
return (
<>
@@ -88,14 +92,30 @@ const MainPagePlain = ({
)}
>
) : (
-
+ <>
+ {trackedEntityTypeId ? (
+
+ ) : (
+
+ )}
+ >
)}
>
);
diff --git a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js
index 56779b2ee2..94eaafeca0 100644
--- a/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js
+++ b/src/core_modules/capture-core/components/Pages/MainPage/MainPage.container.js
@@ -115,7 +115,7 @@ const MainPageContainer = () => {
const selectedProgram = programCollection.get(programId);
// $FlowFixMe[prop-missing]
const trackedEntityTypeId = selectedProgram?.trackedEntityType?.id;
- const displayFrontPageList = trackedEntityTypeId && selectedProgram?.displayFrontPageList;
+ const displayFrontPageList = selectedProgram?.displayFrontPageList;
const MainPageStatus = useMainPageStatus({
programId,
selectedProgram,
diff --git a/src/core_modules/capture-core/components/Pages/MainPage/WorkingListsType/WorkingListsType.component.js b/src/core_modules/capture-core/components/Pages/MainPage/WorkingListsType/WorkingListsType.component.js
index 732f0d07d5..a97df1a33f 100644
--- a/src/core_modules/capture-core/components/Pages/MainPage/WorkingListsType/WorkingListsType.component.js
+++ b/src/core_modules/capture-core/components/Pages/MainPage/WorkingListsType/WorkingListsType.component.js
@@ -8,7 +8,14 @@ import type { Props } from './workingListsType.types';
export const WorkingListsType = ({ programId, orgUnitId, selectedTemplateId, onChangeTemplate }: Props) => {
const { programType } = useProgramInfo(programId);
if (programType === programTypes.EVENT_PROGRAM) {
- return ;
+ return (
+
+ );
}
if (programType === programTypes.TRACKER_PROGRAM) {
diff --git a/src/core_modules/capture-core/components/TemplateSelector/EventTemplateSelector/EventTemplateSelector.container.js b/src/core_modules/capture-core/components/TemplateSelector/EventTemplateSelector/EventTemplateSelector.container.js
new file mode 100644
index 0000000000..2faf1cb042
--- /dev/null
+++ b/src/core_modules/capture-core/components/TemplateSelector/EventTemplateSelector/EventTemplateSelector.container.js
@@ -0,0 +1,30 @@
+// @flow
+import React from 'react';
+import { TemplateSelector as TemplateSelectorComponent } from '../TemplateSelector.component';
+import { useNavigate, buildUrlQueryString, useLocationQuery } from '../../../utils/routing';
+import { useEventTemplates } from '../hooks';
+
+export const EventTemplateSelector = () => {
+ const { navigate } = useNavigate();
+ const { programId, orgUnitId } = useLocationQuery();
+ const { eventTemplates, loading: loadingEventTemplates } = useEventTemplates(programId);
+
+ const onSelectTemplate = template =>
+ navigate(`/?${buildUrlQueryString({ orgUnitId, programId, selectedTemplateId: template.id })}`);
+ const onCreateTemplate = () => {
+ const urlQueryString = buildUrlQueryString({
+ orgUnitId,
+ programId,
+ selectedTemplateId: `${programId}-default`,
+ });
+ navigate(`/?${urlQueryString}`);
+ };
+
+ return programId && !loadingEventTemplates ? (
+
+ ) : null;
+};
diff --git a/src/core_modules/capture-core/components/TemplateSelector/EventTemplateSelector/index.js b/src/core_modules/capture-core/components/TemplateSelector/EventTemplateSelector/index.js
new file mode 100644
index 0000000000..d5de8470d5
--- /dev/null
+++ b/src/core_modules/capture-core/components/TemplateSelector/EventTemplateSelector/index.js
@@ -0,0 +1,2 @@
+// @flow
+export { EventTemplateSelector } from './EventTemplateSelector.container';
diff --git a/src/core_modules/capture-core/components/TemplateSelector/TEITemplateSelector/TEITemplateSelector.container.js b/src/core_modules/capture-core/components/TemplateSelector/TEITemplateSelector/TEITemplateSelector.container.js
new file mode 100644
index 0000000000..4c0fdaf7a8
--- /dev/null
+++ b/src/core_modules/capture-core/components/TemplateSelector/TEITemplateSelector/TEITemplateSelector.container.js
@@ -0,0 +1,31 @@
+// @flow
+import React from 'react';
+import { TemplateSelector as TemplateSelectorComponent } from '../TemplateSelector.component';
+import { useNavigate, buildUrlQueryString, useLocationQuery } from '../../../utils/routing';
+import { useTEITemplates, useProgramStageTemplates } from '../hooks';
+
+export const TEITemplateSelector = () => {
+ const { navigate } = useNavigate();
+ const { programId, orgUnitId } = useLocationQuery();
+ const { TEITemplates, loading: loadingTEITemplates } = useTEITemplates(programId);
+ const { programStageTemplates, loading: loadingProgramStageTemplates } = useProgramStageTemplates(programId);
+
+ const onSelectTemplate = template =>
+ navigate(`/?${buildUrlQueryString({ orgUnitId, programId, selectedTemplateId: template.id })}`);
+ const onCreateTemplate = () => {
+ const urlQueryString = buildUrlQueryString({
+ orgUnitId,
+ programId,
+ selectedTemplateId: `${programId}-default`,
+ });
+ navigate(`/?${urlQueryString}`);
+ };
+
+ return programId && !loadingTEITemplates && !loadingProgramStageTemplates ? (
+
+ ) : null;
+};
diff --git a/src/core_modules/capture-core/components/TemplateSelector/TEITemplateSelector/index.js b/src/core_modules/capture-core/components/TemplateSelector/TEITemplateSelector/index.js
new file mode 100644
index 0000000000..2833eca8bd
--- /dev/null
+++ b/src/core_modules/capture-core/components/TemplateSelector/TEITemplateSelector/index.js
@@ -0,0 +1,2 @@
+// @flow
+export { TEITemplateSelector } from './TEITemplateSelector.container';
diff --git a/src/core_modules/capture-core/components/TemplateSelector/TemplateSelector.container.js b/src/core_modules/capture-core/components/TemplateSelector/TemplateSelector.container.js
index a567f4ffbc..8e645fce34 100644
--- a/src/core_modules/capture-core/components/TemplateSelector/TemplateSelector.container.js
+++ b/src/core_modules/capture-core/components/TemplateSelector/TemplateSelector.container.js
@@ -1,31 +1,22 @@
// @flow
import React from 'react';
-import { TemplateSelector as TemplateSelectorComponent } from './TemplateSelector.component';
-import { useNavigate, buildUrlQueryString, useLocationQuery } from '../../utils/routing';
-import { useTEITemplates, useProgramStageTemplates } from './hooks';
+import { useLocationQuery } from '../../utils/routing';
+import { useProgramInfo, programTypes } from '../../hooks/useProgramInfo';
+import { TEITemplateSelector } from './TEITemplateSelector';
+import { EventTemplateSelector } from './EventTemplateSelector';
export const TemplateSelector = () => {
- const { navigate } = useNavigate();
- const { programId, orgUnitId } = useLocationQuery();
- const { TEITemplates, loading: loadingTEITemplates } = useTEITemplates(programId);
- const { programStageTemplates, loading: loadingProgramStageTemplates } = useProgramStageTemplates(programId);
+ const { programId } = useLocationQuery();
+ const { programType } = useProgramInfo(programId);
- const onSelectTemplate = template =>
- navigate(`/?${buildUrlQueryString({ orgUnitId, programId, selectedTemplateId: template.id })}`);
- const onCreateTemplate = () => {
- const urlQueryString = buildUrlQueryString({
- orgUnitId,
- programId,
- selectedTemplateId: `${programId}-default`,
- });
- navigate(`/?${urlQueryString}`);
- };
+ if (!programId) {
+ return null;
+ }
- return programId && !loadingTEITemplates && !loadingProgramStageTemplates ? (
-
- ) : null;
+ // Use the appropriate template selector based on program type
+ if (programType === programTypes.EVENT_PROGRAM) {
+ return ;
+ }
+
+ return ;
};
diff --git a/src/core_modules/capture-core/components/TemplateSelector/hooks/index.js b/src/core_modules/capture-core/components/TemplateSelector/hooks/index.js
index 5371b69ec3..921ce4cdba 100644
--- a/src/core_modules/capture-core/components/TemplateSelector/hooks/index.js
+++ b/src/core_modules/capture-core/components/TemplateSelector/hooks/index.js
@@ -1,3 +1,4 @@
// @flow
export { useTEITemplates } from './useTEITemplates';
export { useProgramStageTemplates } from './useProgramStageTemplates';
+export { useEventTemplates } from './useEventTemplates';
diff --git a/src/core_modules/capture-core/components/TemplateSelector/hooks/useEventTemplates.js b/src/core_modules/capture-core/components/TemplateSelector/hooks/useEventTemplates.js
new file mode 100644
index 0000000000..aef1ae5966
--- /dev/null
+++ b/src/core_modules/capture-core/components/TemplateSelector/hooks/useEventTemplates.js
@@ -0,0 +1,33 @@
+// @flow
+import { useMemo } from 'react';
+import { useApiDataQuery } from '../../../utils/reactQueryHelpers';
+
+export const useEventTemplates = (programId: string) => {
+ const query = useMemo(() => ({
+ resource: 'eventFilters',
+ params: {
+ filter: `program:eq:${programId}`,
+ fields: `
+ id,displayName,eventQueryCriteria,access,externalAccess,publicAccess,
+ user[id,username],
+ userAccesses[id,access],
+ userGroupAccesses[id,access]
+ `,
+ },
+ }), [programId]);
+
+ const { data, isLoading, error } = useApiDataQuery(
+ ['eventTemplates', programId],
+ query,
+ {
+ enabled: !!programId,
+ select: (response: any) => response?.eventFilters || [],
+ },
+ );
+
+ return {
+ error,
+ loading: isLoading,
+ eventTemplates: data || [],
+ };
+};
diff --git a/src/core_modules/capture-core/components/TemplateSelector/index.js b/src/core_modules/capture-core/components/TemplateSelector/index.js
index f66c549b86..4dcea37113 100644
--- a/src/core_modules/capture-core/components/TemplateSelector/index.js
+++ b/src/core_modules/capture-core/components/TemplateSelector/index.js
@@ -1 +1,4 @@
+// @flow
export { TemplateSelector } from './TemplateSelector.container';
+export { TEITemplateSelector } from './TEITemplateSelector';
+export { EventTemplateSelector } from './EventTemplateSelector';
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ColumnSetup/eventWorkingListsColumnSetup.types.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ColumnSetup/eventWorkingListsColumnSetup.types.js
index e187abbcab..00ffdb7bea 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ColumnSetup/eventWorkingListsColumnSetup.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ColumnSetup/eventWorkingListsColumnSetup.types.js
@@ -10,6 +10,7 @@ type ExtractedProps = {|
customColumnOrder?: CustomColumnOrder,
onLoadView: Function,
onUpdateList: Function,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|};
// had to add customColumnOrder as a non optional type or else it would not be removed. Also, if customColumnOrder is
@@ -31,4 +32,5 @@ export type EventWorkingListsColumnSetupOutputProps = {|
defaultColumns: EventWorkingListsColumnConfigs,
onLoadView: Function,
onUpdateList: Function,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/CurrentViewChangesResolver/currentViewChangesResolver.types.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/CurrentViewChangesResolver/currentViewChangesResolver.types.js
index eb599fda73..bb6cfc6feb 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/CurrentViewChangesResolver/currentViewChangesResolver.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/CurrentViewChangesResolver/currentViewChangesResolver.types.js
@@ -31,4 +31,5 @@ export type CurrentViewChangesResolverOutputProps = {|
sortById?: string,
sortByDirection?: string,
currentViewHasTemplateChanges?: boolean,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/DataSourceSetup/eventWorkingListsDataSourceSetup.types.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/DataSourceSetup/eventWorkingListsDataSourceSetup.types.js
index e8d62c5e5b..cf19bdb3b1 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/DataSourceSetup/eventWorkingListsDataSourceSetup.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/DataSourceSetup/eventWorkingListsDataSourceSetup.types.js
@@ -26,4 +26,5 @@ export type EventWorkingListsDataSourceSetupOutputProps = {|
columns: EventWorkingListsColumnConfigs,
dataSource?: DataSource,
rowIdKey: string,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.component.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.component.js
index e36f40b210..c9baebf1d2 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.component.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.component.js
@@ -7,7 +7,7 @@ import { EventWorkingListsReduxProvider } from './ReduxProvider';
import { useProgramStageInfo } from '../../../metaDataMemoryStores/programCollection/helpers';
import type { Props } from './EventWorkingLists.types';
-export const EventWorkingLists = ({ storeId, programId, programStageId, orgUnitId }: Props) => {
+export const EventWorkingLists = ({ storeId, programId, programStageId, orgUnitId, selectedTemplateId, onChangeTemplate }: Props) => {
const { program, programStage, error } = useProgramStageInfo(programStageId, programId);
useEffect(() => {
@@ -29,6 +29,8 @@ export const EventWorkingLists = ({ storeId, programId, programStageId, orgUnitI
// $FlowFixMe
programStage={programStage}
orgUnitId={orgUnitId}
+ selectedTemplateId={selectedTemplateId}
+ onChangeTemplate={onChangeTemplate}
/>
);
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.types.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.types.js
index c5eaa65af0..49b8894304 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/EventWorkingLists.types.js
@@ -5,4 +5,6 @@ export type Props = {|
orgUnitId: string,
programId?: string,
programStageId?: string,
+ selectedTemplateId?: string,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/EventWorkingListsReduxProvider.container.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/EventWorkingListsReduxProvider.container.js
index bcb525bcc6..c232bd9ed7 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/EventWorkingListsReduxProvider.container.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/EventWorkingListsReduxProvider.container.js
@@ -1,5 +1,5 @@
// @flow
-import React, { useCallback } from 'react';
+import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useDataEngine } from '@dhis2/app-runtime';
import { makeQuerySingleResource } from 'capture-core/utils/api';
@@ -14,7 +14,14 @@ import type { Props } from './eventWorkingListsReduxProvider.types';
import { computeDownloadRequest } from './downloadRequest';
import { convertToClientConfig } from '../helpers/eventFilters';
-export const EventWorkingListsReduxProvider = ({ storeId, program, programStage, orgUnitId }: Props) => {
+export const EventWorkingListsReduxProvider = ({
+ storeId,
+ program,
+ programStage,
+ orgUnitId,
+ selectedTemplateId,
+ onChangeTemplate,
+}: Props) => {
const dispatch = useDispatch();
const dataEngine = useDataEngine();
@@ -26,9 +33,33 @@ export const EventWorkingListsReduxProvider = ({ storeId, program, programStage,
onResetListColumnOrder,
onClearFilters,
onUpdateDefaultTemplate,
+ onSelectTemplate,
+ viewPreloaded,
...commonStateManagementRestProps
} = useWorkingListsCommonStateManagement(storeId, SINGLE_EVENT_WORKING_LISTS_TYPE, program);
+ // Use selected template ID from props if provided
+ useEffect(() => {
+ if (selectedTemplateId &&
+ selectedTemplateId !== currentTemplateId &&
+ !viewPreloaded &&
+ templates &&
+ templates.length > 0) {
+ const template = templates.find(t => t.id === selectedTemplateId);
+ if (template) {
+ onSelectTemplate(selectedTemplateId, template.criteria?.programStage);
+ }
+ }
+ }, [selectedTemplateId, templates, currentTemplateId, onSelectTemplate, viewPreloaded]);
+
+ // Custom onSelectTemplate that calls the provided onChangeTemplate
+ const handleSelectTemplate = useCallback((templateId, programStageId) => {
+ onSelectTemplate(templateId, programStageId);
+ if (onChangeTemplate) {
+ onChangeTemplate(templateId);
+ }
+ }, [onSelectTemplate, onChangeTemplate]);
+
const currentTemplate = currentTemplateId && templates &&
templates.find(template => template.id === currentTemplateId);
@@ -103,6 +134,8 @@ export const EventWorkingListsReduxProvider = ({ storeId, program, programStage,
onUpdateList={injectDownloadRequestToUpdateList}
onDeleteEvent={onDeleteEvent}
downloadRequest={downloadRequest}
+ onSelectTemplate={handleSelectTemplate}
+ onChangeTemplate={onChangeTemplate}
/>
);
};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/eventWorkingListsReduxProvider.types.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/eventWorkingListsReduxProvider.types.js
index 33a53d67a6..9ddb450a72 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/eventWorkingListsReduxProvider.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/ReduxProvider/eventWorkingListsReduxProvider.types.js
@@ -44,7 +44,9 @@ export type Props = $ReadOnly<{|
storeId: string,
program: Program,
programStage: ProgramStage,
- orgUnitId: string
+ orgUnitId: string,
+ selectedTemplateId?: string,
+ onChangeTemplate?: (selectedTemplateId?: string) => void
|}>;
export type EventWorkingListsReduxOutputProps = {|
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/TemplateSetup/EventWorkingListsTemplateSetup.component.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/TemplateSetup/EventWorkingListsTemplateSetup.component.js
index fda2407a82..8856457f01 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/TemplateSetup/EventWorkingListsTemplateSetup.component.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/TemplateSetup/EventWorkingListsTemplateSetup.component.js
@@ -23,6 +23,7 @@ export const EventWorkingListsTemplateSetup = ({
onUpdateTemplate,
onDeleteTemplate,
templates,
+ onChangeTemplate,
...passOnProps
}: Props) => {
const injectArgumentsForUpdateTemplate = React.useCallback((template) => {
@@ -36,8 +37,9 @@ export const EventWorkingListsTemplateSetup = ({
sortById,
sortByDirection,
programId: program.id,
+ callBacks: { onChangeTemplate },
});
- }, [onUpdateTemplate, filters, columns, sortById, sortByDirection, program.id]);
+ }, [onUpdateTemplate, filters, columns, sortById, sortByDirection, program.id, onChangeTemplate]);
const injectArgumentsForAddTemplate = React.useCallback((name) => {
// $FlowFixMe For columns: fixing this will create really ugly code. SortById, sortByDirection: Rather complex logic results in sortById and sortByDirection always having a value here.
@@ -51,11 +53,12 @@ export const EventWorkingListsTemplateSetup = ({
sortByDirection,
clientId: uuid(),
programId: program.id,
- });
- }, [onAddTemplate, filters, columns, sortById, sortByDirection, program.id]);
+ }, { onChangeTemplate });
+ }, [onAddTemplate, filters, columns, sortById, sortByDirection, program.id, onChangeTemplate]);
const injectArgumentsForDeleteTemplate = React.useCallback(template =>
- onDeleteTemplate(template, program.id), [onDeleteTemplate, program.id]);
+ onDeleteTemplate(template, program.id, undefined, { onChangeTemplate }),
+ [onDeleteTemplate, program.id, onChangeTemplate]);
return (
void,
|}>;
type RestProps = $Rest;
@@ -42,4 +43,5 @@ export type EventWorkingListsTemplateSetupOutputProps = {|
onDeleteTemplate: DeleteTemplate,
templates?: WorkingListTemplates,
templateSharingType: string,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/EventWorkingListsUpdateTrigger.component.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/EventWorkingListsUpdateTrigger.component.js
index 0b1954c4eb..9f35e293a9 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/EventWorkingListsUpdateTrigger.component.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/EventWorkingListsUpdateTrigger.component.js
@@ -12,6 +12,7 @@ export const EventWorkingListsUpdateTrigger = ({
lastTransactionOnListDataRefresh,
onLoadView,
onUpdateList,
+ onChangeTemplate,
...passOnProps
}: Props) => {
const forceUpdateOnMount = moment().diff(moment(listDataRefreshTimestamp || 0), 'minutes') > 5 ||
@@ -32,6 +33,7 @@ export const EventWorkingListsUpdateTrigger = ({
forceUpdateOnMount={forceUpdateOnMount}
onLoadView={injectCustomUpdateContextToLoadList}
onUpdateList={injectCustomUpdateContextToUpdateList}
+ onChangeTemplate={onChangeTemplate}
/>
);
};
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/eventWorkingListsUpdateTrigger.types.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/eventWorkingListsUpdateTrigger.types.js
index 5402dd4880..d98e4032ba 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/eventWorkingListsUpdateTrigger.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/UpdateTrigger/eventWorkingListsUpdateTrigger.types.js
@@ -9,6 +9,7 @@ type ExtractedProps = $ReadOnly<{|
lastTransactionOnListDataRefresh?: number,
onLoadView: Function,
onUpdateList: Function,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
|}>;
type RestProps = $Rest void,
|}>;
diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/epics/templates.epics.js b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/epics/templates.epics.js
index f99d504f20..d164a9ff24 100644
--- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/epics/templates.epics.js
+++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingLists/epics/templates.epics.js
@@ -18,6 +18,7 @@ import {
} from '../../WorkingListsCommon';
import { getTemplates } from './getTemplates';
import { SINGLE_EVENT_WORKING_LISTS_TYPE } from '../constants';
+import { getLocationQuery } from '../../../../utils/routing';
export const retrieveTemplatesEpic = (
action$: InputObservable,
@@ -69,6 +70,7 @@ export const updateTemplateEpic = (
criteria: eventQueryCriteria,
programId,
storeId,
+ callBacks,
} }) => {
const eventFilterData = {
name,
@@ -90,6 +92,10 @@ export const updateTemplateEpic = (
const isActiveTemplate =
store.value.workingListsTemplates[storeId].selectedTemplateId === id;
+ if (callBacks?.onChangeTemplate) {
+ callBacks.onChangeTemplate(id);
+ }
+
return updateTemplateSuccess(
id,
eventQueryCriteria, {
@@ -145,6 +151,7 @@ export const addTemplateEpic = (
clientId,
programId,
storeId,
+ callBacks,
} = action.payload;
const eventFilterData = {
@@ -160,7 +167,13 @@ export const addTemplateEpic = (
}).then((result) => {
const isActiveTemplate =
store.value.workingListsTemplates[storeId].selectedTemplateId === clientId;
- return addTemplateSuccess(result.response.uid, clientId, { storeId, isActiveTemplate });
+ const templateId = result.response.uid;
+
+ if (callBacks?.onChangeTemplate) {
+ callBacks.onChangeTemplate(templateId);
+ }
+
+ return addTemplateSuccess(templateId, clientId, { storeId, isActiveTemplate });
}).catch((error) => {
log.error(
errorCreator('could not add template')({
@@ -191,21 +204,28 @@ export const deleteTemplateEpic = (
action$.pipe(
ofType(workingListsCommonActionTypes.TEMPLATE_DELETE),
filter(({ payload: { workingListsType } }) => workingListsType === SINGLE_EVENT_WORKING_LISTS_TYPE),
- concatMap(({ payload: { template, storeId } }) => {
+ concatMap(({ payload: { template, storeId, callBacks } }) => {
const requestPromise = mutate({
resource: 'eventFilters',
id: template.id,
type: 'delete',
- }).then(() => deleteTemplateSuccess(template, storeId))
- .catch((error) => {
- log.error(
- errorCreator('could not delete template')({
- error,
- template,
- }),
- );
- return deleteTemplateError(template, storeId);
- });
+ }).then(() => {
+ const { programId } = getLocationQuery();
+
+ if (callBacks?.onChangeTemplate) {
+ callBacks.onChangeTemplate(`${programId}-default`);
+ }
+
+ return deleteTemplateSuccess(template, storeId);
+ }).catch((error) => {
+ log.error(
+ errorCreator('could not delete template')({
+ error,
+ template,
+ }),
+ );
+ return deleteTemplateError(template, storeId);
+ });
return from(requestPromise).pipe(
takeUntil(
diff --git a/src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/workingListsBase.types.js b/src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/workingListsBase.types.js
index 046c59d246..df0560372b 100644
--- a/src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/workingListsBase.types.js
+++ b/src/core_modules/capture-core/components/WorkingLists/WorkingListsBase/workingListsBase.types.js
@@ -216,6 +216,7 @@ export type InterfaceProps = $ReadOnly<{|
onUpdateFilter: UpdateFilter,
onUpdateList: UpdateList,
onUpdateTemplate?: UpdateTemplate,
+ onChangeTemplate?: (selectedTemplateId?: string) => void,
orgUnitId: string,
programId: string,
rowIdKey: string,