Skip to content

Commit

Permalink
OHRI-2256 Fix breaking cohort list due to infinite loop
Browse files Browse the repository at this point in the history
  • Loading branch information
CynthiaKamau committed Jun 26, 2024
1 parent 20f8a30 commit 69ab655
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 314 deletions.
39 changes: 39 additions & 0 deletions packages/esm-commons-lib/src/api.resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,42 @@ function isInvalidValue(value) {
}
return false;
}

export async function getCohortList(
cohortUuid: string,
queryParams?: string[],
isReportingCohort?: boolean,
encounterType?: string,
) {
const params = queryParams ? queryParams.join('&') : '';
const cohortMembersUrl = params
? `reportingrest/cohort/${cohortUuid}?${params}`
: `reportingrest/cohort/${cohortUuid}`;
const cohortUrl = `cohortm/cohort?v=custom:(uuid,name,voided)${cohortUuid ? `&cohortType=${cohortUuid}` : ''}`;
const url = isReportingCohort ? cohortMembersUrl : cohortUrl;

const { data } = await openmrsFetch(BASE_WS_API_URL + url);

if (data?.members || data?.cohortMembers) {
return Promise.all(
data.members.map((member) => {
return openmrsFetch(
`/ws/rest/v1/encounter?encounterType=${encounterType}&patient=${member.uuid}&v=${encounterRepresentation}`,
).then(({ data }) => {
if (data.results.length) {
const sortedEncounters = data.results.sort(
(firstEncounter, secondEncounter) =>
new Date(secondEncounter.encounterDatetime).getTime() -
new Date(firstEncounter.encounterDatetime).getTime(),
);
return sortedEncounters[0];
}

return null;
});
}),
);
} else {
return data;
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { attach, detach, ExtensionSlot } from '@openmrs/esm-framework';
import {
fetchPatientLastEncounter,
fetchPatientsFinalHIVStatus,
getCohort,
getReportingCohortMembers,
getCohortList,
} from '../../api.resource';
import dayjs from 'dayjs';
import { TableEmptyState } from '../empty-state/table-empty-state.component';
import { DataTableSkeleton } from '@carbon/react';
import { basePath } from '../../constants';
import { useTranslation } from 'react-i18next';
import { useFormsJson } from '../../hooks/useFormsJson';
import { columns, consolidatatePatientMeta, filterPatientsByName, type PatientListColumn } from './helpers';
import { columns, consolidatatePatientMeta, type PatientListColumn } from './helpers';

import styles from './cohort-patient-list.scss';
import { PatientTable } from '../patient-table/patient-table.component';

interface CohortPatientListProps {
cohortId: string;
Expand Down Expand Up @@ -59,24 +57,47 @@ export const CohortPatientList: React.FC<CohortPatientListProps> = ({
viewTptPatientProgramSummary,
}) => {
const [isLoading, setIsLoading] = useState(true);
const [hasLoadedPatients, setHasLoadedPatients] = useState(false);
const [loadedEncounters, setLoadedEncounters] = useState(false);
const [loadedHIVStatuses, setLoadedHIVStatuses] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [patientsCount, setPatientsCount] = useState(0);
const [searchTerm, setSearchTerm] = useState(null);
const [counter, setCounter] = useState(0);
const [filteredResults, setFilteredResults] = useState([]);
const [loadedExtraEncounters, setLoadedExtraEncounters] = useState(false);
const [extraEncounters, setExtraEncounters] = useState([]);
const [paginatedPatients, setPaginatedPatients] = useState([]);
const [allPatients, setAllPatients] = useState([]);
const [rawCohortData, setRawCohortData] = useState<{ location?: any; members: any[] }>({ members: [] });
const [isLoadingCohorts, setIsLoadingCohorts] = useState(true);
const columnAtLastIndex = 'actions';
const forms = useMemo(() => [launchableForm.name], [launchableForm]);
const { formsJson, isLoading: isLoadingFormsJson } = useFormsJson(forms);

useEffect(() => {
getCohortList(cohortId, queryParams, isReportingCohort, associatedEncounterType)
.then((data) => {

if(data) {
const mappedPatients = data.map((mappedData) => {
const patient = constructPatient(mappedData.patient);
patient.url = `${window.spaBase}/patient/${patient?.uuid}/chart/`;
return {
...patient,
...consolidatatePatientMeta(mappedData, formsJson[0], {
isDynamicCohort: isReportingCohort,
location: mappedData.location,
encounterType: associatedEncounterType,
moduleName,
launchableFormProps: launchableForm,
addPatientToListOptions: {
...addPatientToListOptions,
displayText: t('moveToListSideNav', 'Move to list'),
},
viewTptPatientProgramSummary,
viewPatientProgramSummary,
}),
};
})
setAllPatients(mappedPatients);
setIsLoading(false);
console.log("==component fetch data", mappedPatients)

}
}).catch((error) => {
console.error('Error fetching cohort data:', error);
setIsLoading(false);
})
}, []);

const constructPatient = useCallback(
(rawPatient) => {
Expand Down Expand Up @@ -108,143 +129,7 @@ export const CohortPatientList: React.FC<CohortPatientListProps> = ({

const { t } = useTranslation();

const updatePatientTable = (fullDataset, start, itemCount) => {
let currentRows = [];

for (let i = start; i < start + itemCount; i++) {
if (i < fullDataset.length) {
currentRows.push(fullDataset[i]);
}
}
setPaginatedPatients(currentRows);
};

useEffect(() => {
if (!isReportingCohort) {
getCohort(cohortId, 'full').then((results) => {
setRawCohortData({ members: results.cohortMembers, location: results.location });
setIsLoadingCohorts(false);
});
} else {
getReportingCohortMembers(cohortId, queryParams).then((results) => {
setRawCohortData({ members: results.map((result) => result.data) });
setIsLoadingCohorts(false);
});
}
}, [cohortId, isReportingCohort, queryParams]);

useEffect(() => {
if (!isLoadingCohorts && !isLoadingFormsJson) {
const patients = rawCohortData.members.map((member) => {
const patient = constructPatient(member);
member['patientUrl'] = patient.url;
return {
...patient,
...consolidatatePatientMeta(member, formsJson[0], {
isDynamicCohort: isReportingCohort,
location: rawCohortData.location,
encounterType: associatedEncounterType,
moduleName,
launchableFormProps: launchableForm,
addPatientToListOptions: {
...addPatientToListOptions,
displayText: t('moveToListSideNav', 'Move to list'),
},
viewTptPatientProgramSummary,
viewPatientProgramSummary,
}),
};
});
setAllPatients(patients);
updatePatientTable(patients, 0, pageSize);
setHasLoadedPatients(true);
setIsLoading(false);
}
}, [
rawCohortData,
formsJson,
isLoadingCohorts,
isLoadingFormsJson,
pageSize,
constructPatient,
isReportingCohort,
associatedEncounterType,
moduleName,
launchableForm,
addPatientToListOptions,
t,
viewTptPatientProgramSummary,
viewPatientProgramSummary,
]);

useEffect(() => {
if (hasLoadedPatients && allPatients.length) {
Promise.all(allPatients.map((patient) => fetchPatientLastEncounter(patient.uuid, associatedEncounterType))).then(
(results) => {
results.forEach((encounter, index) => {
allPatients[index].latestEncounter = encounter;
if (index == allPatients.length - 1) {
setAllPatients([...allPatients]);
setLoadedEncounters(true);
setIsLoading(false);
}
});
},
);
}
setPatientsCount(allPatients.length);
}, [allPatients, associatedEncounterType, hasLoadedPatients]);

useEffect(() => {
const fetchHivResults = excludeColumns ? !excludeColumns.includes('hivResult') : true;
if ((loadedEncounters || !associatedEncounterType) && !loadedHIVStatuses && fetchHivResults) {
Promise.all(allPatients.map((patient) => fetchPatientsFinalHIVStatus(patient.uuid))).then((results) => {
results.forEach((hivResult, index) => {
allPatients[index].hivResult = hivResult;
if (index == allPatients.length - 1) {
setAllPatients([...allPatients]);
setLoadedHIVStatuses(true);
}
});
});
}
}, [allPatients, associatedEncounterType, excludeColumns, loadedEncounters, loadedHIVStatuses]);

const pagination = useMemo(() => {
return {
usePagination: true,
currentPage: currentPage,
onChange: ({ pageSize, page }) => {
let startOffset = (page - 1) * pageSize;
updatePatientTable(allPatients, startOffset, pageSize);

setCurrentPage(page);
setPageSize(pageSize);
return null;
},
pageSize: pageSize,
totalItems: patientsCount,
};
}, [currentPage, pageSize, patientsCount, allPatients]);

const handleSearch = useCallback(
(searchTerm) => {
setSearchTerm(searchTerm);
const filtrate = filterPatientsByName(searchTerm, allPatients);
setFilteredResults(filtrate);
return true;
},
[allPatients],
);

useEffect(() => {
attach(cohortSlotName, 'patient-table');
return () => {
detach(cohortSlotName, 'patient-table');
};
});

const state = useMemo(() => {
const finalColumns = useMemo(() => {
let filteredColumns = [...columns];
if (excludeColumns) {
filteredColumns = columns.filter((c) => !excludeColumns.includes(c.key));
Expand All @@ -266,80 +151,29 @@ export const CohortPatientList: React.FC<CohortPatientListProps> = ({
filteredColumns.push(column);
}

return {
patients: searchTerm ? filteredResults : paginatedPatients,
columns: filteredColumns,
isLoading,
search: {
placeHolder: t('searchClientList', 'Search client list'),
onSearch: (searchTerm) => {
if (!searchTerm) {
// clear value
setSearchTerm('');
}
},
currentSearchTerm: searchTerm,
otherSearchProps: {
onKeyDown: (e) => {
if (e.keyCode == 13) {
handleSearch(e.target.value);
}
},
autoFocus: true,
},
},
pagination: pagination,
autoFocus: true,
};
return filteredColumns
}, [
searchTerm,
filteredResults,
paginatedPatients,
handleSearch,
pagination,
isLoading,
t,
excludeColumns,
otherColumns,
columns,
]);

useEffect(() => {
setCounter(counter + 1);
}, [counter, state]);

useEffect(() => {
if (allPatients.length && extraAssociatedEncounterTypes && !loadedExtraEncounters) {
allPatients.forEach((patient) => {
extraAssociatedEncounterTypes.forEach((encType) => {
extraEncounters.push(fetchPatientLastEncounter(patient.uuid, encType));
});
});

Promise.all(extraEncounters).then((results) => {
results.forEach((encounter, index) => {
const idx = allPatients.findIndex((patient) => patient.uuid === encounter?.patient.uuid);
if (idx !== -1) {
allPatients[idx].latestExtraEncounters = allPatients[idx].latestExtraEncounters?.concat(encounter) ?? [
encounter,
];
}
});
setLoadedExtraEncounters(true);
});
}
}, [allPatients, extraAssociatedEncounterTypes, extraEncounters, loadedExtraEncounters]);

return (
<div className={styles.table1}>
{isLoading ? (
<DataTableSkeleton rowCount={5} />
) : !paginatedPatients.length ? (
) : !allPatients.length ? (
<TableEmptyState
tableHeaders={state.columns}
tableHeaders={columns}
message={t('noPatientSidenav', 'There are no patients in this list.')}
/>
) : (
<ExtensionSlot name={cohortSlotName} state={state} key={counter} />
<PatientTable
columns={finalColumns}
isFetching={isLoading}
isLoading={isLoading}
patients={allPatients}
/>
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function consolidatatePatientMeta(rawPatientMeta, form, config: PatientMe
viewPatientProgramSummary,
viewTptPatientProgramSummary,
} = config;
const patientUuid = !isDynamicCohort ? rawPatientMeta.patient.uuid : rawPatientMeta.person.uuid;
const patientUuid = !isDynamicCohort ? rawPatientMeta.patient.uuid : rawPatientMeta.patient.person.uuid;
dayjs.extend(localizedFormat);
dayjs.extend(relativeTime);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function OHRIPatientListTabs({ patientListConfigs, moduleName }) {
<Tabs type="container" className={styles.tabContainer}>
<TabList contained>
{patientListConfigs.map((config, index) => {
return <Tab key={index}>{config.label}</Tab>;
return <Tab key={index} id={config.cohortId}>{config.label}</Tab>;
})}
</TabList>
<TabPanels>
Expand Down
Loading

0 comments on commit 69ab655

Please sign in to comment.