@openmrs/esm-active-visits-app 3.0.0-pre.93 → 3.0.1

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 (39) hide show
  1. package/dist/160.openmrs-esm-active-visits-app.js +2 -0
  2. package/dist/160.openmrs-esm-active-visits-app.js.map +1 -0
  3. package/dist/165.openmrs-esm-active-visits-app.js +2 -0
  4. package/dist/165.openmrs-esm-active-visits-app.js.map +1 -0
  5. package/dist/319.openmrs-esm-active-visits-app.js +1 -0
  6. package/dist/371.openmrs-esm-active-visits-app.js +3 -0
  7. package/dist/371.openmrs-esm-active-visits-app.js.LICENSE.txt +29 -0
  8. package/dist/371.openmrs-esm-active-visits-app.js.map +1 -0
  9. package/dist/415.openmrs-esm-active-visits-app.js +2 -0
  10. package/dist/415.openmrs-esm-active-visits-app.js.map +1 -0
  11. package/dist/574.openmrs-esm-active-visits-app.js +1 -0
  12. package/dist/833.openmrs-esm-active-visits-app.js +2 -0
  13. package/dist/833.openmrs-esm-active-visits-app.js.map +1 -0
  14. package/dist/openmrs-esm-active-visits-app.js +1 -1
  15. package/dist/openmrs-esm-active-visits-app.js.buildmanifest.json +100 -25
  16. package/dist/openmrs-esm-active-visits-app.js.map +1 -1
  17. package/package.json +2 -2
  18. package/src/active-visits-widget/active-visits.component.tsx +188 -151
  19. package/src/active-visits-widget/active-visits.resource.tsx +37 -17
  20. package/src/active-visits-widget/active-visits.scss +34 -0
  21. package/src/index.ts +5 -0
  22. package/src/root.scss +16 -0
  23. package/src/visits-summary/visit-detail-overview.scss +195 -0
  24. package/src/visits-summary/visit-detail.component.tsx +75 -0
  25. package/src/visits-summary/visit.resource.ts +179 -0
  26. package/src/visits-summary/visits-components/encounter-list.component.tsx +125 -0
  27. package/src/visits-summary/visits-components/encounter-observations.component.tsx +42 -0
  28. package/src/visits-summary/visits-components/medications-summary.component.tsx +59 -0
  29. package/src/visits-summary/visits-components/notes-summary.component.tsx +31 -0
  30. package/src/visits-summary/visits-components/tests-summary.component.tsx +21 -0
  31. package/src/visits-summary/visits-components/visit-summary.component.tsx +124 -0
  32. package/translations/en.json +27 -1
  33. package/dist/237.openmrs-esm-active-visits-app.js +0 -2
  34. package/dist/237.openmrs-esm-active-visits-app.js.map +0 -1
  35. package/dist/50.openmrs-esm-active-visits-app.js +0 -1
  36. package/dist/678.openmrs-esm-active-visits-app.js +0 -3
  37. package/dist/678.openmrs-esm-active-visits-app.js.LICENSE.txt +0 -44
  38. package/dist/678.openmrs-esm-active-visits-app.js.map +0 -1
  39. package/dist/912.openmrs-esm-active-visits-app.js +0 -1
