@openmrs/esm-patient-chart-app 11.3.1-patch.9064 → 11.3.1-patch.9310

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.turbo/turbo-build.log +24 -21
  2. package/dist/1815.js +2 -0
  3. package/dist/1815.js.map +1 -0
  4. package/dist/2761.js +1 -1
  5. package/dist/2761.js.map +1 -1
  6. package/dist/2859.js +1 -1
  7. package/dist/2859.js.map +1 -1
  8. package/dist/3697.js +1 -0
  9. package/dist/3697.js.map +1 -0
  10. package/dist/4055.js +1 -1
  11. package/dist/4300.js +1 -1
  12. package/dist/4718.js +1 -1
  13. package/dist/4754.js +1 -1
  14. package/dist/5205.js +1 -1
  15. package/dist/5670.js +1 -1
  16. package/dist/5670.js.map +1 -1
  17. package/dist/5827.js +1 -0
  18. package/dist/5827.js.map +1 -0
  19. package/dist/6336.js +1 -0
  20. package/dist/6336.js.map +1 -0
  21. package/dist/6411.js +1 -1
  22. package/dist/6411.js.map +1 -1
  23. package/dist/6529.js +1 -1
  24. package/dist/6568.js +1 -0
  25. package/dist/6568.js.map +1 -0
  26. package/dist/6924.js +1 -0
  27. package/dist/6924.js.map +1 -0
  28. package/dist/7816.js +2 -0
  29. package/dist/7816.js.map +1 -0
  30. package/dist/7818.js +1 -0
  31. package/dist/7818.js.map +1 -0
  32. package/dist/7822.js +1 -1
  33. package/dist/7822.js.map +1 -1
  34. package/dist/8260.js +1 -0
  35. package/dist/8260.js.map +1 -0
  36. package/dist/8278.js +1 -0
  37. package/dist/8278.js.map +1 -0
  38. package/dist/8454.js +1 -1
  39. package/dist/8454.js.map +1 -1
  40. package/dist/8709.js +1 -1
  41. package/dist/8709.js.map +1 -1
  42. package/dist/{4727.js → 9294.js} +1 -1
  43. package/dist/9294.js.map +1 -0
  44. package/dist/9329.js +1 -0
  45. package/dist/9329.js.map +1 -0
  46. package/dist/main.js +1 -1
  47. package/dist/main.js.map +1 -1
  48. package/dist/openmrs-esm-patient-chart-app.js +1 -1
  49. package/dist/openmrs-esm-patient-chart-app.js.buildmanifest.json +312 -287
  50. package/dist/openmrs-esm-patient-chart-app.js.map +1 -1
  51. package/dist/routes.json +1 -1
  52. package/package.json +3 -3
  53. package/src/actions-buttons/delete-visit.component.tsx +8 -3
  54. package/src/actions-buttons/mark-patient-deceased.component.tsx +2 -2
  55. package/src/actions-buttons/start-visit.component.tsx +10 -5
  56. package/src/actions-buttons/start-visit.test.tsx +9 -5
  57. package/src/actions-buttons/stop-visit.component.tsx +1 -1
  58. package/src/clinical-views/encounter-list/{encounter-list-tabs.component.tsx → encounter-list-tabs.extension.tsx} +10 -6
  59. package/src/clinical-views/encounter-list/tag.component.test.tsx +306 -0
  60. package/src/clinical-views/encounter-list/tag.component.tsx +27 -28
  61. package/src/clinical-views/utils/helpers.ts +2 -2
  62. package/src/config-schema.ts +0 -7
  63. package/src/index.ts +21 -22
  64. package/src/mark-patient-deceased/mark-patient-deceased-form.test.tsx +22 -11
  65. package/src/mark-patient-deceased/mark-patient-deceased-form.workspace.tsx +147 -138
  66. package/src/patient-banner-tags/{visit-attribute-tags.component.tsx → visit-attribute-tags.extension.tsx} +9 -4
  67. package/src/patient-chart/chart-review/dashboard-view.component.tsx +2 -2
  68. package/src/patient-chart/patient-chart.component.tsx +19 -36
  69. package/src/patient-chart/patient-chart.resources.ts +150 -0
  70. package/src/routes.json +17 -6
  71. package/src/visit/hooks/useDeleteVisit.test.tsx +39 -42
  72. package/src/visit/hooks/useDeleteVisit.tsx +33 -17
  73. package/src/visit/start-visit-button.component.tsx +2 -2
  74. package/src/visit/start-visit-button.test.tsx +2 -2
  75. package/src/visit/visit-action-items/edit-visit-details.component.tsx +29 -8
  76. package/src/visit/visit-form/base-visit-type.component.tsx +2 -2
  77. package/src/visit/visit-form/exported-visit-form.workspace.tsx +697 -0
  78. package/src/visit/visit-form/visit-attribute-type.component.tsx +2 -1
  79. package/src/visit/visit-form/visit-form.resource.ts +2 -1
  80. package/src/visit/visit-form/visit-form.test.tsx +28 -25
  81. package/src/visit/visit-form/visit-form.workspace.tsx +63 -643
  82. package/src/visit/visit-history-table/visit-actions-cell.component.tsx +3 -2
  83. package/src/visit/visit-history-table/visit-date-cell.component.tsx +1 -0
  84. package/src/visit/visit-history-table/visit-diagnoses-cell.component.tsx +1 -0
  85. package/src/visit/visit-history-table/visit-history-table.component.tsx +3 -2
  86. package/src/visit/visit-history-table/visit-type-cell.component.tsx +1 -0
  87. package/src/visit/visit-prompt/{delete-visit-dialog.component.tsx → delete-visit-dialog.modal.tsx} +10 -4
  88. package/src/visit/visit-prompt/delete-visit-dialog.test.tsx +21 -3
  89. package/src/visit/visit-prompt/{end-visit-dialog.component.tsx → end-visit-dialog.modal.tsx} +7 -1
  90. package/src/visit/visit-prompt/end-visit-dialog.test.tsx +20 -1
  91. package/src/visit/visit-prompt/{start-visit-dialog.component.tsx → start-visit-dialog.modal.tsx} +10 -4
  92. package/src/visit/visit-prompt/start-visit-dialog.test.tsx +3 -3
  93. package/src/visit/visits-widget/active-visit-buttons/active-visit-buttons.tsx +7 -6
  94. package/src/visit/visits-widget/current-visit-summary.extension.tsx +48 -0
  95. package/src/visit/visits-widget/current-visit-summary.test.tsx +45 -25
  96. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.component.tsx +15 -37
  97. package/src/visit/visits-widget/past-visits-components/encounters-table/encounters-table.resource.ts +0 -1
  98. package/src/visit/visits-widget/visit-context/retrospective-data-date-time-picker/retrospective-date-time-picker.component.tsx +6 -7
  99. package/src/visit/visits-widget/visit-context/{visit-context-header.component.tsx → visit-context-header.extension.tsx} +17 -15
  100. package/src/visit/visits-widget/visit-context/visit-context-header.test.tsx +35 -29
  101. package/src/visit/visits-widget/visit-context/visit-context-switcher.modal.tsx +15 -13
  102. package/src/visit/visits-widget/visit-context/visit-context-switcher.test.tsx +31 -9
  103. package/src/visit/visits-widget/visit-detail-overview.component.tsx +3 -2
  104. package/src/visit/visits-widget/visit-detail-overview.test.tsx +4 -4
  105. package/src/visit/visits-widget/visit.resource.tsx +1 -1
  106. package/translations/en.json +1 -2
  107. package/translations/fr.json +15 -15
  108. package/dist/1568.js +0 -2
  109. package/dist/1568.js.map +0 -1
  110. package/dist/2442.js +0 -1
  111. package/dist/2442.js.map +0 -1
  112. package/dist/2537.js +0 -1
  113. package/dist/2537.js.map +0 -1
  114. package/dist/276.js +0 -1
  115. package/dist/276.js.map +0 -1
  116. package/dist/3042.js +0 -1
  117. package/dist/3042.js.map +0 -1
  118. package/dist/3119.js +0 -1
  119. package/dist/3119.js.map +0 -1
  120. package/dist/3905.js +0 -1
  121. package/dist/3905.js.map +0 -1
  122. package/dist/4713.js +0 -1
  123. package/dist/4713.js.map +0 -1
  124. package/dist/4727.js.map +0 -1
  125. package/dist/5048.js +0 -1
  126. package/dist/5048.js.map +0 -1
  127. package/dist/6650.js +0 -2
  128. package/dist/6650.js.map +0 -1
  129. package/src/visit/visits-widget/current-visit-summary.component.tsx +0 -55
  130. /package/dist/{1568.js.LICENSE.txt → 1815.js.LICENSE.txt} +0 -0
  131. /package/dist/{6650.js.LICENSE.txt → 7816.js.LICENSE.txt} +0 -0
