@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2202 → 5.4.2-pre.2223

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 (129) hide show
  1. package/.turbo/turbo-build.log +26 -489
  2. package/dist/144.js +2 -0
  3. package/dist/144.js.map +1 -0
  4. package/dist/300.js +1 -0
  5. package/dist/317.js +1 -0
  6. package/dist/317.js.map +1 -0
  7. package/dist/{807.js → 335.js} +1 -1
  8. package/dist/349.js +2 -0
  9. package/dist/349.js.map +1 -0
  10. package/dist/359.js +2 -0
  11. package/dist/359.js.LICENSE.txt +29 -0
  12. package/dist/359.js.map +1 -0
  13. package/dist/41.js +2 -0
  14. package/dist/41.js.map +1 -0
  15. package/dist/474.js +2 -0
  16. package/dist/474.js.map +1 -0
  17. package/dist/479.js +2 -0
  18. package/dist/479.js.LICENSE.txt +9 -0
  19. package/dist/479.js.map +1 -0
  20. package/dist/537.js +1 -0
  21. package/dist/537.js.map +1 -0
  22. package/dist/{319.js → 55.js} +1 -1
  23. package/dist/60.js +1 -0
  24. package/dist/60.js.map +1 -0
  25. package/dist/746.js +2 -0
  26. package/dist/{164.js.LICENSE.txt → 746.js.LICENSE.txt} +0 -10
  27. package/dist/746.js.map +1 -0
  28. package/dist/907.js +1 -0
  29. package/dist/907.js.map +1 -0
  30. package/dist/913.js +2 -0
  31. package/dist/{591.js.map → 913.js.map} +1 -1
  32. package/dist/{757.js → 99.js} +1 -1
  33. package/dist/kenyaemr-esm-patient-clinical-view-app.js +1 -1
  34. package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +219 -117
  35. package/dist/kenyaemr-esm-patient-clinical-view-app.js.map +1 -1
  36. package/dist/main.js +1 -1
  37. package/dist/main.js.map +1 -1
  38. package/dist/routes.json +1 -1
  39. package/package.json +4 -1
  40. package/src/autosuggest/autosuggest.component.tsx +3 -3
  41. package/src/autosuggest/autosuggest.scss +1 -1
  42. package/src/autosuggest/patient-search-info.component.tsx +1 -1
  43. package/src/case-management/encounters/case-encounter-header.scss +1 -1
  44. package/src/case-management/encounters/case-encounter-overview.component.tsx +11 -8
  45. package/src/case-management/header/case-management-header.scss +1 -1
  46. package/src/case-management/metrics/case-management-header.scss +1 -1
  47. package/src/case-management/table/case-management-list.scss +1 -1
  48. package/src/case-management/tabs/case-management-tabs.component.tsx +2 -2
  49. package/src/case-management/tabs/case-management-tabs.scss +1 -1
  50. package/src/case-management/workspace/case-management.scss +1 -1
  51. package/src/case-management/workspace/patient-info.scss +1 -1
  52. package/src/clinical-encounter/clinical-enc.component.tsx +4 -4
  53. package/src/clinical-encounter/dashboard/in-patient.scss +1 -1
  54. package/src/clinical-encounter/summary/in-patient-medical-summary/in-patient-medical-summary.component.tsx +1 -1
  55. package/src/clinical-encounter/summary/maternal-summary/maternal-summary.component.tsx +4 -4
  56. package/src/clinical-encounter/summary/neonatal summary/neonatal-summary.component.tsx +4 -4
  57. package/src/clinical-encounter/summary/out-patient-summary/out-patient-summary.scss +1 -1
  58. package/src/clinical-encounter/summary/out-patient-summary/patient-medical-history.component.tsx +2 -2
  59. package/src/clinical-encounter/summary/out-patient-summary/patient-social-history.component.tsx +5 -5
  60. package/src/clinical-encounter/summary/surgical summary/surgical-summary.component.tsx +4 -5
  61. package/src/contact-list/contact-actions.component.tsx +4 -5
  62. package/src/contact-list/contact-list-concept-map.ts +1 -0
  63. package/src/contact-list/contact-list.component.tsx +2 -2
  64. package/src/contact-list/contact-list.scss +1 -1
  65. package/src/contact-list/contact-list.workspace.tsx +28 -13
  66. package/src/contact-list/contact-tracing-history.component.tsx +3 -3
  67. package/src/contact-list/forms/contact-list-update.workspace.tsx +22 -267
  68. package/src/deceased-panel/mortuary-summary/mortuary-summary.component.tsx +2 -10
  69. package/src/deceased-panel/mortuary-summary/mortuary-summary.scss +1 -1
  70. package/src/deceased-panel/panels/autopsy.component.tsx +11 -4
  71. package/src/declarations.d.ts +0 -13
  72. package/src/family-partner-history/family-history.component.tsx +2 -2
  73. package/src/family-partner-history/family-history.scss +1 -1
  74. package/src/family-partner-history/family-relationship.scss +1 -1
  75. package/src/family-partner-history/family-relationship.workspace.tsx +27 -12
  76. package/src/hooks/useContacts.ts +1 -0
  77. package/src/in-patient/in-patient-table/in-patient-table.component.tsx +4 -4
  78. package/src/in-patient/in-patient.component.tsx +3 -8
  79. package/src/maternal-and-child-health/maternal-health-component.scss +4 -3
  80. package/src/maternal-and-child-health/partography/labour-delivery.scss +7 -5
  81. package/src/maternal-and-child-health/partography/partograph-chart.scss +1 -1
  82. package/src/maternal-and-child-health/partography/partograph.component.tsx +5 -11
  83. package/src/peer-calendar/forms/peer-form.workspace.tsx +11 -11
  84. package/src/peer-calendar/header/reporting-period-input.component.tsx +3 -1
  85. package/src/peer-calendar/metrics/peer-calendar-metrics.component.tsx +2 -3
  86. package/src/peer-calendar/table/generic-data-table.scss +1 -1
  87. package/src/peer-calendar/table/generic-data-table.tsx +2 -1
  88. package/src/peer-calendar/table/peer-calendar-table-filter.component.tsx +6 -3
  89. package/src/relationships/forms/baseline-info-form-section.component.tsx +46 -31
  90. package/src/relationships/forms/form.scss +1 -1
  91. package/src/relationships/forms/patient-search-create-form.tsx +25 -20
  92. package/src/relationships/modals/birthdate-calculator.modal.tsx +3 -2
  93. package/src/relationships/relationship.resources.ts +18 -2
  94. package/src/relationships/tabs/relationships-tabs-component.tsx +2 -2
  95. package/src/relationships/tabs/relationships-tabs.scss +1 -1
  96. package/src/root.scss +1 -2
  97. package/src/specialized-clinics/generic-nav-links/generic-dashboard.component.tsx +3 -3
  98. package/src/specialized-clinics/hiv-care-and-treatment-services/defaulter-tracing/defaulter-tracing.component.tsx +5 -5
  99. package/src/specialized-clinics/hiv-care-and-treatment-services/defaulter-tracing/defaulter-tracing.test.tsx +2 -8
  100. package/src/specialized-clinics/hiv-care-and-treatment-services/hiv-testing-services/hiv-testing-component.scss +4 -3
  101. package/src/specialized-clinics/hiv-care-and-treatment-services/hiv-testing-services/views/hiv-testing/hiv-testing-services.component.tsx +3 -8
  102. package/src/ui/data-table/o-table.component.tsx +2 -2
  103. package/src/ui/data-table/o-table.scss +1 -1
  104. package/src/ui/encounter-list/encounter-list.scss +5 -3
  105. package/translations/en.json +1 -0
  106. package/dist/130.js +0 -2
  107. package/dist/130.js.LICENSE.txt +0 -7
  108. package/dist/130.js.map +0 -1
  109. package/dist/164.js +0 -2
  110. package/dist/164.js.map +0 -1
  111. package/dist/316.js +0 -2
  112. package/dist/316.js.map +0 -1
  113. package/dist/40.js +0 -1
  114. package/dist/40.js.map +0 -1
  115. package/dist/574.js +0 -1
  116. package/dist/589.js +0 -1
  117. package/dist/589.js.map +0 -1
  118. package/dist/591.js +0 -2
  119. package/dist/685.js +0 -2
  120. package/dist/685.js.map +0 -1
  121. package/dist/700.js +0 -2
  122. package/dist/700.js.map +0 -1
  123. package/dist/784.js +0 -2
  124. package/dist/784.js.map +0 -1
  125. /package/dist/{316.js.LICENSE.txt → 144.js.LICENSE.txt} +0 -0
  126. /package/dist/{700.js.LICENSE.txt → 349.js.LICENSE.txt} +0 -0
  127. /package/dist/{784.js.LICENSE.txt → 41.js.LICENSE.txt} +0 -0
  128. /package/dist/{685.js.LICENSE.txt → 474.js.LICENSE.txt} +0 -0
  129. /package/dist/{591.js.LICENSE.txt → 913.js.LICENSE.txt} +0 -0
