From 42759cde123d99435c16972a66cf936441ce83da Mon Sep 17 00:00:00 2001 From: CynthiaKamau Date: Thu, 20 Jun 2024 17:55:00 +0300 Subject: [PATCH] (refactor) Add workflows to pmtct summary in patient chart (#1868) --- packages/esm-commons-lib/src/api/api.ts | 2 +- .../src/components/card-summary/helpers.ts | 2 +- .../card-summary/summary-card.component.tsx | 30 +- packages/esm-commons-lib/src/constants.ts | 2 +- .../utils/encounter-list-config-builder.ts | 26 +- .../src/utils/encounter-list-utils.ts | 18 +- .../src/utils/schema-manipulation.ts | 2 +- .../src/utils/summary-card-config-builder.ts | 75 +++- .../esm-ohri-pmtct-app/src/config-schema.ts | 27 +- .../child-health/child-health-config.json | 22 +- .../child-health/child-health.component.tsx | 4 +- .../maternal-health-config.json | 68 ++-- .../maternal-health.component.tsx | 4 +- .../mch-summary/tabs/appointments-config.json | 24 ++ .../mch-summary/tabs/arv-therapy-config.json | 35 ++ .../tabs/current-pregnancy.component.tsx | 382 ++++-------------- .../hiv-exposed-family-summary-config.json | 58 +++ .../hiv-exposed-infant-summary-config.json | 45 +++ .../tabs/hiv-exposed-infant.component.tsx | 200 ++------- .../tabs/infant-summary-config.json | 38 ++ .../tabs/mother-previous-visit.json | 81 ++++ .../tabs/recent-pregnancy-config.json | 24 ++ 22 files changed, 624 insertions(+), 545 deletions(-) create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/appointments-config.json create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/arv-therapy-config.json create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-family-summary-config.json create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant-summary-config.json create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/infant-summary-config.json create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/mother-previous-visit.json create mode 100644 packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/recent-pregnancy-config.json diff --git a/packages/esm-commons-lib/src/api/api.ts b/packages/esm-commons-lib/src/api/api.ts index 82af7c46e..e312ada6c 100644 --- a/packages/esm-commons-lib/src/api/api.ts +++ b/packages/esm-commons-lib/src/api/api.ts @@ -257,7 +257,7 @@ export async function fetchMambaReportData(reportId: string) { } } -export function useDataFetch( +export function fetchEtlData( reportType: 'fetchMambaAncData' | 'MotherHivStatus', reportId?: string, patientUuid?: string, diff --git a/packages/esm-commons-lib/src/components/card-summary/helpers.ts b/packages/esm-commons-lib/src/components/card-summary/helpers.ts index 9efdcbf5a..f59901cce 100644 --- a/packages/esm-commons-lib/src/components/card-summary/helpers.ts +++ b/packages/esm-commons-lib/src/components/card-summary/helpers.ts @@ -1,5 +1,5 @@ import { fetchPatientLastEncounter } from '../../api/api'; export function fetchLatestEncountersOfTypes(patientUuid: string, encounterTypes: string[]) { - return Promise.all(encounterTypes.map((type) => fetchPatientLastEncounter(patientUuid, type))); + return Promise.all(encounterTypes?.map((type) => fetchPatientLastEncounter(patientUuid, type))); } diff --git a/packages/esm-commons-lib/src/components/card-summary/summary-card.component.tsx b/packages/esm-commons-lib/src/components/card-summary/summary-card.component.tsx index aabb2c4ca..ef4cf0922 100644 --- a/packages/esm-commons-lib/src/components/card-summary/summary-card.component.tsx +++ b/packages/esm-commons-lib/src/components/card-summary/summary-card.component.tsx @@ -31,13 +31,27 @@ export const SummaryCard: React.FC = ({ patientUuid, columns, const [groupedEncounterMappings, setGroupedEncounterMappings] = useState>([]); useEffect(() => { - Promise.all(columns.map((column) => fetchLatestEncountersOfTypes(patientUuid, column.encounterTypes))).then( - (results) => { - const filteredResults = results.map((result) => result.filter(Boolean)); - setColumnEncountersMappings(columns.map((column, index) => ({ column, encounters: filteredResults[index] }))); - setIsLoading(false); - }, - ); + Promise.all( + columns.map((column) => { + const encounterTypes = Array.isArray(column?.encounterTypes) + ? column.encounterTypes + : column?.encounterTypes + ? [column.encounterTypes] + : []; + + return fetchLatestEncountersOfTypes(patientUuid, encounterTypes); + }), + ).then((results) => { + const filteredResults = results.map((result) => result.filter(Boolean)); + setColumnEncountersMappings( + columns.map((column, index) => ({ + column, + encounters: filteredResults[index], + })), + ); + + setIsLoading(false); + }); }, [columns, patientUuid]); useEffect(() => { @@ -88,7 +102,7 @@ function SummaryItem({ column, encounters }) {
{column.header} - + {column?.getObsValue ? : '--'} {column.getObsSummary && ( diff --git a/packages/esm-commons-lib/src/constants.ts b/packages/esm-commons-lib/src/constants.ts index c4ddbe54b..03fc744c6 100644 --- a/packages/esm-commons-lib/src/constants.ts +++ b/packages/esm-commons-lib/src/constants.ts @@ -8,7 +8,7 @@ export const daysDurationUnit = { export const basePath = '${openmrsSpaBase}/patient/'; export const encounterRepresentation = 'custom:(uuid,encounterDatetime,encounterType,location:(uuid,name),' + - 'patient:(uuid,display),encounterProviders:(uuid,provider:(uuid,name)),' + + 'patient:(uuid,display,age),encounterProviders:(uuid,provider:(uuid,name)),' + 'obs:(uuid,obsDatetime,voided,groupMembers,concept:(uuid,name:(uuid,name)),value:(uuid,name:(uuid,name),' + 'names:(uuid,conceptNameType,name))),form:(uuid,name))'; diff --git a/packages/esm-commons-lib/src/utils/encounter-list-config-builder.ts b/packages/esm-commons-lib/src/utils/encounter-list-config-builder.ts index 680202a64..03bb89960 100644 --- a/packages/esm-commons-lib/src/utils/encounter-list-config-builder.ts +++ b/packages/esm-commons-lib/src/utils/encounter-list-config-builder.ts @@ -28,8 +28,9 @@ interface ConditionalActionProps { label: string; package: string; formName: string; - dependsOn: string; - dependantConcept: string; + dependsOn?: string; + dependantConcept?: string; + dependantEncounter?: string; } interface ColumnDefinition { @@ -52,6 +53,12 @@ interface ColumnDefinition { statusColorMappings?: Record; isConditionalConcept?: boolean; conditionalConceptMappings?: Record; + conditionalEncounterMappings?: Record; +} + +export interface ConditionalEncounterMapping { + concept: string; + isDate?: boolean; } interface LaunchOptions { @@ -86,14 +93,13 @@ export const getTabColumns = (columnsDefinition: Array) => { getValue: (encounter) => { if (column.id === 'actions') { const conditionalActions = []; - const baseActions = column.actionOptions.map((action: ActionProps) => ({ + const baseActions = column.actionOptions?.map((action: ActionProps) => ({ form: { name: action.formName, package: action.package }, encounterUuid: encounter.uuid, intent: '*', label: action.label, mode: action.mode, })); - if (column?.conditionalActionOptions?.length) { column?.conditionalActionOptions?.map((action) => { const dependantObsValue = getObsFromEncounter(encounter, action.dependantConcept); @@ -107,6 +113,18 @@ export const getTabColumns = (columnsDefinition: Array) => { mode: action.mode, }); } + + const dependantEncounterValue = encounter.encounterType.uuid; + + if (dependantEncounterValue === action.dependantEncounter) { + return conditionalActions.push({ + form: { name: action.formName, package: action.package }, + encounterUuid: encounter.uuid, + intent: '*', + label: action.label, + mode: action.mode, + }); + } }); } return [...baseActions, ...conditionalActions]; diff --git a/packages/esm-commons-lib/src/utils/encounter-list-utils.ts b/packages/esm-commons-lib/src/utils/encounter-list-utils.ts index 7f96866c7..f0c5aac11 100644 --- a/packages/esm-commons-lib/src/utils/encounter-list-utils.ts +++ b/packages/esm-commons-lib/src/utils/encounter-list-utils.ts @@ -88,6 +88,10 @@ export function getObsFromEncounter( ) { let obs = findObs(encounter, obsConcept); + if (!encounter || !obsConcept) { + return '--'; + } + if (isTrueFalseConcept) { if ( (obs?.value?.uuid != 'cf82933b-3f3f-45e7-a5ab-5d31aaee3da3' && obs?.value?.name?.name !== 'Unknown') || @@ -113,6 +117,14 @@ export function getObsFromEncounter( return fetchMotherName(encounter.patient.uuid); } + if (type === 'visitType') { + return encounter.encounterType.name; + } + + if (type === 'ageAtHivTest') { + return encounter.patient.age; + } + if (secondaryConcept && typeof obs.value === 'object' && obs.value.names) { const primaryValue = obs.value.names.find((conceptName) => conceptName.conceptNameType === 'SHORT')?.name || obs.value.name.name; @@ -132,7 +144,11 @@ export function getObsFromEncounter( } if (isDate) { - return formatDate(parseDate(obs.value), { mode: 'wide' }); + if (typeof obs.value === 'object' && obs.value?.names) { + return formatDate(parseDate(obs.obsDatetime), { mode: 'wide' }); + } else { + return formatDate(parseDate(obs.value), { mode: 'wide' }); + } } if (typeof obs.value === 'object' && obs.value?.names) { diff --git a/packages/esm-commons-lib/src/utils/schema-manipulation.ts b/packages/esm-commons-lib/src/utils/schema-manipulation.ts index a1c7f07be..25d6b5ac3 100644 --- a/packages/esm-commons-lib/src/utils/schema-manipulation.ts +++ b/packages/esm-commons-lib/src/utils/schema-manipulation.ts @@ -4,7 +4,7 @@ export function extractSchemaValues(schema) { if (obj === null || obj === undefined || typeof obj !== 'object') { return; } - Object.entries(obj)?.forEach(([key, value]) => { + Object.entries(obj).forEach(([key, value]) => { if (value !== undefined && value !== null) { if (typeof value === 'object' && !Array.isArray(value)) { traverse(value); diff --git a/packages/esm-commons-lib/src/utils/summary-card-config-builder.ts b/packages/esm-commons-lib/src/utils/summary-card-config-builder.ts index 2d64cc0df..f3b8c6db9 100644 --- a/packages/esm-commons-lib/src/utils/summary-card-config-builder.ts +++ b/packages/esm-commons-lib/src/utils/summary-card-config-builder.ts @@ -1,3 +1,5 @@ +import isEmpty from 'lodash-es/isEmpty'; +import { fetchEtlData } from '../api/api'; import { getObsFromEncounter, getConditionalConceptValue } from './encounter-list-utils'; import { extractSchemaValues, replaceWithConfigDefaults } from './schema-manipulation'; @@ -13,6 +15,27 @@ export const getSummaryCardProps = (schemaConfig, config = null) => { if (column.isConditionalConcept) { return getConditionalConceptValue(encounter, column.conditionalConceptMappings, column.isDate); } + + if (column?.mambaEtlData === 'ancVisits') { + const response = fetchEtlData('fetchMambaAncData', 'no_of_anc_visits', encounter?.patient?.uuid); + return response?.data; + } + + if (column?.mambaEtlData === 'motherStatus') { + const response = fetchEtlData('fetchMambaAncData', 'mother_status', encounter?.patient?.uuid); + return response?.data; + } + + if (column?.mambaEtlData === 'deliveryDate') { + const response = fetchEtlData('fetchMambaAncData', 'estimated_date_of_delivery', encounter?.patient?.uuid); + return response?.data; + } + + if (column?.mambaEtlData === 'motherHivStatus') { + const response = fetchEtlData('fetchMambaAncData', 'mother_hiv_status', encounter?.patient?.uuid); + return response?.data; + } + return getObsFromEncounter( encounter, column.concept, @@ -23,16 +46,52 @@ export const getSummaryCardProps = (schemaConfig, config = null) => { ); }, getObsSummary: async (encounters) => { - const summaryValues = encounters.map((encounter) => { - if (encounter && encounter.observation && encounter.observation.value) { - return encounter.observation.value.join(', '); - } else { - return ''; - } - }); - return summaryValues.join(' | '); + if (column?.conditionalEncounterMappings && Object.keys(column?.conditionalEncounterMappings)?.length > 0 && column.encounterTypes?.length > 0) { + const filteredEncounters = encounters.filter((encounter) => + column.encounterTypes.includes(encounter.encounterType.uuid), + ); + let latestEncounter = null; + let latestValue = null; + + filteredEncounters.forEach((encounter) => { + if (encounter.obs && Array.isArray(encounter.obs)) { + encounter.obs.forEach((observation) => { + if (observation.concept.uuid === column.concept) { + if (!latestEncounter || encounter.encounterDatetime > latestEncounter.encounterDatetime) { + latestEncounter = encounter; + } + latestValue = getObsFromEncounter(latestEncounter, column.concept, column.isDate); + } + }); + } + }); + return latestValue; + } else { + const summaryValues = encounters.map((encounter) => { + if (column.type === 'nextAppointmentDate') { + let nextVisitDate = getObsFromEncounter(encounter[0], column.concept, true); + if (nextVisitDate !== '--') { + const days = calculateDateDifferenceInDate(nextVisitDate); + nextVisitDate = nextVisitDate > 0 ? `In ${days}` : ''; + } + return nextVisitDate; + } + if (encounter && encounter.observation && !isEmpty(encounter.observation.value)) { + return encounter.observation.value.join(', '); + } else { + return ''; + } + }); + return summaryValues?.length > 0 ? summaryValues.filter((val) => val !== '').join(' | ') : ''; + } }, })); return columns; }; + +export const calculateDateDifferenceInDate = (givenDate: string): string => { + const dateDifference = new Date(givenDate).getTime() - new Date().getTime(); + const totalDays = Math.floor(dateDifference / (1000 * 3600 * 24)); + return `${totalDays} day(s)`; +}; diff --git a/packages/esm-ohri-pmtct-app/src/config-schema.ts b/packages/esm-ohri-pmtct-app/src/config-schema.ts index 3c7e055af..c3dd83cd9 100644 --- a/packages/esm-ohri-pmtct-app/src/config-schema.ts +++ b/packages/esm-ohri-pmtct-app/src/config-schema.ts @@ -20,31 +20,31 @@ export const configSchema = { _type: Type.Object, _description: 'List of forms for PMTCT.', _default: { - antenatal: 'Antenatal Form', - labourAndDelivery: 'Labour & Delivery Form', - motherPostnatal: 'Mother - Postnatal Form', - infantPostnatal: 'Infant - Postanal Form', + antenatalFormName: 'Antenatal Form', + labourAndDeliveryFormName: 'Labour & Delivery Form', + motherPostnatalFormName: 'Mother - Postnatal Form', + infantPostnatalFormName: 'Infant - Postanal Form', }, }, formUuids: { _type: Type.Object, _description: 'List of uuids for PMTCT forms.', _default: { - antenatal: '5255a535-2acb-3f44-bd0a-3f80595dece1', - labourAndDelivery: '1e5614d6-5306-11e6-beb8-9e71128cae77', - motherPostnatal: 'e6b67aa4-6c59-4470-8ad5-b994efeda553', - infantPostnatal: '5022c5d7-ea45-47ce-bd65-1ba1d8ad2467', + antenatalFormUuid: '5255a535-2acb-3f44-bd0a-3f80595dece1', + labourAndDeliveryFormUuid: '1e5614d6-5306-11e6-beb8-9e71128cae77', + motherPostnatalFormUuid: 'e6b67aa4-6c59-4470-8ad5-b994efeda553', + infantPostnatalFormUuid: '120048e5-4122-3c6d-8f77-c79e75b7b3fc', }, }, encounterTypes: { _type: Type.Object, _description: 'List of PMTCT encounter type UUIDs', _default: { - antenatal: '677d1a80-dbbe-4399-be34-aa7f54f11405', - laborAndDelivery: '6dc5308d-27c9-4d49-b16f-2c5e3c759757', - infantPostnatal: 'af1f1b24-d2e8-4282-b308-0bf79b365584', - motherPostnatal: '269bcc7f-04f8-4ddc-883d-7a3a0d569aad', - mchEncounterType: '12de5bc5-352e-4faf-9961-a2125085a75c', + antenatalEncounterType: '677d1a80-dbbe-4399-be34-aa7f54f11405', + laborAndDeliveryEncounterType: '6dc5308d-27c9-4d49-b16f-2c5e3c759757', + infantPostnatalEncounterType: 'af1f1b24-d2e8-4282-b308-0bf79b365584', + motherPostnatalEncounterType: '269bcc7f-04f8-4ddc-883d-7a3a0d569aad', + mchEncounterTypeEncounterType: '12de5bc5-352e-4faf-9961-a2125085a75c', }, }, obsConcepts: { @@ -110,4 +110,5 @@ export interface ConfigObject { encounterTypes: Object; obsConcepts: Object; formNames: Object; + formUuids: Object; } diff --git a/packages/esm-ohri-pmtct-app/src/views/child-health/child-health-config.json b/packages/esm-ohri-pmtct-app/src/views/child-health/child-health-config.json index 77f59e2aa..8873bd098 100644 --- a/packages/esm-ohri-pmtct-app/src/views/child-health/child-health-config.json +++ b/packages/esm-ohri-pmtct-app/src/views/child-health/child-health-config.json @@ -5,12 +5,12 @@ "tabName": "Infant Postnatal Visit", "headerTitle": "Infant Postnatal Visit", "displayText": "Infant Postnatal Visit", - "encounterType": "af1f1b24-d2e8-4282-b308-0bf79b365584", + "encounterType": "infantPostnatalEncounterType", "columns": [ { "id": "pTrackerId", "title": "PTracker Id", - "concept": "6c45421e-2566-47cb-bbb3-07586fffbfe2" + "concept": "pTrackerIdConcept" }, { "id": "mothersName", @@ -20,41 +20,41 @@ { "id": "artProphylaxisStatus", "title": "ART Prophylaxis Status", - "concept": "1148AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "artProphylaxisStatus" }, { "id": "linkedToArt", "title": "Linked to ART", - "concept": "a40d8bc4-56b8-4f28-a1dd-412da5cf20ed" + "concept": "linkedToArt" }, { "id": "breastfeedingStatus", "title": "Breastfeeding status", - "concept": "1151AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "breastfeedingStatus" }, { "id": "outcomeStatus", "title": "Outcome Status", - "concept": "160433AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "outcomeStatus" }, { "id": "nextVisitDate", "isDate": true, "title": "Next visit date", - "concept": "5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "nextVisitDateConcept" }, { "id": "actions", "title": "Actions", "actionOptions": [ { - "formName": "Antenatal Form", + "formName": "infantPostnatalFormName", "package": "pmtct", "label": "View Details", "mode": "view" }, { - "formName": "Antenatal Form", + "formName": "infantPostnatalFormName", "package": "pmtct", "label": "Edit Form", "mode": "edit" @@ -68,8 +68,8 @@ }, "formList": [ { - "name": "Infant - Postanal Form", - "uuid": "5022c5d7-ea45-47ce-bd65-1ba1d8ad2467" + "name": "infantPostnatalFormName", + "uuid": "infantPostnatalFormUuid" } ] } diff --git a/packages/esm-ohri-pmtct-app/src/views/child-health/child-health.component.tsx b/packages/esm-ohri-pmtct-app/src/views/child-health/child-health.component.tsx index 1d1b3e21d..9b6a49f6a 100644 --- a/packages/esm-ohri-pmtct-app/src/views/child-health/child-health.component.tsx +++ b/packages/esm-ohri-pmtct-app/src/views/child-health/child-health.component.tsx @@ -3,13 +3,15 @@ import { Tabs, Tab, TabList, TabPanels, TabPanel } from '@carbon/react'; import styles from '../common.scss'; import { EncounterList, getMenuItemTabConfiguration } from '@ohri/openmrs-esm-ohri-commons-lib'; import childHealthTabConfigSchema from './child-health-config.json'; +import { useConfig } from '@openmrs/esm-framework'; interface OverviewListProps { patientUuid: string; } const ChildHealthSummary: React.FC = ({ patientUuid }) => { - const tabs = getMenuItemTabConfiguration(childHealthTabConfigSchema); + const config = useConfig(); + const tabs = getMenuItemTabConfiguration(childHealthTabConfigSchema, config); return (
diff --git a/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health-config.json b/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health-config.json index 9183d275d..19cd2bd3a 100644 --- a/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health-config.json +++ b/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health-config.json @@ -5,34 +5,34 @@ "tabName": "Antenatal Care", "headerTitle": "Antenatal Care", "displayText": "Antenatal Care", - "encounterType": "677d1a80-dbbe-4399-be34-aa7f54f11405", + "encounterType": "antenatalEncounterType", "columns": [ { "id": "pTrackerId", "title": "PTracker Id", - "concept": "6c45421e-2566-47cb-bbb3-07586fffbfe2" + "concept": "pTrackerIdConcept" }, { "id": "visitDate", "isDate": true, "title": "Visit Date", - "concept": "163260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "visitDate" }, { "id": "hivTestStatus", "title": "HIV Test Status", - "concept": "164401AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "hivTestStatus" }, { "id": "artLinkage", "title": "ART linkage (if positive)", - "concept": "6e62bf7e-2107-4d09-b485-6e60cbbb2d08" + "concept": "artLinkage" }, { "id": "edd", "isDate": true, "title": "EDD", - "concept": "5596AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "eDDConcept" }, { "id": "facility", @@ -43,25 +43,25 @@ "id": "followUpDate", "isDate": true, "title": "Next follow-up date", - "concept": "5096AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "followUpDateConcept" }, { "id": "vlResults", "title": "VL Results", - "concept": "1305AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "MotherViralLoadResult" }, { "id": "actions", "title": "Actions", "actionOptions": [ { - "formName": "Antenatal Form", + "formName": "antenatalFormName", "package": "pmtct", "label": "View Details", "mode": "view" }, { - "formName": "Antenatal Form", + "formName": "antenatalFormName", "package": "pmtct", "label": "Edit Form", "mode": "edit" @@ -75,8 +75,8 @@ }, "formList": [ { - "name": "Antenatal Form", - "uuid": "5255a535-2acb-3f44-bd0a-3f80595dece1" + "name": "antenatalFormName", + "uuid": "antenatalFormUuid" } ] }, @@ -84,52 +84,52 @@ "tabName": "Labour and Delivery", "headerTitle": "Labour and Delivery", "displayText": "Labour and Delivery", - "encounterType": "6dc5308d-27c9-4d49-b16f-2c5e3c759757", + "encounterType": "laborAndDeliveryEncounterType", "columns": [ { "id": "pTrackerId", "title": "PTracker Id", - "concept": "6c45421e-2566-47cb-bbb3-07586fffbfe2" + "concept": "pTrackerIdConcept" }, { "id": "deliveryDate", "isDate": true, "title": "Delivery Date", - "concept": "5599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "dateOfDeliveryConcept" }, { "id": "bookedForANC", "title": "Booked for ANC", - "concept": "1719AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "bookedForAncConcept" }, { "id": "hivTestStatus", "title": "HIV Test Status", - "concept": "164401AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "hivTestStatus" }, { "id": "artInitiation", "isDate": true, "title": "ART Initiation", - "concept": "159599AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "artStartDate" }, { "id": "birthCount", "title": "Birth Count", - "concept": "1568AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "birthCountConcept" }, { "id": "actions", "title": "Actions", "actionOptions": [ { - "formName": "Labour & Delivery Form", + "formName": "labourAndDeliveryFormName", "package": "pmtct", "label": "View Details", "mode": "view" }, { - "formName": "Labour & Delivery Form", + "formName": "labourAndDeliveryFormName", "package": "pmtct", "label": "Edit Form", "mode": "edit" @@ -143,8 +143,8 @@ }, "formList": [ { - "name": "Labour & Delivery Form", - "uuid": "1e5614d6-5306-11e6-beb8-9e71128cae77" + "name": "labourAndDeliveryFormName", + "uuid": "labourAndDeliveryFormUuid" } ] }, @@ -152,53 +152,53 @@ "tabName": "Postnatal Care", "headerTitle": "Postnatal Care", "displayText": "Postnatal Care", - "encounterType": "269bcc7f-04f8-4ddc-883d-7a3a0d569aad", + "encounterType": "motherPostnatalEncounterType", "columns": [ { "id": "pTrackerId", "title": "PTracker Id", - "concept": "6c45421e-2566-47cb-bbb3-07586fffbfe2" + "concept": "pTrackerIdConcept" }, { "id": "visitDate", "isDate": true, "title": "Visit Date", - "concept": "163260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "visitDate" }, { "id": "currentHivStatus", "title": "Current HIV Status", - "concept": "164401AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "hivTestStatus" }, { "id": "recentViralLoadTestDate", "isDate": true, "title": "Recent Viral load test date", - "concept": "163281AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "MotherViralLoadDate" }, { "id": "recentViralLoadResults", "title": "Recent Viral load results", - "concept": "1305AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "MotherViralLoadResult" }, { "id": "nextVisitDate", "isDate": true, "title": "Next visit date", - "concept": "163260AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "concept": "visitDate" }, { "id": "actions", "title": "Actions", "actionOptions": [ { - "formName": "Postnatal Care", + "formName": "motherPostnatalFormName", "package": "pmtct", "label": "View Details", "mode": "view" }, { - "formName": "Postnatal Care", + "formName": "motherPostnatalFormName", "package": "pmtct", "label": "Edit Form", "mode": "edit" @@ -212,8 +212,8 @@ }, "formList": [ { - "name": "Mother - Postnatal Form", - "uuid": "e6b67aa4-6c59-4470-8ad5-b994efeda553" + "name": "motherPostnatalFormName", + "uuid": "motherPostnatalFormUuid" } ] } diff --git a/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health.component.tsx b/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health.component.tsx index 42b420522..68ec8cf99 100644 --- a/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health.component.tsx +++ b/packages/esm-ohri-pmtct-app/src/views/maternal-health/maternal-health.component.tsx @@ -3,13 +3,15 @@ import { Tabs, Tab, TabList, TabPanels, TabPanel } from '@carbon/react'; import styles from '../common.scss'; import { EncounterList, getMenuItemTabConfiguration } from '@ohri/openmrs-esm-ohri-commons-lib'; import maternalHealthTabConfigSchema from './maternal-health-config.json'; +import { useConfig } from '@openmrs/esm-framework'; interface OverviewListProps { patientUuid: string; } const MaternalHealthSummary: React.FC = ({ patientUuid }) => { - const tabs = getMenuItemTabConfiguration(maternalHealthTabConfigSchema); + const config = useConfig(); + const tabs = getMenuItemTabConfiguration(maternalHealthTabConfigSchema, config); return (
diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/appointments-config.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/appointments-config.json new file mode 100644 index 000000000..1d9b4f5ec --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/appointments-config.json @@ -0,0 +1,24 @@ +{ + "menuId": "appointment", + "cardTitle": "Appointments", + "columns": [ + { + "id": "nextAppointmentDate", + "isDate": true, + "title": "Next appointment date", + "encounterTypes": [ + "antenatalEncounterType" + ], + "concept": "nextVisitDateConcept", + "type": "nextAppointmentDate" + }, + { + "id": "ancVisitsAttended", + "title": "ANC visits attended", + "encounterTypes": [ + "antenatalEncounterType" + ], + "mambaEtlData": "ancVisits" + } + ] +} \ No newline at end of file diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/arv-therapy-config.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/arv-therapy-config.json new file mode 100644 index 000000000..932ce6f5c --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/arv-therapy-config.json @@ -0,0 +1,35 @@ +{ + "menuId": "artSummary", + "cardTitle": "ART", + "columns": [ + { + "id": "artInitiation", + "title": "ART initiation", + "encounterTypes": ["motherPostnatalEncounterType", "laborAndDeliveryEncounterType", "antenatalEncounterType"], + "concept": "artInitiationConcept", + "conditionalEncounterMappings": { + "valueOne": { "concept": "pTrackerIdConcept" }, + "valueTwo": { + "concept": "artInitiationConcept", + "isDate": true + }, + "valueThree": { "concept": "artStartDate" } + } + }, + { + "id": "artStartDate", + "isDate": true, + "title": "ART initiation", + "encounterTypes": ["motherPostnatalEncounterType", "laborAndDeliveryEncounterType", "antenatalEncounterType"], + "concept": "artInitiationConcept", + "conditionalEncounterMappings": { + "valueOne": { "concept": "pTrackerIdConcept" }, + "valueTwo": { + "concept": "artInitiationConcept", + "isDate": true + }, + "valueThree": { "concept": "artStartDate" } + } + } + ] +} \ No newline at end of file diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/current-pregnancy.component.tsx b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/current-pregnancy.component.tsx index abbb0d2fa..97fd5ea4d 100644 --- a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/current-pregnancy.component.tsx +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/current-pregnancy.component.tsx @@ -1,21 +1,26 @@ -import React, { useEffect, useState, useMemo, useCallback } from 'react'; +import React, { useEffect, useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { + type PatientChartProps, ExpandableList, - getObsFromEncounter, fetchPatientRelationships, EncounterList, basePath, fetchPatientLastEncounter, + type SummaryCardColumn, SummaryCard, - useDataFetch, + fetchEtlData, + getMenuItemTabConfiguration, + getSummaryCardProps, } from '@ohri/openmrs-esm-ohri-commons-lib'; -import type { PatientChartProps, EncounterListColumn, SummaryCardColumn } from '@ohri/openmrs-esm-ohri-commons-lib'; import dayjs from 'dayjs'; -import { moduleName } from '../../..'; import { Link } from '@carbon/react'; import { navigate, useConfig } from '@openmrs/esm-framework'; import { fetchPatientIdentifiers, fetchChildLatestFinalOutcome } from '../../../api/api'; +import recentPregnancyConfigSchema from './recent-pregnancy-config.json'; +import appointmentSummaryConfigSchema from './appointments-config.json'; +import arvTherapyColumnsConfigSchema from './arv-therapy-config.json'; +import motherPreviousVisitConfigSchema from './mother-previous-visit.json'; interface pregnancyOutcomeProps { id: string; @@ -40,16 +45,16 @@ const CurrentPregnancy: React.FC = ({ patientUuid, pTrackerId const appointmentsHeader = t('appointments', 'Appointments'); const familyHeader = t('family', 'Family'); const pregnancyOutcomeHeader = t('infantStatusAtBirth', 'Infant Status At Birth'); - const previousVisitsTitle = t('previousVisitsSummary', 'Previous Visits'); const [relatives, setRelatives] = useState([]); const [relativeToIdentifierMap, setRelativeToIdentifierMap] = useState([]); const [pregnancyOutcomes, setPregnancyOutcomes] = useState([]); const [infantOutcomes, setInfantOutcomes] = useState([]); - const { formNames, encounterTypes, obsConcepts, formUuids } = useConfig(); - const { data: totalAncCount } = useDataFetch('fetchMambaAncData', 'no_of_anc_visits', patientUuid); - const { data: motherStatus } = useDataFetch('fetchMambaAncData', 'mother_status', patientUuid); - const { data: deliveryDate } = useDataFetch('fetchMambaAncData', 'estimated_date_of_delivery', patientUuid); - const { data: motherHivStatus } = useDataFetch('MotherHivStatus', 'mother_hiv_status', patientUuid, pTrackerId); + const { encounterTypes, obsConcepts, identifiersTypes } = useConfig(); + const config = useConfig(); + const motherPreviousVisitTabs = getMenuItemTabConfiguration(motherPreviousVisitConfigSchema, config); + const recentPregnancyTabs = getSummaryCardProps(recentPregnancyConfigSchema, config); + const appointmentSummaryTabs = getSummaryCardProps(appointmentSummaryConfigSchema, config); + const arvTherapyTabs = getSummaryCardProps(arvTherapyColumnsConfigSchema, config); const headersFamily = [ { @@ -77,42 +82,6 @@ const CurrentPregnancy: React.FC = ({ patientUuid, pTrackerId key: 'finalOutcome', }, ]; - const getParentCurrentLabourAndDeliveryEncounter = useCallback(async () => { - const currentPregnancyANCEncounter = await fetchPatientLastEncounter(patientUuid, encounterTypes.antenatal); - const currentPregnancyLabourAndDeliveryEncounter = await fetchPatientLastEncounter( - patientUuid, - encounterTypes.labourAndDelivery, - ); - if ( - currentPregnancyLabourAndDeliveryEncounter?.encounterDatetime > currentPregnancyANCEncounter?.encounterDatetime || - currentPregnancyANCEncounter?.encounterDatetime == null - ) { - if (currentPregnancyLabourAndDeliveryEncounter !== null) { - setPregnancyOutcomes( - currentPregnancyLabourAndDeliveryEncounter.obs?.filter( - (obs) => obs.concept.uuid === obsConcepts.infantDeliveryGroupingConcept, - ), - ); - } - } - }, [ - encounterTypes.antenatal, - encounterTypes.labourAndDelivery, - obsConcepts.infantDeliveryGroupingConcept, - patientUuid, - ]); - - const getParentRelationships = useCallback(async () => { - let relationships = []; - const relationshipsData = await fetchPatientRelationships(patientUuid); - if (relationshipsData?.length) { - relationshipsData.forEach((item) => { - relationships.push(item); - }); - } - setRelatives(relationships); - }, [patientUuid]); - const headersPregnancyOutcome = [ { header: t('pTrackerId', 'PTracker ID'), @@ -134,44 +103,74 @@ const CurrentPregnancy: React.FC = ({ patientUuid, pTrackerId useEffect(() => { getParentCurrentLabourAndDeliveryEncounter(); getParentRelationships(); - }, [getParentCurrentLabourAndDeliveryEncounter, getParentRelationships]); + }, []); + + async function getParentRelationships() { + let relationships = []; + const relationshipsData = await fetchPatientRelationships(patientUuid); + if (relationshipsData?.length) { + relationshipsData.forEach((item) => { + relationships.push(item); + }); + } + setRelatives(relationships); + } - const getChildPTracker = useCallback( - async (patientUuid) => { - let pTrackerMap = { patientId: patientUuid, pTrackerId: '--' }; - const identifiers = await fetchPatientIdentifiers(patientUuid); - if (identifiers?.length) { - pTrackerMap.pTrackerId = - identifiers.find((id) => id.identifierType.uuid === encounterTypes.PTrackerIdentifierType)?.identifier ?? - '--'; + async function getParentCurrentLabourAndDeliveryEncounter() { + const currentPregnancyANCEncounter = await fetchPatientLastEncounter( + patientUuid, + encounterTypes.antenatalEncounterType, + ); + const currentPregnancyLabourAndDeliveryEncounter = await fetchPatientLastEncounter( + patientUuid, + encounterTypes.labourAndDeliveryEncounterType, + ); + if ( + currentPregnancyLabourAndDeliveryEncounter?.encounterDatetime > currentPregnancyANCEncounter?.encounterDatetime || + currentPregnancyANCEncounter?.encounterDatetime == null + ) { + if (currentPregnancyLabourAndDeliveryEncounter !== null) { + setPregnancyOutcomes( + currentPregnancyLabourAndDeliveryEncounter.obs?.filter( + (obs) => obs.concept.uuid === obsConcepts.infantDeliveryGroupingConcept, + ), + ); } - return pTrackerMap; - }, - [encounterTypes.PTrackerIdentifierType], - ); + } + } + useEffect(() => { + const relativeToPtrackerPromises = relatives.map((relative) => getChildPTracker(relative.personB.uuid)); + Promise.all(relativeToPtrackerPromises).then((values) => { + setRelativeToIdentifierMap(values.map((value) => ({ patientId: value.patientId, pTrackerId: value.pTrackerId }))); + }); + getInfantOutcome(); + }, [relatives]); - const getInfantOutcome = useCallback(() => { - const infantOutcomesPromises = relatives.map(async (relative) => { + const getInfantOutcome = () => { + const infantOutcomes = relatives.map(async (relative) => { const finalOutcome = await fetchChildLatestFinalOutcome( relative.personB.uuid, obsConcepts.outcomeStatus, - encounterTypes.infantPostnatal, + encounterTypes.infantPostnatalEncounterType, ); return { finalOutcome: finalOutcome, childUuid: relative.personB.uuid }; }); - Promise.all(infantOutcomesPromises).then((values) => { + Promise.all(infantOutcomes).then((values) => { setInfantOutcomes(values.map((value) => ({ finalOutcome: value.finalOutcome, childUuid: value.childUuid }))); }); - }, [encounterTypes.infantPostnatal, obsConcepts.outcomeStatus, relatives]); + }; - useEffect(() => { - const relativeToPtrackerPromises = relatives.map((relative) => getChildPTracker(relative.personB.uuid)); - Promise.all(relativeToPtrackerPromises).then((values) => { - setRelativeToIdentifierMap(values.map((value) => ({ patientId: value.patientId, pTrackerId: value.pTrackerId }))); - }); - getInfantOutcome(); - }, [getChildPTracker, getInfantOutcome, relatives]); + async function getChildPTracker(patientUuid: string) { + let pTrackerMap = { patientId: patientUuid, pTrackerId: '--' }; + const identifiers = await fetchPatientIdentifiers(patientUuid); + if (identifiers?.length) { + pTrackerMap.pTrackerId = + identifiers.find((id) => id.identifierType.uuid === identifiersTypes.pTrackerIdentifierType)?.identifier ?? + '--'; + } + return pTrackerMap; + } const parentRelationships: familyItemProps[] = useMemo(() => { let items = []; @@ -235,217 +234,12 @@ const CurrentPregnancy: React.FC = ({ patientUuid, pTrackerId obsConcepts.breastfeedingStatus, ]); - const currentPregnancyColumns: SummaryCardColumn[] = useMemo( - () => [ - { - key: 'motherHIVStatus', - header: t('motherHIVStatus', 'Mother HIV Status'), - encounterTypes: [], - getObsValue: () => { - return motherHivStatus; - }, - }, - { - key: 'expectedDeliveryDate', - header: t('expectedDeliveryDate', 'Expected Delivery Date'), - encounterTypes: [], - getObsValue: () => { - return deliveryDate; - }, - }, - { - key: 'motherStatus', - header: t('motherStatus', 'Mother Status'), - encounterTypes: [], - getObsValue: () => { - return motherStatus; - }, - }, - ], - [t, motherHivStatus, deliveryDate, motherStatus], - ); - - const arvTherapyColumns: SummaryCardColumn[] = useMemo( - () => [ - { - key: 'artInitiation', - header: t('artInitiation', 'ART Initiation'), - encounterTypes: [encounterTypes.motherPostnatal, encounterTypes.labourAndDelivery, encounterTypes.antenatal], - getObsValue: (encounters) => { - const pncArtData = { - artInitiation: getObsFromEncounter(encounters[0], obsConcepts.artInitiationConcept), - artStartDate: getObsFromEncounter(encounters[0], obsConcepts.artStartDate, true), - pTrackerId: getObsFromEncounter(encounters[0], obsConcepts.pTrackerIdConcept), - }; - const lndArtData = { - artInitiation: getObsFromEncounter(encounters[1], obsConcepts.artInitiationConcept), - artStartDate: getObsFromEncounter(encounters[1], obsConcepts.artStartDate, true), - pTrackerId: getObsFromEncounter(encounters[1], obsConcepts.pTrackerIdConcept), - }; - const ancArtData = { - artInitiation: getObsFromEncounter(encounters[2], obsConcepts.artInitiationConcept), - artStartDate: getObsFromEncounter(encounters[2], obsConcepts.artStartDate, true), - pTrackerId: getObsFromEncounter(encounters[2], obsConcepts.pTrackerIdConcept), - }; - const latestArtData = getLatestArtDetails(pncArtData, lndArtData, ancArtData); - return latestArtData['artInitiation']; - }, - }, - { - key: 'artStartDate', - header: t('artStartDate', 'ART Start Date'), - encounterTypes: [encounterTypes.motherPostnatal, encounterTypes.labourAndDelivery, encounterTypes.antenatal], - getObsValue: (encounters) => { - const pncArtData = { - artInitiation: getObsFromEncounter(encounters[0], obsConcepts.artInitiationConcept), - artStartDate: getObsFromEncounter(encounters[0], obsConcepts.artStartDate, true), - pTrackerId: getObsFromEncounter(encounters[0], obsConcepts.pTrackerIdConcept), - }; - const lndArtData = { - artInitiation: getObsFromEncounter(encounters[1], obsConcepts.artInitiationConcept), - artStartDate: getObsFromEncounter(encounters[1], obsConcepts.artStartDate, true), - pTrackerId: getObsFromEncounter(encounters[1], obsConcepts.pTrackerIdConcept), - }; - const ancArtData = { - artInitiation: getObsFromEncounter(encounters[2], obsConcepts.artInitiationConcept), - artStartDate: getObsFromEncounter(encounters[2], obsConcepts.artStartDate, true), - pTrackerId: getObsFromEncounter(encounters[2], obsConcepts.pTrackerIdConcept), - }; - const latestArtData = getLatestArtDetails(pncArtData, lndArtData, ancArtData); - return latestArtData['artStartDate']; - }, - }, - ], - [ - encounterTypes.antenatal, - encounterTypes.labourAndDelivery, - encounterTypes.motherPostnatal, - obsConcepts.artInitiationConcept, - obsConcepts.artStartDate, - obsConcepts.pTrackerIdConcept, - t, - ], - ); - - const appointmentsColumns: SummaryCardColumn[] = useMemo( - () => [ - { - key: 'nextAppointmentDate', - header: t('nextAppointmentDate', 'Next Appointment Date'), - encounterTypes: [encounterTypes.antenatal], - getObsValue: ([encounter]) => { - return getObsFromEncounter(encounter, obsConcepts.nextVisitDateConcept, true); - }, - getObsSummary: ([encounter]) => { - let nextVisitDate = getObsFromEncounter(encounter, obsConcepts.nextVisitDateConcept, true); - if (nextVisitDate !== '--') { - const days = calculateDateDifferenceInDate(nextVisitDate); - nextVisitDate = nextVisitDate > 0 ? `In ${days}` : ''; - } - return nextVisitDate; - }, - }, - { - key: 'ancVisitsAttended', - header: t('ancVisitsAttended', 'ANC visits attended'), - encounterTypes: [], - getObsValue: () => { - return totalAncCount; - }, - }, - ], - [encounterTypes.antenatal, obsConcepts.nextVisitDateConcept, t, totalAncCount], - ); - - const selectMCHFormViewAction = useCallback( - (encounter) => { - const encounterType = encounter.encounterType.name; - if (encounterType === 'Antenatal') { - return { name: formNames.antenatal }; - } else if (encounterType === 'Labor and Delivery') { - return { name: formNames.labourAndDelivery }; - } else { - return { name: formNames.motherPostnatal }; - } - }, - [formNames.antenatal, formNames.labourAndDelivery, formNames.motherPostnatal], - ); - - const columnsMotherPreviousVisit: EncounterListColumn[] = useMemo( - () => [ - { - key: 'visitType', - header: t('visitType', 'Visit Type'), - getValue: (encounter) => { - return encounter.encounterType.name; - }, - }, - { - key: 'visitDate', - header: t('visitDate', 'Visit date'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.visitDate, true); - }, - }, - { - key: 'facility', - header: t('facility', 'Facility'), - getValue: (encounter) => { - return encounter.location.name; - }, - }, - { - key: 'nextFollowUpDate', - header: t('nextFollowUpDate', 'Next Follow-up date'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.followUpDateConcept, true); - }, - }, - { - key: 'actions', - header: t('actions', 'Actions'), - getValue: (encounter) => [ - { - form: selectMCHFormViewAction(encounter), - encounterUuid: encounter.uuid, - intent: '*', - label: t('viewDetails', 'View details'), - mode: 'view', - }, - ], - }, - ], - [obsConcepts.followUpDateConcept, obsConcepts.visitDate, selectMCHFormViewAction, t], - ); - - const getLatestArtDetails = (pncArtData, lndArtData, ancArtData) => { - const allArtData = [pncArtData, lndArtData, ancArtData]; - const filteredArtData = allArtData.filter((row) => row.artInitiation !== '--'); - if (filteredArtData.length == 0) { - return { - artInitiation: '--', - artStartDate: '--', - }; - } else if (filteredArtData.length == 1) { - return filteredArtData[0]; - } else { - filteredArtData.sort((a, b) => b.pTrackerId.localeCompare(a.pTrackerId)); - return filteredArtData[0]; - } - }; - - const calculateDateDifferenceInDate = (givenDate: string): string => { - const dateDifference = new Date(givenDate).getTime() - new Date().getTime(); - const totalDays = Math.floor(dateDifference / (1000 * 3600 * 24)); - return `${totalDays} day(s)`; - }; - return (
- +
- - + +
@@ -478,23 +272,17 @@ const CurrentPregnancy: React.FC = ({ patientUuid, pTrackerId />
- + {motherPreviousVisitTabs.map((tab) => ( + + ))}
); }; diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-family-summary-config.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-family-summary-config.json new file mode 100644 index 000000000..43ed98816 --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-family-summary-config.json @@ -0,0 +1,58 @@ +{ + "menuId": "hivExposedFamilySummary", + "tabDefinitions": [ + { + "tabName": "Visits", + "headerTitle": "Visits", + "displayText": "Visits", + "encounterType": "infantPostnatalEncounterType", + "columns": [ + { + "id": "visitType", + "title": "Visit Type", + "type": "visitType" + }, + { + "id": "visitDate", + "isDate": true, + "title": "Visit Date", + "concept": "infantVisitDate" + }, + { + "id": "facility", + "title": "Facility", + "type": "location" + }, + { + "id": "nextFollowUpDate", + "isDate": true, + "title": "Next follow-up date", + "concept": "followUpDateConcept" + }, + { + "id": "actions", + "title": "Actions", + "actionOptions": [ + { + "formName": "infantPostnatalFormName", + "package": "pmtct", + "label": "View Details", + "mode": "view" + } + ] + } + ], + "launchOptions": { + "hideFormLauncher": true, + "displayText": "", + "moduleName": "@ohri/openmrs-esm-ohri-pmtct-app" + }, + "formList": [ + { + "name": "infantPostnatalFormName", + "uuid": "infantPostnatalFormUuid" + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant-summary-config.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant-summary-config.json new file mode 100644 index 000000000..e7d085af9 --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant-summary-config.json @@ -0,0 +1,45 @@ +{ + "menuId": "hivExposedInfantSummary", + "tabDefinitions": [ + { + "tabName": "HIV Monitoring", + "headerTitle": "HIV Monitoring", + "displayText": "HIV Monitoring", + "encounterType": "infantPostnatalEncounterType", + "columns": [ + { + "id": "date", + "isDate": true, + "title": "Date", + "concept": "artStartDate" + }, + { + "id": "testType", + "title": "Test type", + "concept": "testTypeConcept" + }, + { + "id": "ageAtTimeOfTest", + "title": "Age at time of test", + "type": "ageAtHivTest" + }, + { + "id": "hivStatus", + "title": "Hiv Status", + "concept": "finalTestResults" + } + ], + "launchOptions": { + "hideFormLauncher": true, + "displayText": "", + "moduleName": "@ohri/openmrs-esm-ohri-pmtct-app" + }, + "formList": [ + { + "name": "infantPostnatalFormName", + "uuid": "infantPostnatalFormUuid" + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant.component.tsx b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant.component.tsx index 8a5b5ae1a..59e2096b9 100644 --- a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant.component.tsx +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/hiv-exposed-infant.component.tsx @@ -2,19 +2,21 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ExpandableList, - getObsFromEncounter, EncounterList, fetchPatientRelationships, basePath, SummaryCard, + getMenuItemTabConfiguration, + getSummaryCardProps, } from '@ohri/openmrs-esm-ohri-commons-lib'; -import type { EncounterListColumn, SummaryCardColumn } from '@ohri/openmrs-esm-ohri-commons-lib'; import { navigate, useConfig } from '@openmrs/esm-framework'; import dayjs from 'dayjs'; import { Link } from '@carbon/react'; -import { moduleName } from '../../..'; -import { fetchPatientIdentifiers } from '../../../api/api'; import { type familyItemProps } from './current-pregnancy.component'; +import hivExposedInfantSummary from './hiv-exposed-infant-summary-config.json'; +import hivExposedFamilySummary from './hiv-exposed-family-summary-config.json'; +import hivExposedInfantConfigSchema from './infant-summary-config.json'; +import { fetchPatientIdentifiers } from '../../../api/api'; const HivExposedInfant: React.FC<{ patientUuid: string; @@ -23,7 +25,11 @@ const HivExposedInfant: React.FC<{ const { t } = useTranslation(); const [relatives, setRelatives] = useState([]); const [relativeToIdentifierMap, setRelativeToIdentifierMap] = useState([]); - const { formNames, formUuids, encounterTypes, obsConcepts } = useConfig(); + const { identifiersTypes } = useConfig(); + const config = useConfig(); + const hivExposedInfantSummaryTabs = getMenuItemTabConfiguration(hivExposedInfantSummary, config); + const hivExposedFamilySummaryTabs = getMenuItemTabConfiguration(hivExposedFamilySummary, config); + const infantCardColumns = getSummaryCardProps(hivExposedInfantConfigSchema, config); const getParentRelationships = useCallback(async () => { let relationships = []; @@ -40,85 +46,6 @@ const HivExposedInfant: React.FC<{ getParentRelationships(); }, [getParentRelationships]); - const infantSummaryColumns: SummaryCardColumn[] = useMemo( - () => [ - { - key: 'artProphylaxisStatus', - header: t('artProphylaxisStatus', 'ART Prophylaxis Status'), - encounterTypes: [encounterTypes.infantPostnatal], - getObsValue: ([encounter]) => { - return getObsFromEncounter(encounter, obsConcepts.artProphylaxisStatus); - }, - }, - { - key: 'breastfeeding', - header: t('breastfeeding', 'Breastfeeding'), - encounterTypes: [encounterTypes.infantPostnatal], - getObsValue: ([encounter]) => { - return getObsFromEncounter(encounter, obsConcepts.breastfeedingStatus); - }, - }, - { - key: 'hivStatus', - header: t('hivStatus', 'HIV Status'), - encounterTypes: [encounterTypes.infantPostnatal], - getObsValue: ([encounter]) => { - return getObsFromEncounter(encounter, obsConcepts.finalTestResults); - }, - }, - { - key: 'finalOutcome', - header: t('finalOutcome', 'Final Outcome'), - encounterTypes: [encounterTypes.infantPostnatal], - getObsValue: ([encounter]) => { - return getObsFromEncounter(encounter, obsConcepts.outcomeStatus); - }, - }, - ], - [ - encounterTypes.infantPostnatal, - obsConcepts.artProphylaxisStatus, - obsConcepts.breastfeedingStatus, - obsConcepts.finalTestResults, - obsConcepts.outcomeStatus, - t, - ], - ); - - const hivMonitoringColumns: EncounterListColumn[] = useMemo(() => { - return [ - { - key: 'date', - header: t('date', 'Date'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.artStartDate, true); - }, - }, - { - key: 'testType', - header: t('testType', 'Test Type'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.testTypeConcept); - }, - }, - { - key: 'ageAtTimeOfTest', - header: t('ageAtTimeOfTest', 'Age at time of test'), - getValue: (encounter) => { - const artDate = getObsFromEncounter(encounter, obsConcepts.artStartDate); - return artDate ? dayjs().diff(dayjs(artDate), 'day') : '--'; - }, - }, - { - key: 'hivStatus', - header: t('hivStatus', 'HIV Status'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.finalTestResults); - }, - }, - ]; - }, [obsConcepts.artStartDate, obsConcepts.finalTestResults, obsConcepts.testTypeConcept, t]); - const familyHeaders = [ { key: 'pTrackerId', @@ -148,13 +75,13 @@ const HivExposedInfant: React.FC<{ const identifiers = await fetchPatientIdentifiers(patientUuid); if (identifiers) { pTrackerMap.pTrackerId = identifiers.find( - (id) => id.identifierType.uuid === encounterTypes.PTrackerIdentifierType, - ).identifier; + (id) => id.identifierType.uuid === identifiersTypes?.ptrackerIdentifierType, + )?.identifier; pTrackerMap.patientId = patientUuid; } return pTrackerMap; }, - [encounterTypes.PTrackerIdentifierType], + [identifiersTypes?.ptrackerIdentifierType], ); useEffect(() => { @@ -191,76 +118,25 @@ const HivExposedInfant: React.FC<{ return items; }, [relatives, relativeToIdentifierMap]); - const columnsChildPreviousVisit: EncounterListColumn[] = useMemo( - () => [ - { - key: 'visitType', - header: t('visitType', 'Visit Type'), - getValue: (encounter) => { - return encounter.encounterType.name; - }, - }, - { - key: 'visitDate', - header: t('visitDate', 'Visit date'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.infantVisitDate, true); - }, - }, - { - key: 'facility', - header: t('facility', 'Facility'), - getValue: (encounter) => { - return encounter.location.name; - }, - }, - { - key: 'nextFollowUpDate', - header: t('nextFollowUpDate', 'Next Follow-up date'), - getValue: (encounter) => { - return getObsFromEncounter(encounter, obsConcepts.followUpDateConcept, true); - }, - }, - { - key: 'actions', - header: t('actions', 'Actions'), - getValue: (encounter) => [ - { - form: { name: 'Infant - Postanal Form', package: 'child_health' }, - encounterUuid: encounter.uuid, - intent: '*', - label: t('viewDetails', 'View details'), - mode: 'view', - }, - ], - }, - ], - [obsConcepts.followUpDateConcept, obsConcepts.infantVisitDate, t], - ); - - const previousVisitsTitle = t('previousVisitsSummary', 'Previous Visits'); - return (
- + {hivExposedInfantSummaryTabs.map((tab) => ( + + ))} - + {hivExposedFamilySummaryTabs.map((tab) => ( + + ))}
); }; diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/infant-summary-config.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/infant-summary-config.json new file mode 100644 index 000000000..bbc2d4d7a --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/infant-summary-config.json @@ -0,0 +1,38 @@ +{ + "menuId": "infantSummary", + "cardTitle": "Infant's Summary", + "columns": [ + { + "id": "artProphylaxisStatus", + "title": "Breastfeeding", + "encounterTypes": [ + "infantPostnatalEncounterType" + ], + "concept": "artProphylaxisStatus" + }, + { + "id": "breastfeeding", + "title": "Breastfeeding", + "encounterTypes": [ + "infantPostnatalEncounterType" + ], + "concept": "breastfeedingStatus" + }, + { + "id": "hivStatus", + "title": "HIV status", + "encounterTypes": [ + "infantPostnatalEncounterType" + ], + "concept": "finalTestResults" + }, + { + "id": "finalOutcome", + "title": "Final Outcome", + "encounterTypes": [ + "infantPostnatalEncounterType" + ], + "concept": "outcomeStatus" + } + ] +} \ No newline at end of file diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/mother-previous-visit.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/mother-previous-visit.json new file mode 100644 index 000000000..baa1a444d --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/mother-previous-visit.json @@ -0,0 +1,81 @@ +{ + "menuId": "motherPreviousVisitSummary", + "tabDefinitions": [ + { + "tabName": "Visits", + "headerTitle": "Visits", + "displayText": "Visits", + "encounterType": "mchEncounterTypeEncounterType", + "columns": [ + { + "id": "visitType", + "title": "Visit Type", + "type": "visitType" + }, + { + "id": "visitDate", + "isDate": true, + "title": "Visit Date", + "concept": "visitDate" + }, + { + "id": "facility", + "title": "Facility", + "type": "location" + }, + { + "id": "nextFollowUpDate", + "isDate": true, + "title": "Next follow-up date", + "concept": "followUpDateConcept" + }, + { + "id": "actions", + "title": "Actions", + "actionOptions": [], + "conditionalActionOptions": [ + { + "formName": "antenatalFormName", + "package": "pmtct", + "label": "View details", + "mode": "view", + "dependantEncounter": "antenatalEncounterType" + }, + { + "formName": "labourAndDeliveryFormName", + "package": "pmtct", + "label": "View details", + "mode": "view", + "dependantEncounter": "laborAndDeliveryEncounterType" + }, + { + "formName": "motherPostnatalFormName", + "package": "pmtct", + "label": "View details", + "mode": "view", + "dependantEncounter": "covidTestStatusConcept_UUID" + } + ] + } + ], + "launchOptions": { + "displayText": "Add", + "moduleName": "@ohri/openmrs-esm-ohri-pmtct-app" + }, + "formList": [ + { + "name": "antenatalFormName", + "uuid": "antenatalFormUuid" + }, + { + "name": "labourAndDeliveryFormName", + "uuid": "labourAndDeliveryFormUuid" + }, + { + "name": "motherPostnatalFormName", + "uuid": "motherPostnatalFormUuid" + } + ] + } + ] + } \ No newline at end of file diff --git a/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/recent-pregnancy-config.json b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/recent-pregnancy-config.json new file mode 100644 index 000000000..8666e38fb --- /dev/null +++ b/packages/esm-ohri-pmtct-app/src/views/mch-summary/tabs/recent-pregnancy-config.json @@ -0,0 +1,24 @@ +{ + "menuId": "recentPregnancy", + "cardTitle": "Recent Pregnancy", + "columns": [ + { + "id": "motherHIVStatus", + "title": "Mother HIV Status", + "encounterTypes": [], + "mambaEtlData": "motherHivStatus" + }, + { + "id": "expectedDeliveryDate", + "title": "Expected Delivery Date", + "encounterTypes": [], + "mambaEtlData": "deliveryDate" + }, + { + "id": "motherStatus", + "title": "Mother Status", + "encounterTypes": [], + "mambaEtlData": "motherStatus" + } + ] +} \ No newline at end of file