diff --git a/i18n/en.pot b/i18n/en.pot index a555e6bc51..903fce47f5 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: 2026-09-08T15:17:29.259Z\n" -"PO-Revision-Date: 2026-09-08T15:17:29.262Z\n" +"POT-Creation-Date: 2026-09-08T15:49:17.652Z\n" +"PO-Revision-Date: 2026-09-08T15:49:17.653Z\n" msgid "The application could not be loaded." msgstr "The application could not be loaded." diff --git a/src/core_modules/capture-core/components/EventOverflowMenu/MenuItems/CompletionMenuItem.tsx b/src/core_modules/capture-core/components/EventOverflowMenu/MenuItems/CompletionMenuItem.tsx index de35726c0b..69899a51e1 100644 --- a/src/core_modules/capture-core/components/EventOverflowMenu/MenuItems/CompletionMenuItem.tsx +++ b/src/core_modules/capture-core/components/EventOverflowMenu/MenuItems/CompletionMenuItem.tsx @@ -2,13 +2,14 @@ import React from 'react'; import i18n from '@dhis2/d2-i18n'; import log from 'loglevel'; import { MenuItem, IconCheckmark16, IconUndo16 } from '@dhis2/ui'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useAlert, useDataEngine } from '@dhis2/app-runtime'; import { useDispatch } from 'react-redux'; import { errorCreator } from 'capture-core-utils'; import { statusTypes as eventStatuses } from 'capture-core/events/statusTypes'; -import { CompleteModal } from '../../DataEntries/common/trackerEvent/withAskToCompleteEnrollment/CompleteModal'; +import { removeEventChangelogQueries } from '../../WidgetsChangelog'; import { statusTypes as enrollmentStatuses } from '../../../enrollment'; +import { CompleteModal } from '../../DataEntries/common/trackerEvent/withAskToCompleteEnrollment/CompleteModal'; import { updateEnrollmentAndEvents, commitEnrollmentAndEvents, @@ -64,6 +65,7 @@ export const CompletionMenuItem = ({ onAskCompleteEnrollment, }: MenuItemProps) => { const dataEngine = useDataEngine(); + const queryClient = useQueryClient(); const { show: showError } = useAlert(({ message }) => message, { critical: true }); const isCompleted = eventStatus === eventStatuses.COMPLETED; @@ -78,7 +80,10 @@ export const CompletionMenuItem = ({ log.error(errorCreator('An error occurred when updating event status')({ error, eventId, newStatus })); onError?.(); }, - onSuccess: () => onSuccess?.(newStatus), + onSuccess: () => { + removeEventChangelogQueries(queryClient, eventId); + onSuccess?.(newStatus); + }, }, ); @@ -122,6 +127,7 @@ export const CompleteMenuItemModal = ({ }: ModalProps) => { const dataEngine = useDataEngine(); const dispatch = useDispatch(); + const queryClient = useQueryClient(); const { show: showError } = useAlert(({ message }) => message, { critical: true }); const handleError = (error: unknown) => { @@ -134,7 +140,10 @@ export const CompleteMenuItemModal = ({ { onMutate: () => onMutate?.(eventStatuses.COMPLETED), onError: (error) => { handleError(error); onError?.(); }, - onSuccess: () => onSuccess?.(eventStatuses.COMPLETED), + onSuccess: () => { + removeEventChangelogQueries(queryClient, eventId); + onSuccess?.(eventStatuses.COMPLETED); + }, }, ); @@ -165,6 +174,7 @@ export const CompleteMenuItemModal = ({ }, onSuccess: () => { dispatch(commitEnrollmentAndEvents()); + removeEventChangelogQueries(queryClient, eventId); onSuccess?.(eventStatuses.COMPLETED); }, }, diff --git a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.tsx b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.tsx index bfa61fbf85..0b2af5d2a9 100644 --- a/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.tsx +++ b/src/core_modules/capture-core/components/Pages/Enrollment/EnrollmentPageDefault/EnrollmentPageDefault.container.tsx @@ -5,7 +5,9 @@ import { errorCreator } from 'capture-core-utils'; import { formatMomentEn } from 'capture-core-utils/date'; import { useDispatch, useSelector } from 'react-redux'; import { useTimeZoneConversion } from '@dhis2/app-runtime'; +import { useQueryClient } from '@tanstack/react-query'; import type { ApiEnrollmentEvent } from 'capture-core-utils/types/api-types'; +import { removeEventChangelogQueries } from '../../../WidgetsChangelog'; import { commitEnrollmentAndEvents, EnrollmentAccessProvider, @@ -50,6 +52,7 @@ import { useHideWidgetByRuleLocations } from '../../../../hooks'; export const EnrollmentPageDefault = () => { const { navigate } = useNavigate(); const dispatch = useDispatch(); + const queryClient = useQueryClient(); const { fromClientDate } = useTimeZoneConversion(); const { status: widgetEnrollmentStatus } = useSelector(({ widgetEnrollment }: any) => widgetEnrollment); const { enrollmentId, programId, teiId, orgUnitId } = useLocationQuery(); @@ -181,7 +184,8 @@ export const EnrollmentPageDefault = () => { ); const onUpdateEnrollmentStatusSuccess = useCallback(() => { dispatch(commitEnrollmentAndEvents()); - }, [dispatch]); + removeEventChangelogQueries(queryClient); + }, [dispatch, queryClient]); const onBackToMainPage = useCallback(() => { navigate(`/?${buildUrlQueryString({ orgUnitId, programId })}`); diff --git a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.tsx b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.tsx index 201e616deb..760e891ff4 100644 --- a/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.tsx +++ b/src/core_modules/capture-core/components/Pages/EnrollmentEditEvent/EnrollmentEditEventPage.container.tsx @@ -42,8 +42,7 @@ import { DataStoreKeyByPage, useEnrollmentPageLayout } from '../common/Enrollmen import { DefaultPageLayout } from './PageLayout/DefaultPageLayout.constants'; import { rollbackAssignee, setAssignee } from './EnrollmentEditEventPage.actions'; import { convertClientToServer, convertServerToClient } from '../../../converters'; -import { CHANGELOG_ENTITY_TYPES } from '../../WidgetsChangelog'; -import { ReactQueryAppNamespace } from '../../../utils/reactQueryHelpers'; +import { removeEventChangelogQueries } from '../../WidgetsChangelog'; import { statusTypes } from '../../../enrollment'; import { cancelEditEventDataEntry } from '../../WidgetEventEdit/EditEventDataEntry/editEventDataEntry.actions'; import { setCurrentDataEntry } from '../../DataEntry/actions/dataEntry.actions'; @@ -246,8 +245,7 @@ const EnrollmentEditEventPageWithContextPlain = ({ }, [dispatch, navigate, orgUnitId, enrollmentId, eventId]); const onSaveExternal = useCallback(() => { - const queryKey = [ReactQueryAppNamespace, 'changelog', CHANGELOG_ENTITY_TYPES.EVENT, eventId]; - queryClient.removeQueries(queryKey); + removeEventChangelogQueries(queryClient, eventId); navigate(`enrollment?${buildUrlQueryString({ orgUnitId, enrollmentId })}`); }, [navigate, orgUnitId, enrollmentId, eventId, queryClient]); diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.tsx b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.tsx index 3dd42407a5..b6098cf6ab 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.tsx +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.component.tsx @@ -20,8 +20,7 @@ import { useCoreOrgUnit } from '../../../../metadataRetrieval/coreOrgUnit'; import { NoticeBox } from '../../../NoticeBox'; import { EventChangelogWrapper } from '../../../WidgetEventEdit/EventChangelogWrapper'; import { OverflowButton } from '../../../Buttons'; -import { ReactQueryAppNamespace } from '../../../../utils/reactQueryHelpers'; -import { CHANGELOG_ENTITY_TYPES } from '../../../WidgetsChangelog'; +import { removeEventChangelogQueries } from '../../../WidgetsChangelog'; import { useCategoryCombinations } from '../../../DataEntryDhis2Helpers/AOC/useCategoryCombinations'; import { useMetadataForProgramStage } from '../../../DataEntries/common/ProgramStage/useMetadataForProgramStage'; import { useProgramExpiryForUser, useEventEditPermissions } from '../../../../hooks'; @@ -64,7 +63,6 @@ const EventDetailsSectionPlain = (props: PlainProps & { classes: any }) => { const { classes, eventId, - eventData, onOpenEditEvent, isEditEventPage, programStage, @@ -74,7 +72,9 @@ const EventDetailsSectionPlain = (props: PlainProps & { classes: any }) => { showEditButton, ...passOnProps } = props; - const orgUnitId = useSelector((state: any) => state.viewEventPage.loadedValues?.orgUnit?.id); + const orgUnitId = useSelector((state: { viewEventPage: { loadedValues: any } }) => + state.viewEventPage.loadedValues?.orgUnit?.id); + const loadedValues = useSelector((state: { viewEventPage: { loadedValues: any } }) => state.viewEventPage.loadedValues); const { formFoundation } = useMetadataForProgramStage({ programId }); const { orgUnit, error } = useCoreOrgUnit(orgUnitId); const { programCategory, isLoading } = useCategoryCombinations(programId); @@ -85,14 +85,13 @@ const EventDetailsSectionPlain = (props: PlainProps & { classes: any }) => { const { canToggleCompletion } = useEventEditPermissions({ programId, stage: programStage, - eventStatus: eventData?.eventContainer?.event?.status, - occurredAtClient: convertFormToClient(eventData?.dataEntryValues?.occurredAt, dataElementTypes.DATE) as string, - completedAtClient: eventData?.eventContainer?.event?.completedAt, + eventStatus: loadedValues?.eventContainer?.event?.status, + occurredAtClient: convertFormToClient(loadedValues?.dataEntryValues?.occurredAt, dataElementTypes.DATE) as string, + completedAtClient: loadedValues?.eventContainer?.event?.completedAt, }); const onSaveExternal = useCallback(() => { - const queryKey = [ReactQueryAppNamespace, 'changelog', CHANGELOG_ENTITY_TYPES.EVENT, eventId]; - queryClient.removeQueries(queryKey); + removeEventChangelogQueries(queryClient, eventId); onBackToAllEvents(); }, [eventId, queryClient, onBackToAllEvents]); @@ -190,7 +189,6 @@ const EventDetailsSectionPlain = (props: PlainProps & { classes: any }) => { diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.ts b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.ts index 52962ab10f..3ac04acd35 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.ts +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.container.ts @@ -7,7 +7,6 @@ import { const mapStateToProps = (state: any, ownProps: any) => ({ isEditEventPage: state.viewEventPage.eventDetailsSection && state.viewEventPage.eventDetailsSection.showEditEvent, eventId: state.viewEventPage.eventId, - eventData: state.viewEventPage.loadedValues || {}, programId: state.currentSelections.programId, ...ownProps, }); diff --git a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.types.ts b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.types.ts index 337cb058f4..8f675676ad 100644 --- a/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.types.ts +++ b/src/core_modules/capture-core/components/Pages/ViewEvent/EventDetailsSection/EventDetailsSection.types.ts @@ -3,7 +3,6 @@ import { ProgramStage } from 'capture-core/metaData'; export type PlainProps = { isEditEventPage?: boolean; eventId: string; - eventData: any; onOpenEditEvent: (orgUnit: any, programCategory?: any) => void; programStage: ProgramStage; eventAccess: { read: boolean, write: boolean }; diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.tsx b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.tsx index da9ff11032..ddfed141fe 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.tsx +++ b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.component.tsx @@ -5,7 +5,7 @@ import { dataElementTypes } from '../../../metaData'; import type { Props } from './EventChangelogWrapper.types'; import { WidgetEventChangelog } from '../../WidgetsChangelog'; -export const EventChangelogWrapper = ({ formFoundation, eventId, eventData, ...passOnProps }: Props) => { +export const EventChangelogWrapper = ({ formFoundation, eventId, ...passOnProps }: Props) => { const dataItemDefinitions = useMemo(() => { const elements = formFoundation.getElements(); const contextLabels = formFoundation.getLabels(); @@ -62,7 +62,6 @@ export const EventChangelogWrapper = ({ formFoundation, eventId, eventData, ...p ); diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.ts b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.ts index a5c244f7e9..2ef4d0c471 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.ts +++ b/src/core_modules/capture-core/components/WidgetEventEdit/EventChangelogWrapper/EventChangelogWrapper.types.ts @@ -8,5 +8,4 @@ type PassOnProps = { export type Props = PassOnProps & { formFoundation: RenderFoundation, - eventData: Record, }; diff --git a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.tsx b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.tsx index b15639cb14..8cbdf41980 100644 --- a/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.tsx +++ b/src/core_modules/capture-core/components/WidgetEventEdit/WidgetEventEdit.container.tsx @@ -102,7 +102,7 @@ const WidgetEventEditPlain = ({ const { currentPageMode } = useEnrollmentEditEventPageMode(eventStatus); const [changeLogIsOpen, setChangeLogIsOpen] = useState(false); // "Edit event"-button depends on loadedValues. Delay rendering component until loadedValues has been initialized. - const loadedValues = useSelector((state: { viewEventPage: { loadedValues: any } }) => state.viewEventPage.loadedValues); + const loadedValues = useSelector((state: any) => state.viewEventPage.loadedValues); const orgUnit = loadedValues?.orgUnit; const occurredAt = loadedValues?.dataEntryValues?.occurredAt; const completedAt = loadedValues?.eventContainer?.event?.completedAt; @@ -205,7 +205,6 @@ const WidgetEventEditPlain = ({ isOpen setIsOpen={setChangeLogIsOpen} eventId={loadedValues.eventContainer.id} - eventData={loadedValues.eventContainer.values} formFoundation={formFoundation} /> )} diff --git a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.tsx b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.tsx index d515fa1892..6b8a1f80f7 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.tsx +++ b/src/core_modules/capture-core/components/WidgetProfile/OverflowMenu/TrackedEntityChangelogWrapper/TrackedEntityChangelogWrapper.component.tsx @@ -68,7 +68,6 @@ export const TrackedEntityChangelogWrapper = ({ close={() => setIsOpen(false)} programId={programAPI.id} dataItemDefinitions={dataItemDefinitions} - trackedEntityData={transformedTrackedEntityData} /> ); }; diff --git a/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.tsx b/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.tsx index fbfcd015a9..9493be162d 100644 --- a/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.tsx +++ b/src/core_modules/capture-core/components/WidgetProfile/WidgetProfile.component.tsx @@ -19,8 +19,7 @@ import { useTeiDisplayName, } from './hooks'; import { DataEntry, dataEntryActionTypes, TEI_MODAL_STATE, convertClientToView } from './DataEntry'; -import { ReactQueryAppNamespace } from '../../utils/reactQueryHelpers'; -import { CHANGELOG_ENTITY_TYPES } from '../WidgetsChangelog'; +import { removeTrackedEntityChangelogQueries } from '../WidgetsChangelog'; import { OverflowMenu } from './OverflowMenu'; import { useDataEntryFormConfig, @@ -142,7 +141,7 @@ const WidgetProfilePlain = ({ }), [clientAttributesWithSubvalues]); const onSaveExternal = useCallback(() => { - queryClient.removeQueries([ReactQueryAppNamespace, 'changelog', CHANGELOG_ENTITY_TYPES.TRACKED_ENTITY, teiId]); + removeTrackedEntityChangelogQueries(queryClient, teiId); }, [queryClient, teiId]); useEffect(() => { diff --git a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/SkipAction/SkipAction.tsx b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/SkipAction/SkipAction.tsx index 2b0706687a..c769d62db2 100644 --- a/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/SkipAction/SkipAction.tsx +++ b/src/core_modules/capture-core/components/WidgetStagesAndEvents/Stages/Stage/StageDetail/EventRow/SkipAction/SkipAction.tsx @@ -5,11 +5,12 @@ import { MenuItem, IconRedo16, } from '@dhis2/ui'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useAlert, useDataEngine } from '@dhis2/app-runtime'; import { errorCreator } from 'capture-core-utils'; import type { ApiEnrollmentEvent } from 'capture-core-utils/types/api-types'; import { statusTypes as eventStatuses } from 'capture-core/events/statusTypes'; +import { removeEventChangelogQueries } from '../../../../../../WidgetsChangelog'; import { DirectionalArrow } from '../../../../../../../utils/rtl'; type Props = { @@ -28,6 +29,7 @@ export const SkipAction = ({ onUpdateEventStatus, }: Props) => { const dataEngine = useDataEngine(); + const queryClient = useQueryClient(); const { show: showError } = useAlert( ({ message }) => message, { critical: true }, @@ -55,6 +57,9 @@ export const SkipAction = ({ return { previousStatus }; }, + onSuccess: () => { + removeEventChangelogQueries(queryClient, eventId); + }, onError: (error: unknown, payload: { status: string }, context?: { previousStatus: string }) => { showError({ message: i18n.t('An error occurred when updating event status') }); log.error(errorCreator('An error occurred when updating event status')({ error, payload, context })); @@ -75,7 +80,7 @@ export const SkipAction = ({ icon={} label={i18n.t('Unskip')} onClick={() => handleMenuItemClick(eventStatuses.SCHEDULE)} - suffix="" + suffix={null} /> ); } @@ -86,7 +91,7 @@ export const SkipAction = ({ icon={} label={i18n.t('Skip')} onClick={() => handleMenuItemClick(eventStatuses.SKIPPED)} - suffix="" + suffix={null} /> ); }; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.tsx b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.tsx index 0db8eb304b..2e95903a55 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.tsx +++ b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetEventChangelog/WidgetEventChangelog.tsx @@ -4,7 +4,6 @@ import { Changelog, CHANGELOG_ENTITY_TYPES } from '../common/Changelog'; type Props = { eventId: string; - eventData: Record; dataItemDefinitions: ItemDefinitions; isOpen: boolean; setIsOpen: (value: boolean | ((prev: boolean) => boolean)) => void; @@ -12,7 +11,6 @@ type Props = { export const WidgetEventChangelog = ({ eventId, - eventData, setIsOpen, ...passOnProps }: Props) => ( @@ -20,7 +18,6 @@ export const WidgetEventChangelog = ({ {...passOnProps} close={() => setIsOpen(false)} entityId={eventId} - entityData={eventData} entityType={CHANGELOG_ENTITY_TYPES.EVENT} /> ); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.tsx b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.tsx index 114e0daf26..07b771793d 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.tsx +++ b/src/core_modules/capture-core/components/WidgetsChangelog/WidgetTrackedEntityChangelog/WidgetTrackedEntityChangelog.tsx @@ -8,20 +8,17 @@ type Props = { dataItemDefinitions: ItemDefinitions; isOpen: boolean; close: () => void; - trackedEntityData: Record; }; export const WidgetTrackedEntityChangelog = ({ teiId, programId, close, - trackedEntityData, ...passOnProps }: Props) => ( ; entityType: typeof CHANGELOG_ENTITY_TYPES[keyof typeof CHANGELOG_ENTITY_TYPES]; isOpen: boolean; close: () => void; @@ -17,7 +16,6 @@ type Props = { export const Changelog = ({ entityId, - entityData, entityType, programId, isOpen, @@ -38,8 +36,10 @@ export const Changelog = ({ setSortDirection, filterValue, setFilterValue, + filterParam, attributeToFilterBy, setAttributeToFilterBy, + dataUpdatedAt: rawDataUpdatedAt, } = useChangelogData({ entityId, entityType, programId }); const { @@ -49,12 +49,14 @@ export const Changelog = ({ rawRecords, dataItemDefinitions, entityId, - entityData, entityType, programId, sortDirection, + columnToSortBy, + filterParam, page, pageSize, + rawDataUpdatedAt, }); const loading = (isChangelogLoading || isProcessingLoading); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.ts b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.ts index 2cce8aa7d5..4d2d9163ae 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.ts +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/index.ts @@ -1,2 +1,3 @@ export { useChangelogData } from './useChangelogData'; export { useListDataValues } from './useListDataValues'; +export { useCurrentEntityValues } from './useCurrentEntityValues'; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.ts b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.ts index 85e8797145..76cfb6556d 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.ts +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useChangelogData.ts @@ -40,7 +40,7 @@ export const useChangelogData = ({ entityId, entityType, programId }: Props) => ? undefined : `${columnToSortBy}:${sortDirection}`; - const { data, isInitialLoading, isError } = useApiDataQuery( + const { data, isInitialLoading, isError, dataUpdatedAt } = useApiDataQuery( [ 'changelog', entityType, @@ -74,9 +74,11 @@ export const useChangelogData = ({ entityId, entityType, programId }: Props) => setAttributeToFilterBy, filterValue, setFilterValue, + filterParam, page, pageSize, isLoading: isInitialLoading, isError, + dataUpdatedAt, }; }; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useCurrentEntityValues.ts b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useCurrentEntityValues.ts new file mode 100644 index 0000000000..a45b91b9b5 --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useCurrentEntityValues.ts @@ -0,0 +1,49 @@ +import { useApiDataQuery } from '../../../../utils/reactQueryHelpers'; +import { CHANGELOG_ENTITY_TYPES, QUERY_KEYS_BY_ENTITY_TYPE } from '../Changelog/Changelog.constants'; + +type Props = { + entityId: string; + entityType: typeof CHANGELOG_ENTITY_TYPES[keyof typeof CHANGELOG_ENTITY_TYPES]; + programId?: string; + enabled?: boolean; +}; + +export type CurrentValues = Record; + +const FIELDS_BY_ENTITY_TYPE = Object.freeze({ + [CHANGELOG_ENTITY_TYPES.EVENT]: 'dataValues[dataElement,value]', + [CHANGELOG_ENTITY_TYPES.TRACKED_ENTITY]: 'attributes[attribute,value]', +}); + +const NO_VALUES: CurrentValues = Object.freeze({}); + +export const useCurrentEntityValues = ({ entityId, entityType, programId, enabled = true }: Props) => { + const { data, isInitialLoading, dataUpdatedAt } = useApiDataQuery( + ['changelog', entityType, entityId, 'currentValues', { programId }], + { + resource: `tracker/${QUERY_KEYS_BY_ENTITY_TYPE[entityType]}/${entityId}`, + params: { + program: programId, + fields: FIELDS_BY_ENTITY_TYPE[entityType], + }, + }, + { + enabled: !!entityId && enabled, + select: (response: any) => { + const items = entityType === CHANGELOG_ENTITY_TYPES.EVENT + ? response?.dataValues + : response?.attributes; + return (items ?? []).reduce((acc, { dataElement, attribute, value }) => { + acc[dataElement ?? attribute] = value; + return acc; + }, {}); + }, + }, + ); + + return { + currentValues: data ?? NO_VALUES, + isLoading: isInitialLoading, + dataUpdatedAt, + }; +}; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.ts b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.ts index d9606dcc0a..2a8d0da53f 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.ts +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/hooks/useListDataValues.ts @@ -11,24 +11,27 @@ import { convertServerToClient } from '../../../../converters'; import { convert as convertClientToList } from '../../../../converters/clientToList'; import { RECORD_TYPE, subValueGetterByElementType } from '../utils/getSubValueForChangelogData'; import { makeQuerySingleResource } from '../../../../utils/api'; +import { useCurrentEntityValues } from './useCurrentEntityValues'; type Props = { rawRecords: any; dataItemDefinitions: ItemDefinitions; entityId: string; - entityData: any; entityType: typeof CHANGELOG_ENTITY_TYPES[keyof typeof CHANGELOG_ENTITY_TYPES]; programId?: string; sortDirection: SortDirection; + columnToSortBy: string; + filterParam?: string; page: number; pageSize: number; + rawDataUpdatedAt: number; }; const fetchFormattedValues = async ({ rawRecords, dataItemDefinitions, entityId, - entityData, + currentValues, entityType, programId, absoluteApiPath, @@ -38,8 +41,7 @@ const fetchFormattedValues = async ({ if (!rawRecords) return []; const getItemDefinition = (change: Change) => { - const { dataElement, attribute, field } = change; - const fieldId = dataElement ?? attribute ?? field; + const fieldId = change.dataElement ?? change.attribute ?? change.field; if (!fieldId) { log.error('Could not find fieldId in change:', change); return null; @@ -89,12 +91,11 @@ const fetchFormattedValues = async ({ return null; }; + const isLatestValue = currentValues[metadataElement.id] === change.currentValue; + const [previousValueClient, currentValueClient] = await Promise.all([ change.previousValue ? getValue(change.previousValue, false) : null, - getValue( - change.currentValue, - entityData?.[change.attribute ?? change.dataElement]?.value === change.currentValue, - ), + getValue(change.currentValue, isLatestValue), ]); const { firstName, surname, username } = createdBy; @@ -123,14 +124,34 @@ export const useListDataValues = ({ rawRecords, dataItemDefinitions, entityId, - entityData, entityType, programId, sortDirection, + columnToSortBy, + filterParam, page, pageSize, + rawDataUpdatedAt, }: Props) => { const dataEngine = useDataEngine(); + const hasFileOrImageField = useMemo( + () => Object.values(dataItemDefinitions ?? {}).some( + (definition: any) => + definition?.type === dataElementTypes.FILE_RESOURCE || + definition?.type === dataElementTypes.IMAGE, + ), + [dataItemDefinitions], + ); + const { + currentValues, + isLoading: isCurrentValuesLoading, + dataUpdatedAt: currentValuesUpdatedAt, + } = useCurrentEntityValues({ + entityId, + entityType, + programId, + enabled: hasFileOrImageField, + }); const { baseUrl, apiVersion } = useConfig(); const { fromServerDate } = useTimeZoneConversion(); const absoluteApiPath = buildUrl(baseUrl, `api/${apiVersion}`); @@ -146,7 +167,16 @@ export const useListDataValues = ({ entityType, entityId, 'formattedData', - { sortDirection, page, pageSize, programId, rawRecords }, + { + columnToSortBy, + sortDirection, + page, + pageSize, + programId, + filterParam, + rawDataUpdatedAt, + currentValuesUpdatedAt, + }, ]; const { data: processedRecords, isError, isInitialLoading } = useQuery( @@ -155,7 +185,7 @@ export const useListDataValues = ({ rawRecords, dataItemDefinitions, entityId, - entityData, + currentValues, entityType, programId, absoluteApiPath, @@ -163,12 +193,12 @@ export const useListDataValues = ({ fromServerDate, }), { - enabled: !!rawRecords && !!dataItemDefinitions && !!entityId && !!entityType, + enabled: !!rawRecords && !!dataItemDefinitions && !!entityId && !!entityType && !isCurrentValuesLoading, keepPreviousData: true, staleTime: Infinity, cacheTime: Infinity, }, ); - return { processedRecords, isLoading: isInitialLoading, isError }; + return { processedRecords, isLoading: isInitialLoading || isCurrentValuesLoading, isError }; }; diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/common/utils/removeChangelogQueries.ts b/src/core_modules/capture-core/components/WidgetsChangelog/common/utils/removeChangelogQueries.ts new file mode 100644 index 0000000000..fb7f824ade --- /dev/null +++ b/src/core_modules/capture-core/components/WidgetsChangelog/common/utils/removeChangelogQueries.ts @@ -0,0 +1,22 @@ +import type { QueryClient } from '@tanstack/react-query'; +import { ReactQueryAppNamespace } from '../../../../utils/reactQueryHelpers'; +import { CHANGELOG_ENTITY_TYPES } from '../Changelog/Changelog.constants'; + +type ChangelogEntityType = typeof CHANGELOG_ENTITY_TYPES[keyof typeof CHANGELOG_ENTITY_TYPES]; + +const removeChangelogQueries = ( + queryClient: QueryClient, + entityType: ChangelogEntityType, + entityId?: string, +) => { + const queryKey = entityId + ? [ReactQueryAppNamespace, 'changelog', entityType, entityId] + : [ReactQueryAppNamespace, 'changelog', entityType]; + queryClient.removeQueries(queryKey); +}; + +export const removeEventChangelogQueries = (queryClient: QueryClient, eventId?: string) => + removeChangelogQueries(queryClient, CHANGELOG_ENTITY_TYPES.EVENT, eventId); + +export const removeTrackedEntityChangelogQueries = (queryClient: QueryClient, teiId: string) => + removeChangelogQueries(queryClient, CHANGELOG_ENTITY_TYPES.TRACKED_ENTITY, teiId); diff --git a/src/core_modules/capture-core/components/WidgetsChangelog/index.ts b/src/core_modules/capture-core/components/WidgetsChangelog/index.ts index eb2bbde779..8d7cf09691 100644 --- a/src/core_modules/capture-core/components/WidgetsChangelog/index.ts +++ b/src/core_modules/capture-core/components/WidgetsChangelog/index.ts @@ -1,3 +1,7 @@ export { CHANGELOG_ENTITY_TYPES } from './common/Changelog'; export { WidgetEventChangelog } from './WidgetEventChangelog'; export { WidgetTrackedEntityChangelog } from './WidgetTrackedEntityChangelog'; +export { + removeEventChangelogQueries, + removeTrackedEntityChangelogQueries, +} from './common/utils/removeChangelogQueries'; diff --git a/src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/CompleteAction/hooks/useBulkCompleteEvents.ts b/src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/CompleteAction/hooks/useBulkCompleteEvents.ts index f15e825c93..57e41feef4 100644 --- a/src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/CompleteAction/hooks/useBulkCompleteEvents.ts +++ b/src/core_modules/capture-core/components/WorkingLists/EventWorkingListsCommon/EventBulkActions/Actions/CompleteAction/hooks/useBulkCompleteEvents.ts @@ -1,8 +1,9 @@ import { useCallback, useEffect, useMemo } from 'react'; import i18n from '@dhis2/d2-i18n'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useAlert, useDataEngine } from '@dhis2/app-runtime'; import { useApiDataQuery } from '../../../../../../../utils/reactQueryHelpers'; +import { removeEventChangelogQueries } from '../../../../../../WidgetsChangelog'; import { handleAPIResponse, REQUESTED_ENTITIES } from '../../../../../../../utils/api'; type Props = { @@ -23,6 +24,7 @@ export const useBulkCompleteEvents = ({ programId, }: Props) => { const dataEngine = useDataEngine(); + const queryClient = useQueryClient(); const { show: showAlert } = useAlert( ({ message }) => message, { critical: true }, @@ -87,9 +89,11 @@ export const useBulkCompleteEvents = ({ .find(errorReport => errorReport.uid === eventId), ); + removeEventChangelogQueries(queryClient); removeRowsFromSelection(validEventIds); onUpdateList(true); } else { + removeEventChangelogQueries(queryClient); onUpdateList(); setIsCompleteDialogOpen(false); } diff --git a/src/core_modules/capture-core/components/WorkingLists/TrackerWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/hooks/useCompleteBulkEnrollments.ts b/src/core_modules/capture-core/components/WorkingLists/TrackerWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/hooks/useCompleteBulkEnrollments.ts index aa7388e5bc..d464d8bc02 100644 --- a/src/core_modules/capture-core/components/WorkingLists/TrackerWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/hooks/useCompleteBulkEnrollments.ts +++ b/src/core_modules/capture-core/components/WorkingLists/TrackerWorkingLists/TrackedEntityBulkActions/Actions/CompleteAction/hooks/useCompleteBulkEnrollments.ts @@ -7,6 +7,7 @@ import { errorCreator } from 'capture-core-utils'; import { ReactQueryAppNamespace, useApiDataQuery } from '../../../../../../../utils/reactQueryHelpers'; import { handleAPIResponse, REQUESTED_ENTITIES } from '../../../../../../../utils/api'; import type { ProgramStage } from '../../../../../../../metaData'; +import { removeEventChangelogQueries } from '../../../../../../WidgetsChangelog'; type Props = { selectedRows: Record; @@ -154,7 +155,10 @@ export const useCompleteBulkEnrollments = ({ } = useMutation( ({ enrollments }: any) => importValidEnrollments({ dataEngine, enrollments }), { - onSuccess: () => { + onSuccess: (_, { enrollments }: any) => { + if (enrollments.some(e => e.events?.length > 0)) { + removeEventChangelogQueries(queryClient); + } onUpdateList(); removeQueries(); }, @@ -178,7 +182,10 @@ export const useCompleteBulkEnrollments = ({ } = useMutation( ({ enrollments }: any) => importValidEnrollments({ dataEngine, enrollments }), { - onSuccess: (serverResponse, { enrollments }) => { + onSuccess: (_, { enrollments }) => { + if (enrollments.some(e => e.events?.length > 0)) { + removeEventChangelogQueries(queryClient); + } const enrollmentIds = enrollments.map(enrollment => enrollment.trackedEntity); removeRowsFromSelection(enrollmentIds); removeQueries();