@@ -1,13 +1,8 @@
1
1
  import React from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { ComboButton, MenuItem, DataTableSkeleton } from '@carbon/react';
4
- import {
5
- CardHeader,
6
- EmptyState,
7
- launchPatientWorkspace,
8
- useVisitOrOfflineVisit,
9
- } from '@openmrs/esm-patient-common-lib';
10
- import { useConfig, useVisit, evaluateAsBoolean } from '@openmrs/esm-framework';
4
+ import { CardHeader, EmptyState, useVisitOrOfflineVisit } from '@openmrs/esm-patient-common-lib';
5
+ import { useConfig, useVisit, evaluateAsBoolean, launchWorkspace } from '@openmrs/esm-framework';
11
6
  import dayjs from 'dayjs';
12
7
  import { BedManagementConfig } from '../config-schema';
13
8
  import { usePatientEncounters } from './in-patient.resource';
@@ -41,7 +36,7 @@ const InPatient: React.FC<InPatientProps> = ({ patientUuid, patient }) => {
41
36
  });
42
37
 
43
38
  const handleLaunchForm = (form: { label: string; uuid: string }) => {
44
- launchPatientWorkspace('patient-form-entry-workspace', {
39
+ launchWorkspace('patient-form-entry-workspace', {
45
40
  workspaceTitle: form.label,
46
41
  mutateForm: () => mutate(),
47
42
  formInfo: {
@@ -1,8 +1,9 @@
1
1
  @use '@carbon/styles/scss/spacing';
2
- @import '~@openmrs/esm-styleguide/src/vars';
2
+ @use '@carbon/colors';
3
+ @use '~@openmrs/esm-styleguide/src/vars' as *;
3
4
 
4
5
  .widgetContainer {
5
- background-color: $ui-background;
6
+ background-color: colors.$white;
6
7
  }
7
8
 
8
9
  .widgetHeaderContainer {
@@ -52,7 +53,7 @@
52
53
  }
53
54
 
54
55
  .widgetCard {
55
- background-color: $ui-background;
56
+ background-color: colors.$white;
56
57
  border: 1px solid $ui-03;
57
58
  position: relative;
58
59
  }
@@ -1,9 +1,11 @@
1
1
  @use '@carbon/styles/scss/spacing';
2
2
  @use '@carbon/styles/scss/type';
3
- @import '../../root.scss';
3
+ @use '@carbon/colors';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
+ @use '../../root.scss';
4
6
 
5
7
  .widgetContainer {
6
- background-color: $ui-background;
8
+ background-color: colors.$white;
7
9
  border: 1px solid #e0e0e0;
8
10
  margin-bottom: 1rem;
9
11
  }
@@ -48,7 +50,7 @@
48
50
  }
49
51
  }
50
52
  .widgetCard {
51
- background-color: $ui-background;
53
+ background-color: colors.$white;
52
54
  border: 1px solid $ui-03;
53
55
  }
54
56
 
@@ -82,11 +84,11 @@
82
84
 
83
85
  &:global(.cds--btn--tertiary:focus) {
84
86
  background-color: $interactive-01;
85
- color: $ui-background;
87
+ color: colors.$white;
86
88
  }
87
89
 
88
90
  &:global(.cds--btn--tertiary:hover) {
89
- color: $ui-background;
91
+ color: colors.$white;
90
92
  background-color: $interactive-01;
91
93
  }
92
94
 
@@ -1,6 +1,6 @@
1
1
  @use '@carbon/styles/scss/spacing';
2
2
  @use '@carbon/styles/scss/type';
3
- @import '~@openmrs/esm-styleguide/src/vars';
3
+ @use '~@openmrs/esm-styleguide/src/vars' as *;
4
4
 
5
5
  .label01 {
6
6
  @include type.type-style('label-01');
@@ -15,14 +15,8 @@ import {
15
15
  Button,
16
16
  } from '@carbon/react';
17
17
  import { Add, ChartLineSmooth } from '@carbon/react/icons';
18
- import {
19
- EmptyDataIllustration,
20
- ErrorState,
21
- CardHeader,
22
- launchPatientWorkspace,
23
- EmptyState,
24
- } from '@openmrs/esm-patient-common-lib';
25
- import { formatDate, isDesktop, parseDate, useLayoutType } from '@openmrs/esm-framework';
18
+ import { EmptyDataIllustration, ErrorState, CardHeader, EmptyState } from '@openmrs/esm-patient-common-lib';
19
+ import { formatDate, isDesktop, launchWorkspace, parseDate, useLayoutType } from '@openmrs/esm-framework';
26
20
  import styles from './labour-delivery.scss';
27
21
  import { usePartograph } from '../../hooks/usePartograph';
28
22
  import dayjs from 'dayjs';
@@ -108,7 +102,7 @@ const Partograph: React.FC<PartographyProps> = ({ patientUuid }) => {
108
102
  }) ?? [];
109
103
 
110
104
  const handleAddHistory = () => {
111
- launchPatientWorkspace('patient-form-entry-workspace', {
105
+ launchWorkspace('patient-form-entry-workspace', {
112
106
  workspaceTitle: headerTitle,
113
107
  mutateForm: () => {
114
108
  mutate();
@@ -204,9 +198,9 @@ const Partograph: React.FC<PartographyProps> = ({ patientUuid }) => {
204
198
  <TableHeader
205
199
  {...getHeaderProps({
206
200
  header,
207
- isSortable: header.isSortable,
201
+ isSortable: true,
208
202
  })}>
209
- {header.header?.content ?? header.header}
203
+ {header.header}
210
204
  </TableHeader>
211
205
  ))}
212
206
  </TableRow>
@@ -80,7 +80,7 @@ const PeerForm: React.FC<PeerFormProps> = ({ closeWorkspace }) => {
80
80
  render={({ field }) => (
81
81
  <Dropdown
82
82
  ref={field.ref}
83
- invalid={form.formState.errors[field.name]?.message}
83
+ invalid={!!form.formState.errors[field.name]?.message}
84
84
  invalidText={form.formState.errors[field.name]?.message}
85
85
  id="relationship"
86
86
  titleText={t('relationshipType', 'RelationshipbType')}
@@ -105,7 +105,7 @@ const PeerForm: React.FC<PeerFormProps> = ({ closeWorkspace }) => {
105
105
  render={({ field }) => (
106
106
  <Dropdown
107
107
  ref={field.ref}
108
- invalid={form.formState.errors[field.name]?.message}
108
+ invalid={!!form.formState.errors[field.name]?.message}
109
109
  invalidText={form.formState.errors[field.name]?.message}
110
110
  id="peerEducator"
111
111
  titleText={t('peerEducator', 'Peer Educator')}
@@ -132,16 +132,16 @@ const PeerForm: React.FC<PeerFormProps> = ({ closeWorkspace }) => {
132
132
  value={field.value}
133
133
  onChange={field.onChange}
134
134
  dateFormat="d/m/Y"
135
- id="startDate"
136
135
  datePickerType="single"
137
- invalid={form.formState.errors[field.name]?.message}
136
+ invalid={!!form.formState.errors[field.name]?.message}
138
137
  invalidText={form.formState.errors[field.name]?.message}>
139
138
  <DatePickerInput
140
- invalid={form.formState.errors[field.name]?.message}
139
+ invalid={!!form.formState.errors[field.name]?.message}
141
140
  invalidText={form.formState.errors[field.name]?.message}
142
141
  placeholder="mm/dd/yyyy"
143
142
  labelText={t('startDate', 'Start Date')}
144
- size={'xl'}
143
+ size="lg"
144
+ id="startDate"
145
145
  />
146
146
  </DatePicker>
147
147
  )}
@@ -156,16 +156,16 @@ const PeerForm: React.FC<PeerFormProps> = ({ closeWorkspace }) => {
156
156
  value={field.value}
157
157
  onChange={field.onChange}
158
158
  dateFormat="d/m/Y"
159
- id="endDate"
160
159
  datePickerType="single"
161
- invalid={form.formState.errors[field.name]?.message}
160
+ invalid={!!form.formState.errors[field.name]?.message}
162
161
  invalidText={form.formState.errors[field.name]?.message}>
163
162
  <DatePickerInput
164
- invalid={form.formState.errors[field.name]?.message}
163
+ invalid={!!form.formState.errors[field.name]?.message}
165
164
  invalidText={form.formState.errors[field.name]?.message}
166
165
  placeholder="mm/dd/yyyy"
167
166
  labelText={t('endDate', 'End Date')}
168
- size="xl"
167
+ size="lg"
168
+ id="endDate"
169
169
  />
170
170
  </DatePicker>
171
171
  )}
@@ -173,7 +173,7 @@ const PeerForm: React.FC<PeerFormProps> = ({ closeWorkspace }) => {
173
173
  </Column>
174
174
  </Stack>
175
175
  <ButtonSet className={styles.buttonSet}>
176
- <Button className={styles.button} kind="secondary" onClick={closeWorkspace}>
176
+ <Button className={styles.button} kind="secondary" onClick={() => closeWorkspace()}>
177
177
  {t('discard', 'Discard')}
178
178
  </Button>
179
179
  <Button className={styles.button} kind="primary" type="submit" disabled={form.formState.isSubmitting}>
@@ -22,6 +22,7 @@ const ReportingPeriodInput: React.FC<ReportingPeriodInputProps> = ({
22
22
  <Column className={styles.reportingPeriodInput}>Select Period:</Column>
23
23
  <Column className={styles.reportingPeriodInput}>
24
24
  <Dropdown
25
+ titleText={t('reportingMonth', 'Reporting month')}
25
26
  type="inline"
26
27
  id="reportingMonth"
27
28
  autoAlign={true}
@@ -36,6 +37,7 @@ const ReportingPeriodInput: React.FC<ReportingPeriodInputProps> = ({
36
37
  </Column>
37
38
  <Column className={styles.reportingPeriodInput}>
38
39
  <Dropdown
40
+ titleText={t('reportingYear', 'Reporting year')}
39
41
  type="inline"
40
42
  autoAlign={true}
41
43
  id="reportingYear"
@@ -45,7 +47,7 @@ const ReportingPeriodInput: React.FC<ReportingPeriodInputProps> = ({
45
47
  initialSelectedItem={reportigPeriod?.year}
46
48
  label={t('reportingYear', 'Year')}
47
49
  items={years.map((r) => r)}
48
- itemToString={(item) => years.find((r) => r === item) ?? ''}
50
+ itemToString={(item) => years.find((r) => r === item).toString() ?? ''}
49
51
  />
50
52
  </Column>
51
53
  </div>
@@ -1,7 +1,6 @@
1
1
  import { Button, Layer, Row, SkeletonPlaceholder } from '@carbon/react';
2
2
  import { Add } from '@carbon/react/icons';
3
- import { useSession } from '@openmrs/esm-framework';
4
- import { launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
3
+ import { launchWorkspace, useSession } from '@openmrs/esm-framework';
5
4
  import React from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
  import type { Peer, ReportingPeriod } from '../../types';
@@ -31,7 +30,7 @@ const PeerCalendarMetricsHeader: React.FC<PeerCalendarMetricsHeaderProps> = ({
31
30
 
32
31
  const { t } = useTranslation();
33
32
  const handleAddPeer = () => {
34
- launchPatientWorkspace('peers-form', {});
33
+ launchWorkspace('peers-form', {});
35
34
  };
36
35
 
37
36
  if (isLoading) {
@@ -1,7 +1,7 @@
1
1
  @use '@carbon/colors';
2
2
  @use '@carbon/layout';
3
3
  @use '@carbon/type';
4
- @import '~@openmrs/esm-styleguide/src/vars';
4
+ @use '~@openmrs/esm-styleguide/src/vars' as *;
5
5
 
6
6
  .widgetContainer {
7
7
  background-color: colors.$white-0;
@@ -19,6 +19,7 @@ import { useTranslation } from 'react-i18next';
19
19
 
20
20
  type GenericDataTableProps = {
21
21
  headers: Array<{ key: string; header: string }>;
22
+ // TODO : Map row to correct carbon data-table row type
22
23
  rows: Array<Record<string, any>>;
23
24
  title: string;
24
25
  renderActionComponent?: () => ReactNode;
@@ -31,7 +32,7 @@ const GenericDataTable: React.FC<GenericDataTableProps> = ({ headers, rows, titl
31
32
  return (
32
33
  <div className={styles.widgetContainer}>
33
34
  <CardHeader title={title}>{renderActionComponent?.()}</CardHeader>
34
- <DataTable useZebraStyles size="sm" rows={rows} headers={headers}>
35
+ <DataTable useZebraStyles size="sm" rows={rows as any} headers={headers}>
35
36
  {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
36
37
  <Table {...getTableProps()}>
37
38
  <TableHead>
@@ -2,9 +2,11 @@ import { Dropdown } from '@carbon/react';
2
2
  import React, { useMemo, useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
 
5
+ type FilterStatus = 'completed' | 'pending' | 'all';
6
+
5
7
  type PeerCalendarTableFilterProps = {
6
8
  filterStatus?: 'completed' | 'pending' | 'all';
7
- onUpdateFilterStatus: (status: 'completed' | 'pending' | 'all') => void;
9
+ onUpdateFilterStatus: (status: FilterStatus) => void;
8
10
  };
9
11
 
10
12
  const PeerCalendarTableFilter: React.FC<PeerCalendarTableFilterProps> = ({
@@ -13,13 +15,14 @@ const PeerCalendarTableFilter: React.FC<PeerCalendarTableFilterProps> = ({
13
15
  }) => {
14
16
  const { t } = useTranslation();
15
17
  const statuses = useMemo(() => ['completed', 'pending', 'all'], []);
16
- const [_state, _setState] = useState<'completed' | 'pending' | 'all'>('completed');
18
+ const [_state, setSate] = useState<'completed' | 'pending' | 'all'>('completed');
17
19
  return (
18
20
  <div style={{ width: '200px' }}>
19
21
  <Dropdown
22
+ titleText={t('filterByStatue', 'Filter by status')}
20
23
  id="filterByStatus"
21
24
  onChange={(e) => {
22
- onUpdateFilterStatus(e.selectedItem);
25
+ onUpdateFilterStatus(e.selectedItem as FilterStatus);
23
26
  }}
24
27
  selectedItem={filterStatus}
25
28
  label={t('filterByStatus', 'Filter by status')}
@@ -1,6 +1,6 @@
1
1
  import { Column, Dropdown, RadioButton, RadioButtonGroup, SelectSkeleton } from '@carbon/react';
2
2
  import { useConfig } from '@openmrs/esm-framework';
3
- import React, { useEffect, useMemo } from 'react';
3
+ import React, { FC, useEffect, useMemo } from 'react';
4
4
  import { Controller, useFormContext } from 'react-hook-form';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { z } from 'zod';
@@ -11,45 +11,40 @@ import {
11
11
  getHivStatusBasedOnEnrollmentAndHTSEncounters,
12
12
  } from '../../contact-list/contact-list.resource';
13
13
  import usePersonAttributes from '../../hooks/usePersonAttributes';
14
- import { BOOLEAN_NO, BOOLEAN_YES, relationshipFormSchema } from '../relationship.resources';
14
+ import useRelativeHivEnrollment from '../../hooks/useRelativeHivEnrollment';
15
+ import useRelativeHTSEncounter from '../../hooks/useRelativeHTSEncounter';
16
+ import {
17
+ BOOLEAN_NO,
18
+ BOOLEAN_YES,
19
+ HIV_EXPOSED_INFANT,
20
+ INFANT_AGE_THRESHOLD_IN_MONTHS,
21
+ relationshipFormSchema,
22
+ } from '../relationship.resources';
15
23
  import {
16
24
  LIVING_WITH_PATIENT_CONCEPT_UUID,
17
25
  PARTNER_HIV_STATUS_CONCEPT_UUID,
18
26
  PNS_APROACH_CONCEPT_UUID,
19
27
  } from '../relationships-constants';
20
28
  import styles from './form.scss';
21
- import useRelativeHivEnrollment from '../../hooks/useRelativeHivEnrollment';
22
- import useRelativeHTSEncounter from '../../hooks/useRelativeHTSEncounter';
23
29
 
24
- const RelationshipBaselineInfoFormSection = () => {
30
+ type RelationshipBaselineInfoFormSectionProps = {
31
+ patientAgeMonths?: number;
32
+ patientUuid?: string;
33
+ };
34
+
35
+ const RelationshipBaselineInfoFormSection: FC<RelationshipBaselineInfoFormSectionProps> = ({
36
+ patientAgeMonths = null,
37
+ patientUuid,
38
+ }) => {
25
39
  const form = useFormContext<z.infer<typeof relationshipFormSchema>>();
26
- const {
27
- enrollment,
28
- isLoading: enrollmentLoading,
29
- error: enrollmentError,
30
- } = useRelativeHivEnrollment(form.watch('personB'));
31
- const {
32
- encounters,
33
- isLoading: encounterLoading,
34
- error: encounterError,
35
- } = useRelativeHTSEncounter(form.watch('personB'));
40
+ const { enrollment, isLoading: enrollmentLoading, error: enrollmentError } = useRelativeHivEnrollment(patientUuid);
41
+ const { encounters, isLoading: encounterLoading, error: encounterError } = useRelativeHTSEncounter(patientUuid);
36
42
  const hivStatusPersonB = getHivStatusBasedOnEnrollmentAndHTSEncounters(encounters, enrollment);
37
43
 
38
44
  const { t } = useTranslation();
39
- const personUuid = form.watch('personB');
40
45
  const config = useConfig<ConfigObject>();
41
46
  const { setValue } = form;
42
- const { attributes, isLoading } = usePersonAttributes(personUuid);
43
-
44
- const hivStatus = useMemo(
45
- () =>
46
- Object.entries(contactListConceptMap[PARTNER_HIV_STATUS_CONCEPT_UUID].answers).map(([uuid, display]) => ({
47
- label: display,
48
- value: uuid,
49
- })),
50
- [],
51
- );
52
-
47
+ const { attributes, isLoading } = usePersonAttributes(patientUuid);
53
48
  const pnsAproach = useMemo(
54
49
  () =>
55
50
  Object.entries(contactListConceptMap[PNS_APROACH_CONCEPT_UUID].answers).map(([uuid, display]) => ({
@@ -77,6 +72,26 @@ const RelationshipBaselineInfoFormSection = () => {
77
72
  (r) => r.uuid === observableRelationship && r.category.some((c) => c === 'sexual'),
78
73
  ) !== -1;
79
74
 
75
+ const hivStatus = useMemo(
76
+ () =>
77
+ Object.entries(contactListConceptMap[PARTNER_HIV_STATUS_CONCEPT_UUID].answers).reduce((prev, [uuid, display]) => {
78
+ if (
79
+ display === HIV_EXPOSED_INFANT &&
80
+ (patientAgeMonths === null || patientAgeMonths > INFANT_AGE_THRESHOLD_IN_MONTHS)
81
+ ) {
82
+ return prev;
83
+ }
84
+ return [
85
+ ...prev,
86
+ {
87
+ label: display,
88
+ value: uuid,
89
+ },
90
+ ];
91
+ }, []),
92
+ [patientAgeMonths],
93
+ );
94
+
80
95
  useEffect(() => {
81
96
  if ([observablePhysicalAssault, observableThreatened, observableSexualAssault].includes(BOOLEAN_YES)) {
82
97
  form.setValue('ipvOutCome', 'True');
@@ -196,7 +211,7 @@ const RelationshipBaselineInfoFormSection = () => {
196
211
  '1. Has he/she ever hit, kicked, slapped, or otherwise physically hurt you?',
197
212
  )}
198
213
  {...field}
199
- invalid={error?.message}
214
+ invalid={!!error?.message}
200
215
  invalidText={error?.message}
201
216
  className={styles.billingItem}>
202
217
  <RadioButton labelText={t('yes', 'Yes')} value={BOOLEAN_YES} id="physicalAssault_yes" />
@@ -214,7 +229,7 @@ const RelationshipBaselineInfoFormSection = () => {
214
229
  id="threatened"
215
230
  legendText={t('threatened', '2. Has he/she ever threatened to hurt you?')}
216
231
  {...field}
217
- invalid={error?.message}
232
+ invalid={!!error?.message}
218
233
  invalidText={error?.message}
219
234
  className={styles.billingItem}>
220
235
  <RadioButton labelText={t('yes', 'Yes')} value={BOOLEAN_YES} id="threatened_yes" />
@@ -235,7 +250,7 @@ const RelationshipBaselineInfoFormSection = () => {
235
250
  '3.Has he/she ever forced you to do something sexually that made you feel uncomfortable?',
236
251
  )}
237
252
  {...field}
238
- invalid={error?.message}
253
+ invalid={!!error?.message}
239
254
  invalidText={error?.message}
240
255
  className={styles.billingItem}>
241
256
  <RadioButton labelText={t('yes', 'Yes')} value={BOOLEAN_YES} id="sexualAssault_yes" />
@@ -256,7 +271,7 @@ const RelationshipBaselineInfoFormSection = () => {
256
271
  ) : (
257
272
  <Dropdown
258
273
  ref={field.ref}
259
- invalid={error?.message}
274
+ invalid={!!error?.message}
260
275
  invalidText={error?.message}
261
276
  id="ipvOutCome"
262
277
  titleText={t('ipvOutCome', 'IPV Outcome')}
@@ -2,7 +2,7 @@
2
2
  @use '@carbon/styles/scss/type';
3
3
  @use '@carbon/layout';
4
4
  @use '@carbon/colors';
5
- @import '~@openmrs/esm-styleguide/src/vars';
5
+ @use '~@openmrs/esm-styleguide/src/vars' as *;
6
6
 
7
7
  .loading {
8
8
  display: flex;
@@ -74,6 +74,7 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
74
74
  name="mode"
75
75
  render={({ field }) => (
76
76
  <ContentSwitcher
77
+ size="md"
77
78
  selectedIndex={field.value == 'search' ? 0 : 1}
78
79
  onChange={(value) => {
79
80
  let { index, name, text } = value;
@@ -128,7 +129,8 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
128
129
  name="personBInfo.givenName"
129
130
  render={({ field, fieldState: { error } }) => (
130
131
  <TextInput
131
- invalid={error?.message}
132
+ id={field.name}
133
+ invalid={!!error?.message}
132
134
  invalidText={error?.message}
133
135
  {...field}
134
136
  placeholder="First name"
@@ -143,7 +145,8 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
143
145
  name="personBInfo.middleName"
144
146
  render={({ field, fieldState: { error } }) => (
145
147
  <TextInput
146
- invalid={error?.message}
148
+ id={field.name}
149
+ invalid={!!error?.message}
147
150
  invalidText={error?.message}
148
151
  {...field}
149
152
  placeholder="Middle name"
@@ -158,7 +161,8 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
158
161
  name="personBInfo.familyName"
159
162
  render={({ field, fieldState: { error } }) => (
160
163
  <TextInput
161
- invalid={error?.message}
164
+ id={field.name}
165
+ invalid={!!error?.message}
162
166
  invalidText={error?.message}
163
167
  {...field}
164
168
  placeholder="Last name"
@@ -172,17 +176,15 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
172
176
  control={form.control}
173
177
  name="personBInfo.gender"
174
178
  render={({ field, fieldState: { error } }) => (
175
- <>
176
- <RadioButtonGroup
177
- name="personBInfo.gender"
178
- legendText={t('sex', 'Sex')}
179
- {...field}
180
- invalid={error?.message}
181
- invalidText={error?.message}>
182
- <RadioButton labelText={t('male', 'Male')} value="M" id="M" />
183
- <RadioButton labelText={t('female', 'Female')} value="F" id="F" />
184
- </RadioButtonGroup>
185
- </>
179
+ <RadioButtonGroup
180
+ name="personBInfo.gender"
181
+ legendText={t('sex', 'Sex')}
182
+ {...field}
183
+ invalid={!!error?.message}
184
+ invalidText={error?.message}>
185
+ <RadioButton labelText={t('male', 'Male')} value="M" id="M" />
186
+ <RadioButton labelText={t('female', 'Female')} value="F" id="F" />
187
+ </RadioButtonGroup>
186
188
  )}
187
189
  />
188
190
  </Column>
@@ -194,15 +196,16 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
194
196
  <DatePicker
195
197
  datePickerType="single"
196
198
  {...field}
197
- invalid={error?.message}
199
+ invalid={!!error?.message}
198
200
  invalidText={error?.message}
199
201
  className={styles.datePickerInput}>
200
202
  <DatePickerInput
201
- invalid={error?.message}
203
+ id={field.name}
204
+ invalid={!!error?.message}
202
205
  invalidText={error?.message}
203
206
  placeholder="mm/dd/yyyy"
204
207
  labelText={t('dateOfBirth', 'Date of birth')}
205
- size="xl"
208
+ size="lg"
206
209
  />
207
210
  </DatePicker>
208
211
  )}
@@ -219,7 +222,7 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
219
222
  render={({ field, fieldState: { error } }) => (
220
223
  <Dropdown
221
224
  ref={field.ref}
222
- invalid={error?.message}
225
+ invalid={!!error?.message}
223
226
  invalidText={error?.message}
224
227
  id="maritalStatus"
225
228
  titleText={t('maritalStatus', 'Marital status')}
@@ -242,7 +245,8 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
242
245
  name="personBInfo.address"
243
246
  render={({ field, fieldState: { error } }) => (
244
247
  <TextInput
245
- invalid={error?.message}
248
+ id={field.name}
249
+ invalid={!!error?.message}
246
250
  invalidText={error?.message}
247
251
  {...field}
248
252
  placeholder="Physical Address/Landmark"
@@ -258,7 +262,8 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
258
262
  render={({ field, fieldState: { error } }) => (
259
263
  <TextInput
260
264
  {...field}
261
- invalid={error?.message}
265
+ id={field.name}
266
+ invalid={!!error?.message}
262
267
  invalidText={error?.message}
263
268
  placeholder="Phone number"
264
269
  labelText={t('phoneNumber', 'Phone number')}
@@ -46,12 +46,13 @@ const BirthDateCalculator = ({ onClose, props: { date, onBirthDateChange } }) =>
46
46
  <Column>
47
47
  <Layer>
48
48
  <TextInput
49
+ id="age"
49
50
  placeholder="age"
50
51
  labelText={t('age', 'Age')}
51
52
  type="number"
52
53
  min={5}
53
54
  value={formState.age}
54
- onChange={(e) => setFormState({ ...formState, age: e.target.value })}
55
+ onChange={(e) => setFormState({ ...formState, age: parseInt(e.target.value) })}
55
56
  />
56
57
  </Layer>
57
58
  </Column>
@@ -61,7 +62,7 @@ const BirthDateCalculator = ({ onClose, props: { date, onBirthDateChange } }) =>
61
62
  datePickerType="single"
62
63
  value={formState.fromDate}
63
64
  onChange={([date]) => setFormState({ ...formState, fromDate: date })}>
64
- <DatePickerInput placeholder="mm/dd/yyyy" labelText={t('onDate', 'On Date')} size="xl" />
65
+ <DatePickerInput id="onDate" placeholder="mm/dd/yyyy" labelText={t('onDate', 'On Date')} size="lg" />
65
66
  </DatePicker>
66
67
  </Layer>
67
68
  </Column>
@@ -1,5 +1,5 @@
1
- import { openmrsFetch, restBaseUrl, Session, showModal, showSnackbar } from '@openmrs/esm-framework';
2
- import { mutate } from 'swr';
1
+ import { FetchResponse, openmrsFetch, restBaseUrl, Session, showModal, showSnackbar } from '@openmrs/esm-framework';
2
+ import useSWR, { mutate } from 'swr';
3
3
  import { z } from 'zod';
4
4
  import { Patient } from '../types';
5
5
  import { ConfigObject } from '../config-schema';
@@ -7,6 +7,8 @@ import omit from 'lodash/omit';
7
7
 
8
8
  export const BOOLEAN_YES = '1065';
9
9
  export const BOOLEAN_NO = '1066';
10
+ export const HIV_EXPOSED_INFANT = 'HIV exposed infant';
11
+ export const INFANT_AGE_THRESHOLD_IN_MONTHS = 24;
10
12
 
11
13
  export const relationshipUpdateFormSchema = z
12
14
  .object({
@@ -230,3 +232,17 @@ export const saveRelationship = async (
230
232
  throw error;
231
233
  }
232
234
  };
235
+
236
+ export const usePatientBirthdate = (patientUuid?: string) => {
237
+ const customRep = 'custom:(person:(birthdate))';
238
+ const url = `${restBaseUrl}/patient/${patientUuid}?v=${customRep}`;
239
+ const { data, error, isLoading } = useSWR<FetchResponse<{ person: { birthdate: string } }>>(
240
+ patientUuid ? url : null,
241
+ openmrsFetch,
242
+ );
243
+ return {
244
+ isLoading,
245
+ error,
246
+ birthdate: data?.data?.person?.birthdate,
247
+ };
248
+ };
@@ -12,8 +12,8 @@ interface RelationshipsTabProps {
12
12
  export const RelationshipsTab: React.FC<RelationshipsTabProps> = ({ patientUuid }) => {
13
13
  const { t } = useTranslation();
14
14
  return (
15
- <main>
16
- <Tabs className={styles.relationshipTabs}>
15
+ <main className={styles.relationshipTabs}>
16
+ <Tabs>
17
17
  <TabList className={styles.relationshipTablist} aria-label="List tabs" contained>
18
18
  <Tab className={styles.relationshipTab}>{t('family', 'Family')}</Tab>
19
19
  <Tab className={styles.relationshipTab}>{t('pnsContacts', 'PNS Contacts')}</Tab>
@@ -1,7 +1,7 @@
1
1
  @use '@carbon/colors';
2
2
  @use '@carbon/styles/scss/spacing';
3
3
  @use '@carbon/styles/scss/type';
4
- @import '~@openmrs/esm-styleguide/src/vars';
4
+ @use '~@openmrs/esm-styleguide/src/vars' as *;
5
5
 
6
6
  .relationshipTabs {
7
7
  grid-column: span 2;
package/src/root.scss CHANGED
@@ -1,12 +1,11 @@
1
1
  @use '@carbon/styles/scss/spacing';
2
2
  @use '@carbon/styles/scss/type';
3
- @import '~@openmrs/esm-styleguide/src/vars';
3
+ @use '~@openmrs/esm-styleguide/src/vars' as *;
4
4
 
5
5
  $ui-01: #f4f4f4;
6
6
  $ui-02: #ffffff;
7
7
  $ui-03: #e0e0e0;
8
8
  $ui-05: #161616;
9
- $ui-background: #ffffff;
10
9
  $color-gray-70: #525252;
11
10
  $color-blue-60-2: #0f62fe;
12
11
  $color-yellow-50: #feecae;