diff --git a/packages/esm-patient-chart-app/src/dashboard.meta.ts b/packages/esm-patient-chart-app/src/dashboard.meta.ts
index dc827279a1..06fee43c45 100644
--- a/packages/esm-patient-chart-app/src/dashboard.meta.ts
+++ b/packages/esm-patient-chart-app/src/dashboard.meta.ts
@@ -9,3 +9,10 @@ export const encountersDashboardMeta = {
path: 'Visits',
title: 'Visits',
};
+
+export const activeVisitDashboardMeta = {
+ slot: 'patient-chart-active-visit-dashboard-slot',
+ columns: 1,
+ path: 'Active Visit',
+ title: 'Active Visit',
+};
diff --git a/packages/esm-patient-chart-app/src/index.ts b/packages/esm-patient-chart-app/src/index.ts
index d2831af5af..bf364f6b49 100644
--- a/packages/esm-patient-chart-app/src/index.ts
+++ b/packages/esm-patient-chart-app/src/index.ts
@@ -8,11 +8,11 @@ import {
import * as PatientCommonLib from '@openmrs/esm-patient-common-lib';
import { createDashboardLink } from '@openmrs/esm-patient-common-lib';
import { esmPatientChartSchema } from './config-schema';
+import { moduleName, spaBasePath } from './constants';
+import { summaryDashboardMeta, encountersDashboardMeta, activeVisitDashboardMeta } from './dashboard.meta';
+import { setupOfflineVisitsSync, setupCacheableRoutes } from './offline';
import { genericDashboardConfigSchema } from './side-nav/generic-dashboard.component';
import { genericNavGroupConfigSchema } from './side-nav/generic-nav-group.component';
-import { moduleName } from './constants';
-import { setupOfflineVisitsSync, setupCacheableRoutes } from './offline';
-import { summaryDashboardMeta, encountersDashboardMeta } from './dashboard.meta';
import addPastVisitActionButtonComponent from './actions-buttons/add-past-visit.component';
import cancelVisitActionButtonComponent from './actions-buttons/cancel-visit.component';
import currentVisitSummaryComponent from './visit/visits-widget/current-visit-summary.component';
@@ -30,6 +30,7 @@ import startVisitActionButtonOnPatientSearch from './visit/start-visit-button.co
import startVisitFormComponent from './visit/visit-form/visit-form.component';
import stopVisitActionButtonComponent from './actions-buttons/stop-visit.component';
import visitAttributeTagsComponent from './patient-banner-tags/visit-attribute-tags.component';
+import activeVisitDetailOverviewComponent from './visit/visits-widget/current-visit-summary.component';
// This allows @openmrs/esm-patient-common-lib to be accessed by modules that are not
// using webpack. This is used for ngx-formentry.
@@ -55,6 +56,11 @@ export function startupApp() {
'Print patient identifier sticker',
'Features to support printing a patient identifier sticker',
);
+ registerFeatureFlag(
+ 'activeVisitSummaryTab',
+ 'Active Visit Summary Tab',
+ 'This feature displays a summary of all forms filled in an encounter instead of displaying the encounters tab.',
+ );
}
export const root = getSyncLifecycle(patientChartPageComponent, { featureName: 'patient-chart', moduleName });
@@ -238,3 +244,16 @@ export const activeVisitActionsComponent = getAsyncLifecycle(
() => import('./visit/visits-widget/active-visit-buttons/active-visit-buttons'),
{ featureName: 'active-visit-actions', moduleName },
);
+
+export const activeVisitSummaryDashboardLink = getSyncLifecycle(
+ createDashboardLink({
+ ...activeVisitDashboardMeta,
+ moduleName,
+ }),
+ { featureName: 'activeVisitSummaryTab', moduleName },
+);
+
+export const activeVisitDetailOverview = getSyncLifecycle(activeVisitDetailOverviewComponent, {
+ featureName: 'active-visit-overview',
+ moduleName,
+});
diff --git a/packages/esm-patient-chart-app/src/routes.json b/packages/esm-patient-chart-app/src/routes.json
index 3bcd6bc778..8e0ce7a3e0 100644
--- a/packages/esm-patient-chart-app/src/routes.json
+++ b/packages/esm-patient-chart-app/src/routes.json
@@ -228,6 +228,30 @@
"online": true,
"offline": true,
"order": 1
+ },
+ {
+ "name": "active-visit-summary-dashboard",
+ "slot": "patient-chart-dashboard-slot",
+ "component": "activeVisitSummaryDashboardLink",
+ "featureFlag": "activeVisitSummaryTab",
+ "meta": {
+ "slot": "patient-chart-active-visit-dashboard-slot",
+ "columns": 1,
+ "path": "Active Visit"
+ },
+ "order": 12,
+ "online": true,
+ "offline": true
+ },
+ {
+ "name": "active-visit-overview",
+ "component": "activeVisitDetailOverview",
+ "slot": "patient-chart-active-visit-dashboard-slot",
+ "order": 1,
+ "meta": {
+ "title": "Active Visits",
+ "view": "Active Visits"
+ }
}
],
"modals": [
diff --git a/packages/esm-patient-chart-app/src/visit/visits-widget/active-visits-summary.component.tsx b/packages/esm-patient-chart-app/src/visit/visits-widget/active-visits-summary.component.tsx
new file mode 100644
index 0000000000..2de069bf0b
--- /dev/null
+++ b/packages/esm-patient-chart-app/src/visit/visits-widget/active-visits-summary.component.tsx
@@ -0,0 +1,80 @@
+import React from 'react';
+import { InlineLoading, Tab, Tabs, TabList, TabPanel, TabPanels } from '@carbon/react';
+import { EmptyState, ErrorState } from '@openmrs/esm-patient-common-lib';
+import { ExtensionSlot, formatDatetime, parseDate } from '@openmrs/esm-framework';
+import { useTranslation } from 'react-i18next';
+import { useVisits } from './visit.resource';
+import VisitSummary from './past-visits-components/visit-summary.component';
+import styles from './visit-detail-overview.scss';
+
+interface ActiveVisitOverviewComponentProps {
+ patientUuid: string;
+}
+
+function ActiveVisitDetailOverviewComponent({ patientUuid }: ActiveVisitOverviewComponentProps) {
+ const { t } = useTranslation();
+ const { visits, error, isLoading, mutateVisits } = useVisits(patientUuid);
+
+ const activeVisits = visits?.filter((visit) => visit.stopDatetime === null);
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return ;
+ }
+
+ return (
+
+
+
+ {activeVisits?.map((visit, index) => (
+
+ {t('activeVisitType', '{{visitType}} Visit', { visitType: visit?.visitType?.name })}
+
+ ))}
+
+
+ {activeVisits?.map((visit, index) => (
+
+ {visit && (
+
+
+
+
+
{visit.visitType.name}
+
+
{t('start', 'Start')}:
+ {formatDatetime(parseDate(visit.startDatetime))}
+ {visit.stopDatetime ? (
+ <>
+ {t('end', 'End')}:
+ {formatDatetime(parseDate(visit.stopDatetime))}
+ >
+ ) : null}
+
+
+
+
+
+
+
+
+
+ )}
+
+ ))}
+
+
+
+ );
+}
+
+export default ActiveVisitDetailOverviewComponent;
diff --git a/packages/esm-patient-chart-app/src/visit/visits-widget/current-visit-summary.test.tsx b/packages/esm-patient-chart-app/src/visit/visits-widget/current-visit-summary.test.tsx
index ee15bddb87..b8833c048d 100644
--- a/packages/esm-patient-chart-app/src/visit/visits-widget/current-visit-summary.test.tsx
+++ b/packages/esm-patient-chart-app/src/visit/visits-widget/current-visit-summary.test.tsx
@@ -1,11 +1,18 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
-import { useVisit, getConfig } from '@openmrs/esm-framework';
+import { useVisit, getConfig, useFeatureFlag } from '@openmrs/esm-framework';
import { waitForLoadingToFinish } from 'tools';
import CurrentVisitSummary from './current-visit-summary.component';
const mockGetConfig = jest.mocked(getConfig);
const mockUseVisits = jest.mocked(useVisit);
+const mockedUseFeatureFlag = useFeatureFlag as jest.Mock;
+
+jest.mock('@openmrs/esm-framework', () => ({
+ ...jest.requireActual('@openmrs/esm-framework/mock'),
+ useVisits: jest.fn(),
+ getConfig: jest.fn(),
+}));
describe('CurrentVisitSummary', () => {
test('renders an empty state when there is no active visit', () => {
@@ -18,6 +25,7 @@ describe('CurrentVisitSummary', () => {
isValidating: false,
mutate: jest.fn(),
});
+ mockedUseFeatureFlag.mockReturnValueOnce(false);
render();
expect(screen.getByText(/current visit/i)).toBeInTheDocument();
@@ -26,6 +34,7 @@ describe('CurrentVisitSummary', () => {
test('renders a visit summary when for the active visit', async () => {
mockGetConfig.mockResolvedValue({ htmlFormEntryForms: [] });
+ mockedUseFeatureFlag.mockReturnValueOnce(false);
mockUseVisits.mockReturnValueOnce({
activeVisit: null,
currentVisit: {
diff --git a/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx b/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx
index 8489747d4a..f1b7b051cd 100644
--- a/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx
+++ b/packages/esm-patient-chart-app/src/visit/visits-widget/past-visits-components/visit-summary.component.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo } from 'react';
+import React, { useMemo, useState } from 'react';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { Tab, Tabs, TabList, TabPanel, TabPanels, Tag } from '@carbon/react';
@@ -12,6 +12,7 @@ import {
useConnectedExtensions,
useLayoutType,
type Visit,
+ useFeatureFlag,
} from '@openmrs/esm-framework';
import {
type Order,
@@ -22,12 +23,12 @@ import {
type Diagnosis,
mapEncounters,
} from '../visit.resource';
-import VisitsTable from './visits-table/visits-table.component';
import MedicationSummary from './medications-summary.component';
import NotesSummary from './notes-summary.component';
import TestsSummary from './tests-summary.component';
import type { ExternalOverviewProps } from '@openmrs/esm-patient-common-lib';
import styles from './visit-summary.scss';
+import VisitsTable from './visits-table';
interface DiagnosisItem {
diagnosis: string;
@@ -112,6 +113,15 @@ const VisitSummary: React.FC = ({ visit, patientUuid }) => {
};
}, [visit?.encounters]);
+ const isactiveVisitSummaryTabEnabled = useFeatureFlag('activeVisitSummaryTab');
+ const [selectedIndex, setSelectedIndex] = useState(0);
+ const [selectedTab, setSelectedTab] = useState(null);
+
+ const handleTabChange = (evt) => {
+ setSelectedTab(visit.encounters[evt.selectedIndex - 3]?.uuid || ''); // Assuming the first 3 tabs are predefined
+ setSelectedIndex(evt.selectedIndex);
+ };
+
return (
{t('diagnoses', 'Diagnoses')}
@@ -128,7 +138,11 @@ const VisitSummary: React.FC
= ({ visit, patientUuid }) => {
)}
-
+
= ({ visit, patientUuid }) => {
>
{t('medications', 'Medications')}
-
- {t('encounters_title', 'Encounters')}
-
+ {!isactiveVisitSummaryTabEnabled ? (
+
+ {t('encounters_title', 'Encounters')}
+
+ ) : (
+ visit?.encounters?.length > 0 &&
+ visit?.encounters
+ .filter((enc) => !!enc.form)
+ .map((enc, ind) => (
+
+ {enc?.form?.name ? enc?.form?.name : enc?.form?.display}
+
+ ))
+ )}
{extensions.map((extension, index) => (
{t(extension.meta.title, {
@@ -173,9 +198,31 @@ const VisitSummary: React.FC = ({ visit, patientUuid }) => {
-
-
-
+ {!isactiveVisitSummaryTabEnabled ? (
+
+
+
+ ) : (
+ visit?.encounters?.length > 0 &&
+ visit?.encounters
+ .filter((enc) => !!enc.form)
+ .map((enc, ind) => (
+
+ {selectedTab === enc.uuid && (
+ {},
+ }}
+ />
+ )}
+
+ ))
+ )}
diff --git a/packages/esm-patient-chart-app/src/visit/visits-widget/visit-detail-overview.test.tsx b/packages/esm-patient-chart-app/src/visit/visits-widget/visit-detail-overview.test.tsx
index d89434fb9b..8fc31f140b 100644
--- a/packages/esm-patient-chart-app/src/visit/visits-widget/visit-detail-overview.test.tsx
+++ b/packages/esm-patient-chart-app/src/visit/visits-widget/visit-detail-overview.test.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/react';
-import { openmrsFetch, getConfig, useConfig, getDefaultsFromConfigSchema } from '@openmrs/esm-framework';
+import { openmrsFetch, getConfig, useConfig, getDefaultsFromConfigSchema, useFeatureFlag } from '@openmrs/esm-framework';
import { esmPatientChartSchema, type ChartConfig } from '../../config-schema';
import { mockPatient, renderWithSwr, waitForLoadingToFinish } from 'tools';
import { visitOverviewDetailMockData } from '__mocks__';
@@ -10,6 +10,13 @@ import VisitDetailOverview from './visit-detail-overview.component';
const mockGetConfig = getConfig as jest.Mock;
const mockOpenmrsFetch = openmrsFetch as jest.Mock;
const mockUseConfig = jest.mocked(useConfig);
+const mockedUseFeatureFlag = useFeatureFlag as jest.Mock;
+
+jest.mock('@openmrs/esm-framework', () => ({
+ ...jest.requireActual('@openmrs/esm-framework'),
+ getVisitsForPatient: jest.fn(),
+ userHasAccess: jest.fn().mockImplementation((privilege, _) => (privilege ? false : true)),
+}));
mockUseConfig.mockReturnValue({
...getDefaultsFromConfigSchema(esmPatientChartSchema),
@@ -60,6 +67,7 @@ describe('VisitDetailOverview', () => {
...getDefaultsFromConfigSchema(esmPatientChartSchema),
showAllEncountersTab: true,
});
+ mockedUseFeatureFlag.mockReturnValueOnce(false);
renderWithSwr();
@@ -96,6 +104,7 @@ describe('VisitDetailOverview', () => {
...getDefaultsFromConfigSchema(esmPatientChartSchema),
showAllEncountersTab: false,
});
+ mockedUseFeatureFlag.mockReturnValueOnce(false);
renderWithSwr();
diff --git a/packages/esm-patient-chart-app/translations/en.json b/packages/esm-patient-chart-app/translations/en.json
index c17f4b4a89..86810e6106 100644
--- a/packages/esm-patient-chart-app/translations/en.json
+++ b/packages/esm-patient-chart-app/translations/en.json
@@ -1,4 +1,5 @@
{
+ "activeVisitType": "{{visitType}} Visit",
"addAPastVisit": "Add a past visit",
"addPastVisit": "Add past visit",
"addPastVisitText": "You can add a new past visit or update an old one. Choose from one of the options below to continue.",
@@ -67,6 +68,7 @@
"errorUpdatingVisitDetails": "Error updating visit details",
"errorWhenRestoringVisit": "Error occured when restoring {{visit}}",
"failedDeleting": "couldn't be deleted",
+ "failedToLoadActiveVisits": "Failed loading active visits",
"failedToLoadCurrentVisit": "Failed loading current visit",
"female": "Female",
"fieldRequired": "This field is required",