@@ -4,14 +4,15 @@ import styles from './visit-actions-cell.scss';
4
4
 
5
5
  interface Props {
6
6
  visit: Visit;
7
+ patient: fhir.Patient;
7
8
  }
8
9
 
9
- const VisitActionsCell: React.FC<Props> = ({ visit }) => {
10
+ const VisitActionsCell: React.FC<Props> = ({ visit, patient }) => {
10
11
  return (
11
12
  <ExtensionSlot
12
13
  name="visit-detail-overview-actions"
13
14
  className={styles.visitActions}
14
- state={{ patientUuid: visit.patient.uuid, visit, compact: true }}
15
+ state={{ patientUuid: visit.patient.uuid, patient, visit, compact: true }}
15
16
  />
16
17
  );
17
18
  };
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
4
4
 
5
5
  interface Props {
6
6
  visit: Visit;
7
+ patient: fhir.Patient;
7
8
  }
8
9
 
9
10
  const VisitDateCell: React.FC<Props> = ({ visit }) => {
@@ -3,6 +3,7 @@ import React from 'react';
3
3
 
4
4
  interface Props {
5
5
  visit: Visit;
6
+ patient: fhir.Patient;
6
7
  }
7
8
 
8
9
  const VisitDiagnosisCell: React.FC<Props> = ({ visit }) => {
@@ -27,12 +27,13 @@ import styles from './visit-history-table.scss';
27
27
 
28
28
  interface VisitHistoryTableProps {
29
29
  patientUuid: string;
30
+ patient: fhir.Patient;
30
31
  }
31
32
 
32
33
  /**
33
34
  * This show a list of visit histories in the visit tab in patient chart
34
35
  */
35
- const VisitHistoryTable: React.FC<VisitHistoryTableProps> = ({ patientUuid }) => {
36
+ const VisitHistoryTable: React.FC<VisitHistoryTableProps> = ({ patientUuid, patient }) => {
36
37
  const defaultPageSize = 10;
37
38
  const [pageSize, setPageSize] = useState(defaultPageSize);
38
39
  const pageSizes = [10, 20, 30, 40, 50];
@@ -54,7 +55,7 @@ const VisitHistoryTable: React.FC<VisitHistoryTableProps> = ({ patientUuid }) =>
54
55
  const rowData = visits?.map((visit) => {
55
56
  const row: Record<string, JSX.Element | string> = { id: visit.uuid };
56
57
  for (const { key, CellComponent } of columns) {
57
- row[key] = <CellComponent key={key} visit={visit} />;
58
+ row[key] = <CellComponent key={key} visit={visit} patient={patient} />;
58
59
  }
59
60
  return row;
60
61
  });
@@ -3,6 +3,7 @@ import React from 'react';
3
3
 
4
4
  interface Props {
5
5
  visit: Visit;
6
+ patient: fhir.Patient;
6
7
  }
7
8
 
8
9
  const VisitTypeCell: React.FC<Props> = ({ visit }) => {
@@ -8,12 +8,18 @@ import styles from './start-visit-dialog.scss';
8
8
  interface DeleteVisitDialogProps {
9
9
  closeModal: () => void;
10
10
  patientUuid: string;
11
- visit: Visit;
11
+ activeVisit: Visit;
12
+ mutateActiveVisit: () => void;
12
13
  }
13
14
 
14
- const DeleteVisitDialog: React.FC<DeleteVisitDialogProps> = ({ closeModal, patientUuid, visit }) => {
15
+ const DeleteVisitDialog: React.FC<DeleteVisitDialogProps> = ({
16
+ closeModal,
17
+ mutateActiveVisit,
18
+ patientUuid,
19
+ activeVisit,
20
+ }) => {
15
21
  const { t } = useTranslation();
16
- const { isDeletingVisit, initiateDeletingVisit } = useDeleteVisit(visit, closeModal);
22
+ const { isDeletingVisit, initiateDeletingVisit } = useDeleteVisit(activeVisit, mutateActiveVisit, closeModal);
17
23
 
18
24
  return (
19
25
  <div>
@@ -24,7 +30,7 @@ const DeleteVisitDialog: React.FC<DeleteVisitDialogProps> = ({ closeModal, patie
24
30
  <ModalBody>
25
31
  <p className={styles.body}>
26
32
  {t('confirmDeleteVisitText', 'Deleting this {{visit}} will delete its associated encounters.', {
27
- visit: visit?.visitType?.display ?? t('visit', 'Visit'),
33
+ visit: activeVisit?.visitType?.display ?? t('visit', 'Visit'),
28
34
  })}
29
35
  </p>
30
36
  </ModalBody>
@@ -4,11 +4,12 @@ import userEvent from '@testing-library/user-event';
4
4
  import { mockCurrentVisit } from '__mocks__';
5
5
  import React from 'react';
6
6
  import { mockPatient } from 'tools';
7
- import DeleteVisitDialog from './delete-visit-dialog.component';
7
+ import DeleteVisitDialog from './delete-visit-dialog.modal';
8
8
 
9
9
  const mockCloseModal = jest.fn();
10
10
  const mockOpenmrsFetch = jest.mocked(openmrsFetch);
11
11
  const mockShowSnackbar = jest.mocked(showSnackbar);
12
+ const mockActiveVisit = jest.fn();
12
13
 
13
14
  describe('Delete visit', () => {
14
15
  it('voids the visit and voids its associated encounters', async () => {
@@ -21,7 +22,14 @@ describe('Delete visit', () => {
21
22
 
22
23
  mockOpenmrsFetch.mockResolvedValue(response as FetchResponse);
23
24
 
24
- render(<DeleteVisitDialog visit={mockCurrentVisit} closeModal={mockCloseModal} patientUuid={mockPatient.id} />);
25
+ render(
26
+ <DeleteVisitDialog
27
+ activeVisit={mockCurrentVisit}
28
+ mutateActiveVisit={mockActiveVisit}
29
+ closeModal={mockCloseModal}
30
+ patientUuid={mockPatient.id}
31
+ />,
32
+ );
25
33
 
26
34
  const cancelButton = screen.getByRole('button', { name: /^cancel$/i });
27
35
  const deleteVisitButton = screen.getByRole('button', { name: /delete visit$/i });
@@ -46,6 +54,7 @@ describe('Delete visit', () => {
46
54
  subtitle: 'Facility Visit deleted successfully',
47
55
  }),
48
56
  );
57
+ expect(mockActiveVisit).toHaveBeenCalledTimes(1);
49
58
  });
50
59
 
51
60
  it('displays an error notification if there was problem with deleting a visit', async () => {
@@ -53,7 +62,14 @@ describe('Delete visit', () => {
53
62
 
54
63
  mockOpenmrsFetch.mockRejectedValueOnce({ message: 'Internal server error', status: 500 });
55
64
 
56
- render(<DeleteVisitDialog visit={mockCurrentVisit} closeModal={mockCloseModal} patientUuid={mockPatient.id} />);
65
+ render(
66
+ <DeleteVisitDialog
67
+ activeVisit={mockCurrentVisit}
68
+ mutateActiveVisit={mockActiveVisit}
69
+ closeModal={mockCloseModal}
70
+ patientUuid={mockPatient.id}
71
+ />,
72
+ );
57
73
 
58
74
  const cancelButton = screen.getByRole('button', { name: /^cancel$/i });
59
75
  const deleteVisitButton = screen.getByRole('button', { name: /delete visit$/i });
@@ -74,5 +90,7 @@ describe('Delete visit', () => {
74
90
  kind: 'error',
75
91
  title: 'Error deleting visit',
76
92
  });
93
+
94
+ expect(mockActiveVisit).toHaveBeenCalledTimes(1);
77
95
  });
78
96
  });
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
4
4
  import { showSnackbar, updateVisit, useVisit } from '@openmrs/esm-framework';
5
+ import { usePatientChartStore } from '@openmrs/esm-patient-common-lib';
5
6
  import styles from './end-visit-dialog.scss';
6
7
 
7
8
  interface EndVisitDialogProps {
@@ -13,10 +14,13 @@ interface EndVisitDialogProps {
13
14
  * This modal shows up when user clicks on the "End visit" button in the action menu within the
14
15
  * patient banner. It should only show when the patient has an active visit. See stop-visit.component.tsx
15
16
  * for the button.
17
+ *
18
+ * This dialog uses the patient chart store and SHOULD only be mounted within the patient chart
16
19
  */
17
20
  const EndVisitDialog: React.FC<EndVisitDialogProps> = ({ patientUuid, closeModal }) => {
18
21
  const { t } = useTranslation();
19
22
  const { activeVisit, mutate } = useVisit(patientUuid);
23
+ const { visitContext, setVisitContext } = usePatientChartStore(patientUuid);
20
24
 
21
25
  const handleEndVisit = () => {
22
26
  if (activeVisit) {
@@ -31,7 +35,9 @@ const EndVisitDialog: React.FC<EndVisitDialogProps> = ({ patientUuid, closeModal
31
35
  mutate();
32
36
  window.dispatchEvent(new CustomEvent('queue-entry-updated'));
33
37
  closeModal();
34
-
38
+ if (visitContext?.uuid == activeVisit.uuid) {
39
+ setVisitContext(null, null);
40
+ }
35
41
  showSnackbar({
36
42
  isLowContrast: true,
37
43
  kind: 'success',
@@ -3,7 +3,8 @@ import userEvent from '@testing-library/user-event';
3
3
  import { screen, render } from '@testing-library/react';
4
4
  import { showSnackbar, updateVisit, useVisit, type Visit, type FetchResponse } from '@openmrs/esm-framework';
5
5
  import { mockCurrentVisit } from '__mocks__';
6
- import EndVisitDialog from './end-visit-dialog.component';
6
+ import EndVisitDialog from './end-visit-dialog.modal';
7
+ import { usePatientChartStore } from '@openmrs/esm-patient-common-lib';
7
8
 
8
9
  const endVisitPayload = {
9
10
  stopDatetime: expect.any(Date),
@@ -15,6 +16,22 @@ const mockShowSnackbar = jest.mocked(showSnackbar);
15
16
  const mockUseVisit = jest.mocked(useVisit);
16
17
  const mockUpdateVisit = jest.mocked(updateVisit);
17
18
 
19
+ const mockUsePatientChartStore = jest.mocked(usePatientChartStore);
20
+ const mockSetVisitContext = jest.fn();
21
+
22
+ jest.mock('@openmrs/esm-patient-common-lib', () => ({
23
+ usePatientChartStore: jest.fn(),
24
+ }));
25
+
26
+ mockUsePatientChartStore.mockReturnValue({
27
+ patientUuid: 'patient-123',
28
+ patient: null,
29
+ visitContext: mockCurrentVisit,
30
+ mutateVisitContext: jest.fn(),
31
+ setPatient: jest.fn(),
32
+ setVisitContext: mockSetVisitContext,
33
+ });
34
+
18
35
  describe('End visit dialog', () => {
19
36
  beforeEach(() => {
20
37
  mockUseVisit.mockReturnValue({
@@ -66,6 +83,8 @@ describe('End visit dialog', () => {
66
83
  kind: 'success',
67
84
  title: 'Visit ended',
68
85
  });
86
+
87
+ expect(mockSetVisitContext).toHaveBeenCalledTimes(1);
69
88
  });
70
89
 
71
90
  test('displays an error snackbar if there was a problem ending a visit', async () => {
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
4
- import { launchWorkspace } from '@openmrs/esm-framework';
4
+ import { launchWorkspace2 } from '@openmrs/esm-framework';
5
5
  import { launchPatientChartWithWorkspaceOpen } from '@openmrs/esm-patient-common-lib';
6
6
  import styles from './start-visit-dialog.scss';
7
7
 
@@ -9,9 +9,15 @@ interface StartVisitDialogProps {
9
9
  patientUuid: string;
10
10
  closeModal: () => void;
11
11
  launchPatientChart?: boolean;
12
+ onVisitStarted?: () => void;
12
13
  }
13
14
 
14
- const StartVisitDialog: React.FC<StartVisitDialogProps> = ({ patientUuid, closeModal, launchPatientChart }) => {
15
+ const StartVisitDialog: React.FC<StartVisitDialogProps> = ({
16
+ patientUuid,
17
+ closeModal,
18
+ launchPatientChart,
19
+ onVisitStarted,
20
+ }) => {
15
21
  const { t } = useTranslation();
16
22
 
17
23
  const handleStartNewVisit = useCallback(() => {
@@ -22,11 +28,11 @@ const StartVisitDialog: React.FC<StartVisitDialogProps> = ({ patientUuid, closeM
22
28
  additionalProps: { openedFrom: 'patient-chart-start-visit' },
23
29
  });
24
30
  } else {
25
- launchWorkspace('start-visit-workspace-form', { openedFrom: 'patient-chart-start-visit' });
31
+ launchWorkspace2('start-visit-workspace-form', { openedFrom: 'patient-chart-start-visit', onVisitStarted });
26
32
  }
27
33
 
28
34
  closeModal();
29
- }, [closeModal, patientUuid, launchPatientChart]);
35
+ }, [closeModal, patientUuid, launchPatientChart, onVisitStarted]);
30
36
 
31
37
  const modalHeaderText = t('noActiveVisit', 'No active visit');
32
38
 
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
- import { launchWorkspace } from '@openmrs/esm-framework';
5
- import StartVisitDialog from './start-visit-dialog.component';
4
+ import { launchWorkspace2 } from '@openmrs/esm-framework';
5
+ import StartVisitDialog from './start-visit-dialog.modal';
6
6
 
7
7
  const defaultProps = {
8
8
  patientUuid: 'some-uuid',
@@ -10,7 +10,7 @@ const defaultProps = {
10
10
  visitType: null,
11
11
  };
12
12
 
13
- const mockLaunchWorkspace = jest.mocked(launchWorkspace);
13
+ const mockLaunchWorkspace = jest.mocked(launchWorkspace2);
14
14
 
15
15
  describe('StartVisit', () => {
16
16
  test('should launch start visit form', async () => {
@@ -20,13 +20,14 @@ const ActiveVisitActions: React.FC<ActiveVisitActionsInterface> = ({ visit, pati
20
20
  const isTablet = layout === 'tablet';
21
21
  const isMobile = layout === 'phone';
22
22
 
23
- const launchAllergiesFormWorkspace = useLaunchWorkspaceRequiringVisit('patient-allergy-form-workspace');
24
- const launchAppointmentsFormWorkspace = useLaunchWorkspaceRequiringVisit('appointments-form-workspace');
25
- const launchClinicalFormsWorkspace = useLaunchWorkspaceRequiringVisit('clinical-forms-workspace');
26
- const launchConditionsFormWorkspace = useLaunchWorkspaceRequiringVisit('conditions-form-workspace');
27
- const launchOrderBasketFormWorkspace = useLaunchWorkspaceRequiringVisit('order-basket');
28
- const launchVisitNotesFormWorkspace = useLaunchWorkspaceRequiringVisit('visit-notes-form-workspace');
23
+ const launchAllergiesFormWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, 'patient-allergy-form-workspace');
24
+ const launchAppointmentsFormWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, 'appointments-form-workspace');
25
+ const launchClinicalFormsWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, 'clinical-forms-workspace');
26
+ const launchConditionsFormWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, 'conditions-form-workspace');
27
+ const launchOrderBasketFormWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, 'order-basket');
28
+ const launchVisitNotesFormWorkspace = useLaunchWorkspaceRequiringVisit(patientUuid, 'visit-notes-form-workspace');
29
29
  const launchVitalsAndBiometricsFormWorkspace = useLaunchWorkspaceRequiringVisit(
30
+ patientUuid,
30
31
  'patient-vitals-biometrics-form-workspace',
31
32
  );
32
33
 
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { launchWorkspace2 } from '@openmrs/esm-framework';
4
+ import { CardHeader, EmptyState, usePatientChartStore } from '@openmrs/esm-patient-common-lib';
5
+ import VisitSummary from './past-visits-components/visit-summary.component';
6
+ import styles from './current-visit-summary.scss';
7
+
8
+ interface CurrentVisitSummaryProps {
9
+ patientUuid: string;
10
+ }
11
+
12
+ /**
13
+ * This extension is not used in the refapp.
14
+ * This extension uses the patient chart store and SHOULD only be mounted within the patient chart
15
+ */
16
+ const CurrentVisitSummary: React.FC<CurrentVisitSummaryProps> = ({ patientUuid }) => {
17
+ const { t } = useTranslation();
18
+ const { patientUuid: storePatientUuid, visitContext } = usePatientChartStore(patientUuid);
19
+
20
+ if (patientUuid !== storePatientUuid) {
21
+ return null;
22
+ }
23
+
24
+ if (!visitContext) {
25
+ return (
26
+ <EmptyState
27
+ headerTitle={t('currentVisit', 'Current visit')}
28
+ displayText={t('noActiveVisitMessage', 'active visits')}
29
+ launchForm={() =>
30
+ launchWorkspace2('start-visit-workspace-form', { openedFrom: 'patient-chart-current-visit-summary' })
31
+ }
32
+ />
33
+ );
34
+ }
35
+
36
+ return (
37
+ <div className={styles.container}>
38
+ <CardHeader title={t('currentVisit', 'Current visit')}>
39
+ <span />
40
+ </CardHeader>
41
+ <div className={styles.visitSummaryCard}>
42
+ <VisitSummary visit={visitContext} patientUuid={patientUuid} />
43
+ </div>
44
+ </div>
45
+ );
46
+ };
47
+
48
+ export default CurrentVisitSummary;
@@ -1,34 +1,52 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
- import { useVisit, getConfig } from '@openmrs/esm-framework';
4
- import { waitForLoadingToFinish } from 'tools';
5
- import CurrentVisitSummary from './current-visit-summary.component';
3
+ import { getConfig } from '@openmrs/esm-framework';
4
+ import { mockPatient, waitForLoadingToFinish } from 'tools';
5
+ import { usePatientChartStore } from '@openmrs/esm-patient-common-lib';
6
+ import CurrentVisitSummary from './current-visit-summary.extension';
6
7
 
7
8
  const mockGetConfig = jest.mocked(getConfig);
8
- const mockUseVisits = jest.mocked(useVisit);
9
+ const mockUsePatientChartStore = jest.mocked(usePatientChartStore);
10
+
11
+ jest.mock('@openmrs/esm-patient-common-lib', () => ({
12
+ ...jest.requireActual('@openmrs/esm-patient-common-lib'),
13
+ usePatientChartStore: jest.fn(),
14
+ }));
9
15
 
10
16
  describe('CurrentVisitSummary', () => {
11
17
  test('renders an empty state when there is no active visit', () => {
12
- mockUseVisits.mockReturnValueOnce({
13
- activeVisit: null,
14
- currentVisit: null,
15
- currentVisitIsRetrospective: false,
16
- error: null,
17
- isLoading: false,
18
- isValidating: false,
19
- mutate: jest.fn(),
18
+ mockUsePatientChartStore.mockReturnValue({
19
+ patientUuid: mockPatient.id,
20
+ patient: mockPatient,
21
+ visitContext: null,
22
+ mutateVisitContext: null,
23
+ setPatient: jest.fn(),
24
+ setVisitContext: jest.fn(),
20
25
  });
21
-
22
- render(<CurrentVisitSummary patientUuid="some-uuid" />);
26
+ render(<CurrentVisitSummary patientUuid={mockPatient.id} />);
23
27
  expect(screen.getByText(/current visit/i)).toBeInTheDocument();
24
- expect(screen.getByText('There are no active visit to display for this patient')).toBeInTheDocument();
28
+ expect(screen.getByText('There are no active visits to display for this patient')).toBeInTheDocument();
29
+ });
30
+
31
+ test('returns null when patientUuid does not match store patientUuid', () => {
32
+ mockUsePatientChartStore.mockReturnValue({
33
+ patientUuid: 'different-patient-id',
34
+ patient: mockPatient,
35
+ visitContext: null,
36
+ mutateVisitContext: null,
37
+ setPatient: jest.fn(),
38
+ setVisitContext: jest.fn(),
39
+ });
40
+ render(<CurrentVisitSummary patientUuid={mockPatient.id} />);
41
+ expect(screen.queryByText(/current visit/i)).not.toBeInTheDocument();
25
42
  });
26
43
 
27
- test('renders a visit summary when for the active visit', async () => {
44
+ test('renders a visit summary when visit context exists', async () => {
28
45
  mockGetConfig.mockResolvedValue({ htmlFormEntryForms: [] });
29
- mockUseVisits.mockReturnValueOnce({
30
- activeVisit: null,
31
- currentVisit: {
46
+ mockUsePatientChartStore.mockReturnValue({
47
+ patientUuid: mockPatient.id,
48
+ patient: mockPatient,
49
+ visitContext: {
32
50
  uuid: 'some-uuid',
33
51
  display: 'Visit 1',
34
52
  startDatetime: '2021-03-23T10:00:00.000+0300',
@@ -42,15 +60,17 @@ describe('CurrentVisitSummary', () => {
42
60
  display: 'Visit Type 1',
43
61
  },
44
62
  encounters: [],
63
+ patient: {
64
+ uuid: mockPatient.id,
65
+ display: 'Test Patient',
66
+ },
45
67
  },
46
- currentVisitIsRetrospective: false,
47
- error: null,
48
- isLoading: false,
49
- isValidating: false,
50
- mutate: jest.fn(),
68
+ mutateVisitContext: null,
69
+ setPatient: jest.fn(),
70
+ setVisitContext: jest.fn(),
51
71
  });
52
72
 
53
- render(<CurrentVisitSummary patientUuid="some-uuid" />);
73
+ render(<CurrentVisitSummary patientUuid={mockPatient.id} />);
54
74
 
55
75
  await waitForLoadingToFinish();
56
76
 
@@ -27,7 +27,7 @@ import {
27
27
  import {
28
28
  EditIcon,
29
29
  isDesktop,
30
- launchWorkspace,
30
+ launchWorkspace2,
31
31
  showModal,
32
32
  showSnackbar,
33
33
  TrashCanIcon,
@@ -35,16 +35,11 @@ import {
35
35
  useLayoutType,
36
36
  userHasAccess,
37
37
  useSession,
38
- useVisit,
39
38
  type EncounterType,
40
39
  ExtensionSlot,
41
40
  useFeatureFlag,
42
41
  } from '@openmrs/esm-framework';
43
- import {
44
- type HtmlFormEntryForm,
45
- launchFormEntryOrHtmlForms,
46
- invalidateVisitAndEncounterData,
47
- } from '@openmrs/esm-patient-common-lib';
42
+ import { invalidateVisitAndEncounterData, usePatientChartStore } from '@openmrs/esm-patient-common-lib';
48
43
  import { jsonSchemaResourceName } from '../../../../constants';
49
44
  import {
50
45
  deleteEncounter,
@@ -80,17 +75,12 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
80
75
  const pageSizes = [10, 20, 30, 40, 50];
81
76
  const desktopLayout = isDesktop(useLayoutType());
82
77
  const session = useSession();
83
- const { mutate: mutateCurrentVisit } = useVisit(patientUuid);
78
+ const { mutateVisitContext } = usePatientChartStore(patientUuid);
84
79
  const { mutate } = useSWRConfig();
85
80
  const responsiveSize = desktopLayout ? 'sm' : 'lg';
86
81
  const { data: encounterTypes, isLoading: isLoadingEncounterTypes } = useEncounterTypes();
87
82
  const enableEmbeddedFormView = useFeatureFlag('enable-embedded-form-view');
88
-
89
83
  const { encounterEditableDuration, encounterEditableDurationOverridePrivileges } = useConfig<ChartConfig>();
90
- const formsConfig: { htmlFormEntryForms: HtmlFormEntryForm[] } = useConfig({
91
- externalModuleName: '@openmrs/esm-patient-forms-app',
92
- });
93
- const { htmlFormEntryForms } = formsConfig;
94
84
  const paginatedMappedEncounters = useMemo(
95
85
  () => (paginatedEncounters ?? []).map(mapEncounter).filter(Boolean),
96
86
  [paginatedEncounters],
@@ -133,7 +123,7 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
133
123
  deleteEncounter(encounterUuid, abortController)
134
124
  .then(() => {
135
125
  // Update current visit data for critical components
136
- mutateCurrentVisit();
126
+ mutateVisitContext?.();
137
127
 
138
128
  // Also invalidate visit history and encounter tables since the encounter was deleted
139
129
  invalidateVisitAndEncounterData(mutate, patientUuid);
@@ -160,7 +150,7 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
160
150
  },
161
151
  });
162
152
  },
163
- [mutate, mutateCurrentVisit, patientUuid, t],
153
+ [mutate, mutateVisitContext, patientUuid, t],
164
154
  );
165
155
 
166
156
  if (isLoadingEncounterTypes || isLoading) {
@@ -269,22 +259,16 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
269
259
  itemText={t('editThisEncounter', 'Edit this encounter')}
270
260
  onClick={() => {
271
261
  if (isVisitNoteEncounter(encounter)) {
272
- launchWorkspace('visit-notes-form-workspace', {
262
+ launchWorkspace2('visit-notes-form-workspace', {
273
263
  encounter,
274
264
  formContext: 'editing',
275
265
  patientUuid,
276
266
  });
277
267
  } else {
278
- launchFormEntryOrHtmlForms(
279
- htmlFormEntryForms,
280
- patientUuid,
281
- encounter.form,
282
- encounter.visitUuid,
283
- encounter.id,
284
- encounter.visitTypeUuid,
285
- encounter.visitStartDatetime,
286
- encounter.visitStopDatetime,
287
- );
268
+ launchWorkspace2('patient-form-entry-workspace', {
269
+ form: encounter.form,
270
+ encounterUuid: encounter.id,
271
+ });
288
272
  }
289
273
  }}
290
274
  />
@@ -326,22 +310,16 @@ const EncountersTable: React.FC<EncountersTableProps> = ({
326
310
  kind="ghost"
327
311
  onClick={() => {
328
312
  if (isVisitNoteEncounter(encounter)) {
329
- launchWorkspace('visit-notes-form-workspace', {
313
+ launchWorkspace2('visit-notes-form-workspace', {
330
314
  encounter,
331
315
  formContext: 'editing',
332
316
  patientUuid,
333
317
  });
334
318
  } else {
335
- launchFormEntryOrHtmlForms(
336
- htmlFormEntryForms,
337
- patientUuid,
338
- encounter.form,
339
- encounter.visitUuid,
340
- encounter.id,
341
- encounter.visitTypeUuid,
342
- encounter.visitStartDatetime,
343
- encounter.visitStopDatetime,
344
- );
319
+ launchWorkspace2('patient-form-entry-workspace', {
320
+ form: encounter.form,
321
+ encounterUuid: encounter.id,
322
+ });
345
323
  }
346
324
  }}
347
325
  renderIcon={(props: ComponentProps<typeof EditIcon>) => (
@@ -8,7 +8,6 @@ import {
8
8
  type Encounter,
9
9
  type EncounterType,
10
10
  type Obs,
11
- type OpenmrsResource,
12
11
  useOpenmrsFetchAll,
13
12
  useOpenmrsPagination,
14
13
  } from '@openmrs/esm-framework';
@@ -1,10 +1,9 @@
1
1
  import { SelectItem, TimePickerSelect, TimePicker, Checkbox } from '@carbon/react';
2
- import { OpenmrsDatePicker, ResponsiveWrapper, useFeatureFlag, useVisit } from '@openmrs/esm-framework';
2
+ import { OpenmrsDatePicker, ResponsiveWrapper, useFeatureFlag, type Visit } from '@openmrs/esm-framework';
3
3
  import React, { useEffect, useState } from 'react';
4
4
  import { type Control, Controller, useForm } from 'react-hook-form';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import styles from './restrospective-date-time-picker.scss';
7
- import { useSystemVisitSetting } from '@openmrs/esm-patient-common-lib';
8
7
 
9
8
  type FormValues = {
10
9
  retrospectiveDate: Date;
@@ -14,22 +13,22 @@ type FormValues = {
14
13
 
15
14
  type RetrospectiveDateTimePickerProps = {
16
15
  patientUuid: string;
16
+ visitContext: Visit;
17
17
  control?: Control<FormValues>;
18
18
  onChange?: (data: FormValues) => void;
19
19
  };
20
20
 
21
21
  const RetrospectiveDateTimePicker = ({
22
- patientUuid,
22
+ visitContext,
23
23
  control: propControl,
24
24
  onChange,
25
25
  }: RetrospectiveDateTimePickerProps) => {
26
26
  const { t } = useTranslation();
27
27
  const isRdeEnabled = useFeatureFlag('rde');
28
28
 
29
- const { currentVisit } = useVisit(patientUuid);
30
- const isActiveVisit = !Boolean(currentVisit && currentVisit.stopDatetime);
31
- const maxDate = currentVisit?.stopDatetime;
32
- const minDate = currentVisit?.startDatetime;
29
+ const isActiveVisit = !Boolean(visitContext && visitContext.stopDatetime);
30
+ const maxDate = visitContext?.stopDatetime;
31
+ const minDate = visitContext?.startDatetime;
33
32
 
34
33
  const [manuallyEnableDateTimePicker, setManuallyEnableDateTimePicker] = useState<boolean>(false);
35
34