@@ -0,0 +1,195 @@
1
+ @import "~@openmrs/esm-styleguide/src/vars";
2
+ @import "~carbon-components/src/globals/scss/vars";
3
+ @import "~carbon-components/src/globals/scss/mixins";
4
+ @import "../root.scss";
5
+
6
+ .container {
7
+ display: flex;
8
+ flex-direction: column;
9
+ align-items: center;
10
+ justify-content: flex-start;
11
+ }
12
+
13
+ .encounterHeading {
14
+ text-align: left;
15
+ width: 100%;
16
+ margin: 0 1rem 1.3125rem 1rem;
17
+ color: #161616;
18
+ }
19
+
20
+ .visitsDetailWidgetContainer {
21
+ background-color: $ui-background;
22
+ width: 100%;
23
+ border: 1px solid #e0e0e0;
24
+ }
25
+
26
+ .visitsDetailHeaderContainer {
27
+ display: flex;
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ padding: $spacing-04 0 $spacing-04 $spacing-05;
31
+ background-color: $ui-background;
32
+ }
33
+
34
+ .visitsDetailHeaderContainer > h4:after {
35
+ content: "";
36
+ display: block;
37
+ width: 2rem;
38
+ padding-top: 0.188rem;
39
+ border-bottom: 0.375rem solid $brand-teal-01;
40
+ }
41
+
42
+ .toggleButtons {
43
+ margin: 0 1rem;
44
+ height: 2.5rem;
45
+ }
46
+
47
+ .toggle{
48
+ border: 1px solid #A6C8FF;
49
+ }
50
+
51
+ .toggle:hover {
52
+ background-color: #EDF5FF;
53
+ }
54
+
55
+ .toggle:active, .toggle:focus {
56
+ box-shadow: none;
57
+ background-color: #EDF5FF;
58
+ }
59
+
60
+ .toggleActive{
61
+ background-color: #EDF5FF;
62
+ border: 1px solid $link-01;
63
+ }
64
+
65
+ .toggleActive:active, .toggleActive:focus {
66
+ box-shadow: none;
67
+ border: 1px solid $link-01;
68
+ background-color: #EDF5FF;
69
+ }
70
+
71
+ .toggle:first-of-type {
72
+ border-radius: $spacing-02 0 0 $spacing-02;
73
+ }
74
+
75
+ .toggle:last-of-type {
76
+ border-radius: 0 $spacing-02 $spacing-02 0;
77
+ }
78
+
79
+ .customTable tr[data-parent-row]:nth-child(odd) td {
80
+ background-color: #fff;
81
+ }
82
+
83
+ .customTable tbody tr[data-parent-row]:nth-child(even) td {
84
+ background-color: #f4f4f4;
85
+ }
86
+
87
+ .encounterEmptyState {
88
+ text-align: center;
89
+ margin: 0 1rem 1rem 1rem;
90
+ }
91
+
92
+ .expandedRow > td {
93
+ padding: inherit !important
94
+ }
95
+
96
+ .expandedRow > td > div {
97
+ max-height: max-content !important;
98
+ }
99
+
100
+ .observation {
101
+ display: grid;
102
+ grid-template-columns: max-content auto;
103
+ grid-gap: 0.5rem;
104
+ margin: 0.5rem 0;
105
+ }
106
+
107
+ .observation > span {
108
+ align-self: center;
109
+ }
110
+
111
+ .summaryContainer {
112
+ margin-left: 1rem;
113
+ display: grid;
114
+ grid-template-columns: max-content auto;
115
+ }
116
+
117
+ .flexSections {
118
+ display: flex;
119
+ }
120
+
121
+ .verticalTabs {
122
+ margin: 1rem 0;
123
+ }
124
+
125
+ .verticalTabs > ul {
126
+ flex-direction: column !important;
127
+ }
128
+
129
+ .tab {
130
+ margin-left: 0 !important;
131
+ }
132
+
133
+ .tab > button {
134
+ border-bottom: 0 !important;
135
+ border-left: 3px solid #e0e0e0 !important;
136
+ }
137
+
138
+ .tab > button, .tab > button:focus, .tab > button:active {
139
+ outline: 0 !important;
140
+ outline-offset: 0 !important;
141
+ }
142
+
143
+ .selectedTab > button {
144
+ border-left: 3px solid #007d79 !important;
145
+ border-bottom: 0 !important;
146
+ font-weight: 600 !important;
147
+ }
148
+
149
+ .tabContent {
150
+ border-top: 1px solid #e0e0e0;
151
+ padding: 1rem 0;
152
+ width: 70%;
153
+ }
154
+
155
+ .medicationBlock {
156
+ background-color: #f4f4f4;
157
+ padding: 0.625rem 6.75rem 0.75rem 1.063rem;
158
+ margin-top: 1.5rem;
159
+ width: 100% !important;
160
+ }
161
+
162
+ .medicationBlock:first-child {
163
+ margin-top: 0;
164
+ }
165
+
166
+ .diagnosesList {
167
+ display: flex;
168
+ padding-bottom: 0.5rem;
169
+ margin: 0 1rem;
170
+ border-bottom: 1px solid #e0e0e0;
171
+ }
172
+
173
+ .diagnosis {
174
+ border-radius: 12px;
175
+ height: 1.5rem;
176
+ display: flex;
177
+ align-items: center;
178
+ padding: 0 0.5rem;
179
+ margin: 0 0.5rem 0.5rem 0;
180
+ }
181
+
182
+ .primaryDiagnose {
183
+ background-color: #d0e2ff;
184
+ color: #0043ce;
185
+ }
186
+
187
+ .secondaryDiagnose {
188
+ background-color: #ffd7d9;
189
+ color:#a2191f;
190
+ }
191
+
192
+ .testSummaryExtension p {
193
+ @include carbon--type-style("body-long-01");
194
+ color: #525252
195
+ }
@@ -0,0 +1,75 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import { Button, DataTableSkeleton } from 'carbon-components-react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Encounter, useVisit } from './visit.resource';
5
+ import dayjs from 'dayjs';
6
+ import styles from './visit-detail-overview.scss';
7
+ import EncounterList from './visits-components/encounter-list.component';
8
+ import VisitSummary from './visits-components/visit-summary.component';
9
+
10
+ interface VisitDetailComponentProps {
11
+ visitUuid: string;
12
+ patientUuid: string;
13
+ }
14
+
15
+ const VisitDetailComponent: React.FC<VisitDetailComponentProps> = ({ visitUuid, patientUuid }) => {
16
+ const { t } = useTranslation();
17
+ const [listView, setView] = useState(true);
18
+
19
+ const { data: visit, isError, isLoading, isValidating } = useVisit(visitUuid);
20
+
21
+ const encounters = useMemo(
22
+ () =>
23
+ visit
24
+ ? visit?.encounters?.map((encounter: Encounter) => ({
25
+ id: encounter.uuid,
26
+ time: dayjs(encounter.encounterDateTime).format('hh:mm'),
27
+ encounterType: encounter.encounterType.display,
28
+ provider: encounter.encounterProviders.length > 0 ? encounter.encounterProviders[0].display : '',
29
+ obs: encounter.obs,
30
+ }))
31
+ : [],
32
+ [visit],
33
+ );
34
+
35
+ if (isLoading) {
36
+ return <DataTableSkeleton role="progressbar" />;
37
+ }
38
+ if (visit) {
39
+ return (
40
+ <div className={styles.visitsDetailWidgetContainer}>
41
+ <div className={styles.visitsDetailHeaderContainer}>
42
+ <h4 className={styles.productiveHeading02}>
43
+ {visit?.visitType?.display}
44
+ <br />
45
+ <p className={`${styles.bodyLong01} ${styles.text02}`}>{formatDateTime(visit?.startDatetime)}</p>
46
+ </h4>
47
+ <div className={styles.toggleButtons}>
48
+ <Button
49
+ className={`${styles.toggle} ${listView ? styles.toggleActive : ''}`}
50
+ size="small"
51
+ kind="ghost"
52
+ onClick={() => setView(true)}>
53
+ {t('allEncounters', 'All Encounters')}
54
+ </Button>
55
+ <Button
56
+ className={`${styles.toggle} ${!listView ? styles.toggleActive : ''}`}
57
+ size="small"
58
+ kind="ghost"
59
+ onClick={() => setView(false)}>
60
+ {t('visitSummary', 'Visit Summary')}
61
+ </Button>
62
+ </div>
63
+ </div>
64
+ {listView && visit?.encounters && <EncounterList visitUuid={visit.uuid} encounters={encounters} />}
65
+ {!listView && <VisitSummary encounters={visit.encounters} patientUuid={patientUuid} />}
66
+ </div>
67
+ );
68
+ }
69
+ };
70
+
71
+ function formatDateTime(date) {
72
+ return date ? dayjs(date).format('MMM DD, YYYY - hh:mm') : null;
73
+ }
74
+
75
+ export default VisitDetailComponent;
@@ -0,0 +1,179 @@
1
+ import { openmrsFetch, Visit } from '@openmrs/esm-framework';
2
+ import useSWR from 'swr';
3
+
4
+ export interface Encounter {
5
+ uuid: string;
6
+ encounterDateTime: string;
7
+ encounterProviders: Array<{
8
+ uuid: string;
9
+ display: string;
10
+ encounterRole: {
11
+ uuid: string;
12
+ display: string;
13
+ };
14
+ provider: {
15
+ uuid: string;
16
+ person: {
17
+ uuid: string;
18
+ display: string;
19
+ };
20
+ };
21
+ }>;
22
+ encounterType: {
23
+ uuid: string;
24
+ display: string;
25
+ };
26
+ obs: Array<Observation>;
27
+ orders: Array<Order>;
28
+ }
29
+
30
+ export interface EncounterProvider {
31
+ uuid: string;
32
+ display: string;
33
+ encounterRole: {
34
+ uuid: string;
35
+ display: string;
36
+ };
37
+ provider: {
38
+ uuid: string;
39
+ person: {
40
+ uuid: string;
41
+ display: string;
42
+ };
43
+ };
44
+ }
45
+
46
+ export interface Observation {
47
+ uuid: string;
48
+ concept: {
49
+ uuid: string;
50
+ display: string;
51
+ conceptClass: {
52
+ uuid: string;
53
+ display: string;
54
+ };
55
+ };
56
+ display: string;
57
+ groupMembers: null | Array<{
58
+ uuid: string;
59
+ concept: {
60
+ uuid: string;
61
+ display: string;
62
+ };
63
+ value: {
64
+ uuid: string;
65
+ display: string;
66
+ };
67
+ }>;
68
+ value: any;
69
+ obsDatetime: string;
70
+ }
71
+
72
+ export interface Order {
73
+ uuid: string;
74
+ dateActivated: string;
75
+ dose: number;
76
+ doseUnits: {
77
+ uuid: string;
78
+ display: string;
79
+ };
80
+ drug: {
81
+ uuid: string;
82
+ name: string;
83
+ strength: string;
84
+ };
85
+ duration: number;
86
+ durationUnits: {
87
+ uuid: string;
88
+ display: string;
89
+ };
90
+ frequency: {
91
+ uuid: string;
92
+ display: string;
93
+ };
94
+ numRefills: number;
95
+ orderer: {
96
+ uuid: string;
97
+ person: {
98
+ uuid: string;
99
+ display: string;
100
+ };
101
+ };
102
+ orderType: {
103
+ uuid: string;
104
+ display: string;
105
+ };
106
+ route: {
107
+ uuid: string;
108
+ display: string;
109
+ };
110
+ }
111
+
112
+ export interface Note {
113
+ note: string;
114
+ provider: {
115
+ name: string;
116
+ role: string;
117
+ };
118
+ time: string;
119
+ }
120
+
121
+ export interface OrderItem {
122
+ order: Order;
123
+ provider: {
124
+ name: string;
125
+ role: string;
126
+ };
127
+ }
128
+
129
+ export function useVisit(visitUuid: string) {
130
+ const customRepresentation =
131
+ 'custom:(uuid,encounters:(uuid,encounterDatetime,' +
132
+ 'orders:(uuid,dateActivated,' +
133
+ 'drug:(uuid,name,strength),doseUnits:(uuid,display),' +
134
+ 'dose,route:(uuid,display),frequency:(uuid,display),' +
135
+ 'duration,durationUnits:(uuid,display),numRefills,' +
136
+ 'orderType:(uuid,display),orderer:(uuid,person:(uuid,display))),' +
137
+ 'obs:(uuid,concept:(uuid,display,conceptClass:(uuid,display)),' +
138
+ 'display,groupMembers:(uuid,concept:(uuid,display),' +
139
+ 'value:(uuid,display)),value),encounterType:(uuid,display),' +
140
+ 'encounterProviders:(uuid,display,encounterRole:(uuid,display),' +
141
+ 'provider:(uuid,person:(uuid,display)))),visitType:(uuid,name,display),startDatetime';
142
+
143
+ const { data, error, isValidating } = useSWR<{ data: Visit }, Error>(
144
+ `/ws/rest/v1/visit/${visitUuid}?v=${customRepresentation}`,
145
+ openmrsFetch,
146
+ );
147
+
148
+ return {
149
+ data: data ? data.data : null,
150
+ isError: error,
151
+ isLoading: !data && !error,
152
+ isValidating,
153
+ };
154
+ }
155
+
156
+ export function getDosage(strength: string, doseNumber: number) {
157
+ if (!strength || !doseNumber) {
158
+ return '';
159
+ }
160
+
161
+ const i = strength.search(/\D/);
162
+ const strengthQuantity = parseInt(strength.substring(0, i));
163
+
164
+ const concentrationStartIndex = strength.search(/\//);
165
+
166
+ let strengthUnits = strength.substring(i);
167
+
168
+ if (concentrationStartIndex >= 0) {
169
+ strengthUnits = strength.substring(i, concentrationStartIndex);
170
+ const j = strength.substring(concentrationStartIndex + 1).search(/\D/);
171
+ const concentrationQuantity = parseInt(strength.substr(concentrationStartIndex + 1, j));
172
+ const concentrationUnits = strength.substring(concentrationStartIndex + 1 + j);
173
+ return `${doseNumber} ${strengthUnits} (${
174
+ (doseNumber / strengthQuantity) * concentrationQuantity
175
+ } ${concentrationUnits})`;
176
+ } else {
177
+ return `${strengthQuantity * doseNumber} ${strengthUnits}`;
178
+ }
179
+ }
@@ -0,0 +1,125 @@
1
+ import React, { useEffect, useState, useMemo, useRef } from 'react';
2
+ import {
3
+ DataTable,
4
+ TableContainer,
5
+ Table,
6
+ TableHead,
7
+ TableExpandHeader,
8
+ TableRow,
9
+ TableHeader,
10
+ TableBody,
11
+ TableExpandRow,
12
+ TableCell,
13
+ TableExpandedRow,
14
+ } from 'carbon-components-react';
15
+ import EncounterObservations from './encounter-observations.component';
16
+ import styles from '../visit-detail-overview.scss';
17
+ import { Observation } from '../visit.resource';
18
+ import { useTranslation } from 'react-i18next';
19
+ import { useLayoutType } from '@openmrs/esm-framework';
20
+
21
+ interface EncounterListProps {
22
+ encounters: Array<{
23
+ id: any;
24
+ time: any;
25
+ encounterType: string;
26
+ provider: string;
27
+ obs: Array<Observation>;
28
+ }>;
29
+ visitUuid: string;
30
+ }
31
+
32
+ const EncounterListDataTable: React.FC<EncounterListProps> = ({ encounters, visitUuid }) => {
33
+ const { t } = useTranslation();
34
+ const layout = useLayoutType();
35
+ const isDesktop = layout === 'desktop';
36
+ const [headerWidth, setHeaderWidth] = useState(0);
37
+ const headerRef = useRef(null);
38
+
39
+ const headerData = useMemo(
40
+ () => [
41
+ {
42
+ id: 1,
43
+ header: t('time', 'Time'),
44
+ key: 'time',
45
+ },
46
+ {
47
+ id: 2,
48
+ header: t('encounterType', 'Encounter Type'),
49
+ key: 'encounterType',
50
+ },
51
+ {
52
+ id: 3,
53
+ header: t('provider', 'Provider'),
54
+ key: 'provider',
55
+ },
56
+ ],
57
+ [t],
58
+ );
59
+
60
+ useEffect(() => {
61
+ setHeaderWidth(headerRef?.current?.clientWidth);
62
+ const handler = () => setHeaderWidth(headerRef?.current?.clientWidth);
63
+ window.addEventListener('resize', handler);
64
+ return () => window.removeEventListener('resize', handler);
65
+ }, []);
66
+
67
+ return encounters.length !== 0 ? (
68
+ <DataTable rows={encounters} headers={headerData}>
69
+ {({ rows, headers, getHeaderProps, getRowProps, getTableProps }) => {
70
+ return (
71
+ <TableContainer>
72
+ <Table className={styles.customTable} {...getTableProps()} size={!isDesktop ? 'normal' : 'short'}>
73
+ <TableHead>
74
+ <TableRow>
75
+ <TableExpandHeader />
76
+ {headers.map((header, ind) =>
77
+ ind === 0 ? (
78
+ <TableHeader id={`header_${visitUuid}_${ind}`} ref={headerRef} {...getHeaderProps({ header })}>
79
+ {header.header}
80
+ </TableHeader>
81
+ ) : (
82
+ <TableHeader id={`header_${visitUuid}_${ind}`} {...getHeaderProps({ header })}>
83
+ {header.header}
84
+ </TableHeader>
85
+ ),
86
+ )}
87
+ </TableRow>
88
+ </TableHead>
89
+ <TableBody>
90
+ {rows.map((row, ind) => (
91
+ <React.Fragment key={row.id}>
92
+ <TableExpandRow {...getRowProps({ row })}>
93
+ {row.cells.map((cell) => (
94
+ <TableCell key={cell.id}>{cell.value}</TableCell>
95
+ ))}
96
+ </TableExpandRow>
97
+ {row.isExpanded && (
98
+ <TableExpandedRow
99
+ className={styles.expandedRow}
100
+ style={{ paddingLeft: isDesktop ? '3rem' : '4rem' }}
101
+ colSpan={headers.length + 2}>
102
+ <div style={{ marginLeft: headerWidth }}>
103
+ <EncounterObservations observations={encounters[ind].obs} />
104
+ </div>
105
+ </TableExpandedRow>
106
+ )}
107
+ </React.Fragment>
108
+ ))}
109
+ </TableBody>
110
+ </Table>
111
+ </TableContainer>
112
+ );
113
+ }}
114
+ </DataTable>
115
+ ) : (
116
+ <div className={styles.encounterEmptyState}>
117
+ <h4 className={styles.productiveHeading02}>{t('noEncountersFound', 'No encounters found')}</h4>
118
+ <p className={`${styles.bodyLong01} ${styles.text02}`}>
119
+ {t('thereIsNoInformationToDisplayHere', 'There is no information to display here')}
120
+ </p>
121
+ </div>
122
+ );
123
+ };
124
+
125
+ export default EncounterListDataTable;
@@ -0,0 +1,42 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { SkeletonText } from 'carbon-components-react';
4
+ import { Observation } from '../visit.resource';
5
+ import styles from '../visit-detail-overview.scss';
6
+
7
+ interface EncounterObservationsProps {
8
+ observations: Array<Observation>;
9
+ }
10
+
11
+ const EncounterObservations: React.FC<EncounterObservationsProps> = ({ observations }) => {
12
+ const { t } = useTranslation();
13
+
14
+ const observationsList = useMemo(() => {
15
+ return (
16
+ observations &&
17
+ observations.map((obs: Observation) => {
18
+ const [question, answer] = obs.display.split(':');
19
+ return { question, answer };
20
+ })
21
+ );
22
+ }, [observations]);
23
+
24
+ return observationsList ? (
25
+ observationsList.length > 0 ? (
26
+ <div className={styles.observation}>
27
+ {observationsList.map((obs, ind) => (
28
+ <React.Fragment key={ind}>
29
+ <span className={styles.caption01}>{obs.question}: </span>
30
+ <span className={`${styles.bodyShort02} ${styles.text01}`}>{obs.answer}</span>
31
+ </React.Fragment>
32
+ ))}
33
+ </div>
34
+ ) : (
35
+ <p className={styles.caption01}>{t('noObservationsFound', 'No observations found')}</p>
36
+ )
37
+ ) : (
38
+ <SkeletonText />
39
+ );
40
+ };
41
+
42
+ export default EncounterObservations;
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import capitalize from 'lodash-es/capitalize';
3
+ import dayjs from 'dayjs';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { OrderItem, getDosage } from '../visit.resource';
6
+ import styles from '../visit-detail-overview.scss';
7
+
8
+ function formatTime(dateTime) {
9
+ return dayjs(dateTime).format('hh:mm');
10
+ }
11
+
12
+ interface MedicationSummaryProps {
13
+ medications: Array<OrderItem>;
14
+ }
15
+
16
+ const MedicationSummary: React.FC<MedicationSummaryProps> = ({ medications }) => {
17
+ const { t } = useTranslation();
18
+
19
+ return (
20
+ <React.Fragment>
21
+ {medications.length > 0 ? (
22
+ medications.map(
23
+ (medication: OrderItem, ind) =>
24
+ medication.order?.dose &&
25
+ medication.order?.orderType?.display === 'Drug Order' && (
26
+ <React.Fragment key={ind}>
27
+ <p className={`${styles.bodyLong01} ${styles.medicationBlock}`}>
28
+ <strong>{capitalize(medication.order.drug?.name)}</strong> &mdash;{' '}
29
+ {medication.order.doseUnits?.display} &mdash; {medication.order.route?.display}
30
+ <br />
31
+ <span className={styles.label01}>{t('dose', 'Dose').toUpperCase()}</span>{' '}
32
+ <strong>{getDosage(medication.order.drug?.strength, medication.order.dose).toLowerCase()}</strong>{' '}
33
+ &mdash; {medication.order.frequency?.display} &mdash;{' '}
34
+ {!medication.order.duration
35
+ ? t('orderIndefiniteDuration', 'Indefinite duration')
36
+ : t('orderDurationAndUnit', 'for {duration} {durationUnit}', {
37
+ duration: medication.order.duration,
38
+ durationUnit: medication.order.durationUnits?.display,
39
+ })}
40
+ <br />
41
+ <span className={styles.label01}>{t('refills', 'Refills').toUpperCase()}</span>{' '}
42
+ {medication.order.numRefills}
43
+ </p>
44
+ <p className={styles.caption01} style={{ color: '#525252' }}>
45
+ {formatTime(medication.order.dateActivated)} &middot;{' '}
46
+ {medication.provider && medication.provider.name} &middot;{' '}
47
+ {medication.provider && medication.provider.role}
48
+ </p>
49
+ </React.Fragment>
50
+ ),
51
+ )
52
+ ) : (
53
+ <p className={`${styles.bodyLong01} ${styles.text02}`}>{t('noMedicationsFound', 'No medications found')}</p>
54
+ )}
55
+ </React.Fragment>
56
+ );
57
+ };
58
+
59
+ export default MedicationSummary;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import styles from '../visit-detail-overview.scss';
3
+ import { Note } from '../visit.resource';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ interface NotesSummaryProps {
7
+ notes: Array<Note>;
8
+ }
9
+
10
+ const NotesSummary: React.FC<NotesSummaryProps> = ({ notes }) => {
11
+ const { t } = useTranslation();
12
+
13
+ return (
14
+ <React.Fragment>
15
+ {notes.length > 0 ? (
16
+ notes.map((note, ind) => (
17
+ <React.Fragment key={ind}>
18
+ <p className={`${styles.medicationBlock} ${styles.bodyLong01}`}>{note.note}</p>
19
+ <p className={styles.caption01} style={{ color: '#525252' }}>
20
+ {note.time} &middot; {note.provider.name} &middot; {note.provider.role}
21
+ </p>
22
+ </React.Fragment>
23
+ ))
24
+ ) : (
25
+ <p className={`${styles.bodyLong01} ${styles.text02}`}>{t('noNotesFound', 'No notes found')}</p>
26
+ )}
27
+ </React.Fragment>
28
+ );
29
+ };
30
+
31
+ export default NotesSummary;