@kenyaemr/esm-morgue-app 5.4.1-pre.1842 → 5.4.1-pre.1850

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 (111) hide show
  1. package/.turbo/turbo-build.log +85 -22
  2. package/dist/160.js +1 -0
  3. package/dist/160.js.map +1 -0
  4. package/dist/195.js +1 -0
  5. package/dist/195.js.map +1 -0
  6. package/dist/240.js +1 -0
  7. package/dist/240.js.map +1 -0
  8. package/dist/282.js +1 -0
  9. package/dist/282.js.map +1 -0
  10. package/dist/300.js +1 -1
  11. package/dist/{561.js → 521.js} +1 -1
  12. package/dist/521.js.map +1 -0
  13. package/dist/527.js +1 -0
  14. package/dist/527.js.map +1 -0
  15. package/dist/596.js +1 -0
  16. package/dist/596.js.map +1 -0
  17. package/dist/652.js +1 -1
  18. package/dist/652.js.map +1 -1
  19. package/dist/659.js +2 -0
  20. package/dist/659.js.map +1 -0
  21. package/dist/675.js +2 -0
  22. package/dist/{485.js.map → 675.js.map} +1 -1
  23. package/dist/730.js +1 -0
  24. package/dist/730.js.map +1 -0
  25. package/dist/755.js +1 -0
  26. package/dist/755.js.map +1 -0
  27. package/dist/795.js +1 -0
  28. package/dist/795.js.map +1 -0
  29. package/dist/818.js +1 -0
  30. package/dist/818.js.map +1 -0
  31. package/dist/{942.js → 870.js} +1 -1
  32. package/dist/870.js.map +1 -0
  33. package/dist/909.js +2 -0
  34. package/dist/909.js.map +1 -0
  35. package/dist/926.js +1 -1
  36. package/dist/926.js.map +1 -1
  37. package/dist/929.js +1 -0
  38. package/dist/929.js.map +1 -0
  39. package/dist/960.js +1 -0
  40. package/dist/960.js.map +1 -0
  41. package/dist/kenyaemr-esm-morgue-app.js +1 -1
  42. package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +371 -127
  43. package/dist/kenyaemr-esm-morgue-app.js.map +1 -1
  44. package/dist/main.js +1 -1
  45. package/dist/main.js.map +1 -1
  46. package/dist/routes.json +1 -1
  47. package/package.json +1 -1
  48. package/src/autosuggest/autosuggest.component.tsx +162 -0
  49. package/src/autosuggest/autosuggest.scss +61 -0
  50. package/src/autosuggest/patient-search-info.component.tsx +75 -0
  51. package/src/autosuggest/patient-search-info.scss +62 -0
  52. package/src/autosuggest/search-empty-state.component.tsx +21 -0
  53. package/src/autosuggest/search-empty-state.scss +18 -0
  54. package/src/card/avail-compartment.compartment.tsx +40 -41
  55. package/src/card/compartment-view.compartment.tsx +37 -14
  56. package/src/card/compartment.scss +18 -13
  57. package/src/card/compartmentSharing.component.tsx +21 -0
  58. package/src/card/compartmentSharing.scss +24 -0
  59. package/src/card/empty-compartment.component.tsx +11 -7
  60. package/src/card/empty-compartment.scss +61 -0
  61. package/src/component/deceasedInfo/deceased-info.component.tsx +1 -1
  62. package/src/component/main.component.tsx +1 -7
  63. package/src/component/next-of-kin-details/nextOfKinDetails.component.tsx +50 -0
  64. package/src/component/next-of-kin-details/nextOfKinDetails.scss +37 -0
  65. package/src/config-schema.ts +30 -22
  66. package/src/extension/actionButton.component.tsx +74 -0
  67. package/src/extension/actionButton.scss +69 -0
  68. package/src/extension/deceasedInfoBanner.component.tsx +57 -0
  69. package/src/hook/useAdmitPatient.ts +285 -0
  70. package/src/hook/useDeceasedPatients.ts +12 -0
  71. package/src/hook/useDischargedPatient.ts +55 -0
  72. package/src/hook/useMorgue.resource.ts +11 -120
  73. package/src/hook/useMortuaryAdmissionLocation.ts +64 -0
  74. package/src/hook/usePersonAttributes.ts +65 -0
  75. package/src/index.ts +4 -0
  76. package/src/routes.json +24 -7
  77. package/src/tables/admitted-queue.component.tsx +17 -21
  78. package/src/tables/discharge-queue.component.tsx +33 -27
  79. package/src/tables/generic-table.component.tsx +44 -19
  80. package/src/tabs/tabs.component.tsx +36 -16
  81. package/src/tabs/tabs.scss +3 -186
  82. package/src/types/index.ts +291 -9
  83. package/src/utils/utils.ts +55 -4
  84. package/src/workspaces/admit-body.scss +46 -0
  85. package/src/workspaces/admit-body.workspace.tsx +79 -0
  86. package/src/workspaces/discharge-body.scss +2 -2
  87. package/src/workspaces/discharge-body.workspace.tsx +157 -101
  88. package/src/workspaces/patientAdditionalInfoForm.workspace.tsx +141 -218
  89. package/src/workspaces/swap-unit.scss +46 -0
  90. package/src/workspaces/swap-unit.workspace.tsx +168 -0
  91. package/translations/en.json +22 -7
  92. package/dist/340.js +0 -1
  93. package/dist/340.js.map +0 -1
  94. package/dist/38.js +0 -1
  95. package/dist/38.js.map +0 -1
  96. package/dist/485.js +0 -2
  97. package/dist/553.js +0 -1
  98. package/dist/553.js.map +0 -1
  99. package/dist/561.js.map +0 -1
  100. package/dist/592.js +0 -2
  101. package/dist/592.js.map +0 -1
  102. package/dist/759.js +0 -1
  103. package/dist/759.js.map +0 -1
  104. package/dist/942.js.map +0 -1
  105. package/dist/987.js +0 -2
  106. package/dist/987.js.map +0 -1
  107. package/src/component/deceasedInfo/deceased-header.component.tsx +0 -37
  108. package/src/tables/waiting-queue.component.tsx +0 -91
  109. /package/dist/{987.js.LICENSE.txt → 659.js.LICENSE.txt} +0 -0
  110. /package/dist/{485.js.LICENSE.txt → 675.js.LICENSE.txt} +0 -0
  111. /package/dist/{592.js.LICENSE.txt → 909.js.LICENSE.txt} +0 -0
@@ -1,43 +1,53 @@
1
+ import { Button, Layer, Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react';
2
+ import { SearchAdvanced } from '@carbon/react/icons';
3
+ import { launchWorkspace } from '@openmrs/esm-framework';
1
4
  import React from 'react';
2
- import { Tabs, Tab, TabList, TabPanel, TabPanels, InlineLoading, Layer } from '@carbon/react';
3
5
  import { useTranslation } from 'react-i18next';
4
- import styles from './tabs.scss';
5
- import { WaitingQueue } from '../tables/waiting-queue.component';
6
+ import useEmrConfiguration from '../hook/useAdmitPatient';
7
+ import { useDischargedPatient } from '../hook/useDischargedPatient';
8
+ import { useAdmissionLocation } from '../hook/useMortuaryAdmissionLocation';
6
9
  import { AdmittedQueue } from '../tables/admitted-queue.component';
7
- import { useDeceasedPatient } from '../hook/useMorgue.resource';
8
10
  import { DischargedBodies } from '../tables/discharge-queue.component';
11
+ import styles from './tabs.scss';
9
12
 
10
13
  export const MorgueTabs: React.FC = () => {
11
14
  const { t } = useTranslation();
12
- const { data: deceasedPatients, error, isLoading } = useDeceasedPatient();
15
+ const { admissionLocation } = useAdmissionLocation();
16
+ const admittedCount = admissionLocation?.bedLayouts?.filter((bed) => bed.status === 'OCCUPIED').length || 0;
17
+ const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
13
18
 
14
- const awaitingCount = deceasedPatients?.filter((p) => p.status === 'awaiting').length || 0;
15
- const admittedCount = deceasedPatients?.filter((p) => p.status === 'admitted').length || 0;
16
- const dischargedCount = deceasedPatients?.filter((p) => p.status === 'discharged').length || 0;
19
+ const dischargeEncounterTypeUuid = emrConfiguration?.exitFromInpatientEncounterType?.uuid;
20
+ const { dischargedPatientUuids, isLoading: isLoadingDischargedPatient } =
21
+ useDischargedPatient(dischargeEncounterTypeUuid);
22
+ const dischargedCount = dischargedPatientUuids?.length || 0;
17
23
 
18
24
  const getTabLabel = (baseLabel: string, count: number | null) => (
19
25
  <span className={styles.tabLabel}>
20
- {baseLabel} {isLoading ? '' : `(${count})`}
26
+ {baseLabel} {count ? `(${count})` : ''}
21
27
  </span>
22
28
  );
23
29
 
24
30
  const tabPanels = [
25
- {
26
- name: getTabLabel(t('waitQueue', 'Waiting queue'), awaitingCount),
27
- component: <WaitingQueue isLoading={isLoading} deceasedPatients={deceasedPatients} error={error} />,
28
- },
29
31
  {
30
32
  name: getTabLabel(t('admitted', 'Admitted'), admittedCount),
31
33
  component: <AdmittedQueue />,
32
34
  },
33
35
  {
34
36
  name: getTabLabel(t('discharged', 'Discharged'), dischargedCount),
35
- component: <DischargedBodies isLoading={isLoading} deceasedPatients={deceasedPatients} error={error} />,
37
+ component: (
38
+ <DischargedBodies
39
+ isLoading={isLoadingDischargedPatient || isLoadingEmrConfiguration}
40
+ error={errorFetchingEmrConfiguration}
41
+ dischargedPatientUuids={dischargedPatientUuids}
42
+ />
43
+ ),
36
44
  },
37
45
  ];
38
-
46
+ const handleAdmitBodyWorkspace = () => {
47
+ launchWorkspace('admit-body-form');
48
+ };
39
49
  return (
40
- <div className={styles.referralsList} data-testid="referralsList-list">
50
+ <div className={styles.referralsList} data-testid="">
41
51
  <Tabs selected={0} role="navigation">
42
52
  <div className={styles.tabsContainer}>
43
53
  <TabList aria-label="Content Switcher as Tabs" contained>
@@ -45,6 +55,16 @@ export const MorgueTabs: React.FC = () => {
45
55
  <Tab key={index}>{tab.name}</Tab>
46
56
  ))}
47
57
  </TabList>
58
+ <div className={styles.actionBtn}>
59
+ <Button
60
+ kind="primary"
61
+ renderIcon={(props) => <SearchAdvanced size={40} {...props} />}
62
+ onClick={() => handleAdmitBodyWorkspace()}
63
+ className={styles.actionBtn}
64
+ disabled={isLoadingDischargedPatient}>
65
+ {t('admitBodies', 'Admit bodies')}
66
+ </Button>
67
+ </div>
48
68
  </div>
49
69
 
50
70
  <TabPanels>
@@ -2,197 +2,14 @@
2
2
  @use '@carbon/type';
3
3
  @use '@carbon/colors';
4
4
 
5
- .container {
6
- margin: 2rem 0;
7
- }
8
-
9
- .emptyStateContainer,
10
- .loaderContainer {
11
- @extend .container;
12
- }
13
-
14
- .serviceContainer {
15
- width: 100%;
16
- margin: 0 auto;
17
- max-width: 95vw;
18
- padding-bottom: 0;
19
-
20
- :has(.filterEmptyState) {
21
- border-bottom: none;
22
- }
23
- }
24
-
25
- .left-justified-items {
26
- display: flex;
27
- flex-direction: row;
28
- align-items: center;
29
- cursor: pointer;
30
- align-items: center;
31
- }
32
-
33
- .filterContainer {
34
- flex: 1;
35
-
36
- :global(.cds--dropdown__wrapper--inline) {
37
- gap: 0;
38
- }
39
-
40
- :global(.cds--list-box__menu-icon) {
41
- height: 1rem;
42
- }
43
-
44
- :global(.cds--list-box__menu) {
45
- min-width: max-content;
46
- }
47
-
48
- :global(.cds--list-box) {
49
- margin-left: layout.$spacing-03;
50
- }
51
- }
52
-
53
- .menu {
54
- margin-left: layout.$spacing-03;
55
- }
56
-
57
- .headerContainer {
58
- display: flex;
59
- justify-content: space-between;
60
- align-items: center;
61
- padding: layout.$spacing-04 layout.$spacing-05;
62
- }
63
-
64
- .backgroundDataFetchingIndicator {
65
- align-items: center;
5
+ .tabsContainer {
66
6
  display: flex;
67
- flex: 1;
68
7
  justify-content: space-between;
69
-
70
- &:global(.cds--inline-loading) {
71
- max-height: layout.$spacing-05;
72
- }
73
- }
74
-
75
- .tableContainer section {
76
- position: relative;
77
- }
78
-
79
- .tableContainer a {
80
- text-decoration: none;
81
- }
82
-
83
- .pagination {
84
- overflow: hidden;
85
-
86
- &:global(.cds--pagination) {
87
- border-top: none;
88
- }
89
- }
90
-
91
- .hiddenRow {
92
- display: none;
93
- }
94
-
95
- .emptyRow {
96
- padding: 0 1rem;
97
- display: flex;
98
8
  align-items: center;
99
- }
100
-
101
- .visitSummaryContainer {
102
- width: 100%;
103
- max-width: 768px;
104
- margin: 1rem auto;
105
- }
106
-
107
- .expandedActiveVisitRow > td > div {
108
- max-height: max-content !important;
109
- }
110
-
111
- .expandedActiveVisitRow td {
112
- padding: 0 2rem;
113
- }
114
-
115
- .expandedActiveVisitRow th[colspan] td[colspan] > div:first-child {
116
- padding: 0 1rem;
117
- }
118
-
119
- .action {
120
- margin-bottom: layout.$spacing-03;
121
- }
122
-
123
- .illo {
124
- margin-top: layout.$spacing-05;
125
- }
126
-
127
- .content {
128
- @include type.type-style('heading-compact-01');
129
- margin-top: layout.$spacing-05;
130
- margin-bottom: layout.$spacing-03;
131
- }
132
-
133
- .desktopHeading,
134
- .tabletHeading {
135
- text-align: left;
136
- text-transform: capitalize;
137
- flex: 1;
138
-
139
- h4 {
140
- @include type.type-style('heading-compact-02');
141
-
142
- &:after {
143
- content: '';
144
- display: block;
145
- width: 2rem;
146
- padding-top: 3px;
147
- border-bottom: 0.375rem solid;
148
- }
149
- }
150
- }
151
-
152
- .tile {
153
- text-align: center;
154
- }
155
-
156
- .menuitem {
157
- max-width: none;
158
- }
159
-
160
- .filterEmptyState {
161
- display: flex;
162
- justify-content: center;
163
- align-items: center;
164
- padding: layout.$spacing-05;
165
- margin: layout.$spacing-09;
166
- text-align: center;
167
- }
168
-
169
- .filterEmptyStateTile {
170
- margin: auto;
171
- }
172
-
173
- .filterEmptyStateContent {
174
- @include type.type-style('heading-compact-02');
175
- margin-bottom: 0.5rem;
176
- }
177
-
178
- .filterEmptyStateHelper {
179
- @include type.type-style('body-compact-01');
9
+ padding-left: layout.$spacing-05;
180
10
  }
181
11
 
182
12
  .actionBtn {
183
13
  display: flex;
184
- gap: 0.5rem;
185
- margin-right: layout.$spacing-07;
186
- }
187
-
188
- .mainSection {
189
- display: grid;
190
- grid-template-columns: 16rem 1fr;
191
- }
192
-
193
- .tabsContainer {
194
- display: flex;
195
- justify-content: space-between;
196
- align-items: center;
197
- padding-left: 1rem;
14
+ margin-right: layout.$spacing-03;
198
15
  }
@@ -1,6 +1,4 @@
1
- import { OpenmrsResource } from '@openmrs/esm-framework';
2
-
3
- export interface Concept extends OpenmrsResource {}
1
+ import { type OpenmrsResource, type Concept, type OpenmrsResourceStrict } from '@openmrs/esm-framework';
4
2
  export type QueuePriority = 'Emergency' | 'Not Urgent' | 'Priority' | 'Urgent';
5
3
  export type MappedQueuePriority = Omit<QueuePriority, 'Urgent'>;
6
4
  export type QueueService = 'Clinical consultation' | 'Triage';
@@ -9,13 +7,8 @@ export interface Patient {
9
7
  uuid: string;
10
8
  display: string;
11
9
  identifiers: Array<{
12
- identifier: string;
13
10
  uuid: string;
14
- preferred: boolean;
15
- location: {
16
- uuid: string;
17
- name: string;
18
- };
11
+ display: string;
19
12
  }>;
20
13
  person: {
21
14
  uuid: string;
@@ -35,6 +28,14 @@ export interface Patient {
35
28
  countyDistrict: string | null;
36
29
  address4: string | null;
37
30
  } | null;
31
+ attributes: {
32
+ uuid: string;
33
+ display: string;
34
+ value: string;
35
+ attributeType: {
36
+ uuid: string;
37
+ };
38
+ }[];
38
39
  };
39
40
  }
40
41
  export interface DeceasedInfo {
@@ -304,3 +305,284 @@ export interface PreferredAddress {
304
305
  countyDistrict: any;
305
306
  address4: any;
306
307
  }
308
+
309
+ export interface MortuaryLocationFetchResponse {
310
+ totalBeds: number;
311
+ occupiedBeds: number;
312
+ ward: Location;
313
+ bedLayouts: Array<BedLayout>;
314
+ }
315
+ export interface BedLayout {
316
+ rowNumber: number;
317
+ columnNumber: number;
318
+ bedNumber: string;
319
+ bedId: number;
320
+ bedUuid: string;
321
+ status: BedStatus;
322
+ bedType: BedType;
323
+ location: string;
324
+ patients: Patient[];
325
+ bedTagMaps: BedTagMap[];
326
+ }
327
+ export interface BedType {
328
+ uuid: string;
329
+ name: string;
330
+ displayName: string;
331
+ description: string;
332
+ resourceVersion: string;
333
+ }
334
+ interface BedTagMap {
335
+ uuid: string;
336
+ bedTag: {
337
+ id: number;
338
+ name: string;
339
+ uuid: string;
340
+ resourceVersion: string;
341
+ };
342
+ }
343
+ export type BedStatus = 'AVAILABLE' | 'OCCUPIED';
344
+
345
+ export interface BedDetail {
346
+ bedId: number;
347
+ bedNumber: string;
348
+ bedType: BedType;
349
+ physicalLocation: Location;
350
+ patients: Array<Patient>;
351
+ }
352
+
353
+ export interface LocationTag extends OpenmrsResource {
354
+ name: string;
355
+ }
356
+
357
+ export type DispositionType = 'ADMIT' | 'DISCHARGE' | 'TRANSFER';
358
+
359
+ export interface ObsPayload {
360
+ concept: Concept | string;
361
+ value?: string | OpenmrsResource;
362
+ groupMembers?: Array<ObsPayload>;
363
+ }
364
+
365
+ export interface EmrApiConfigurationResponse {
366
+ admissionEncounterType: OpenmrsResource;
367
+ clinicianEncounterRole: OpenmrsResource;
368
+ consultFreeTextCommentsConcept: OpenmrsResource;
369
+ visitNoteEncounterType: OpenmrsResource;
370
+ inpatientNoteEncounterType: OpenmrsResource;
371
+ transferRequestEncounterType: OpenmrsResource;
372
+ transferWithinHospitalEncounterType: OpenmrsResource;
373
+ exitFromInpatientEncounterType: OpenmrsResource;
374
+ supportsTransferLocationTag: LocationTag;
375
+ supportsAdmissionLocationTag: LocationTag;
376
+ supportsLoginLocationTag: LocationTag;
377
+ supportsVisitsLocationTag: LocationTag;
378
+ dispositionDescriptor: {
379
+ admissionLocationConcept: OpenmrsResource;
380
+ dateOfDeathConcept: OpenmrsResource;
381
+ dispositionConcept: OpenmrsResource;
382
+ internalTransferLocationConcept: OpenmrsResource;
383
+ dispositionSetConcept: OpenmrsResource;
384
+ };
385
+ dispositions: Array<{
386
+ encounterTypes: null;
387
+ keepsVisitOpen: null;
388
+ additionalObs: null;
389
+ careSettingTypes: ['OUTPATIENT'];
390
+ name: string;
391
+ conceptCode: string;
392
+ type: DispositionType;
393
+ actions: [];
394
+ excludedEncounterTypes: Array<string>;
395
+ uuid: string;
396
+ }>;
397
+ bedAssignmentEncounterType: OpenmrsResource;
398
+ cancelADTRequestEncounterType: OpenmrsResource;
399
+ denyAdmissionConcept: OpenmrsResource;
400
+ admissionDecisionConcept: OpenmrsResource;
401
+ }
402
+
403
+ export const customRepProps = [
404
+ ['metadataSourceName', 'ref'],
405
+ ['orderingProviderEncounterRole', 'ref'],
406
+ ['supportsTransferLocationTag', '(uuid,display,name,links)'],
407
+ ['unknownLocation', 'ref'],
408
+ ['denyAdmissionConcept', 'ref'],
409
+ ['admissionForm', 'ref'],
410
+ ['exitFromInpatientEncounterType', 'ref'],
411
+ ['extraPatientIdentifierTypes', 'ref'],
412
+ ['consultFreeTextCommentsConcept', 'ref'],
413
+ ['sameAsConceptMapType', 'ref'],
414
+ ['testPatientPersonAttributeType', 'ref'],
415
+ ['admissionDecisionConcept', 'ref'],
416
+ ['supportsAdmissionLocationTag', '(uuid,display,name,links)'],
417
+ ['checkInEncounterType', 'ref'],
418
+ ['transferWithinHospitalEncounterType', 'ref'],
419
+ ['suppressedDiagnosisConcepts', 'ref'],
420
+ ['primaryIdentifierType', 'ref'],
421
+ ['nonDiagnosisConceptSets', 'ref'],
422
+ ['fullPrivilegeLevel', 'ref'],
423
+ ['unknownProvider', 'ref'],
424
+ ['diagnosisSets', 'ref'],
425
+ ['personImageDirectory', 'ref'],
426
+ ['visitNoteEncounterType', 'ref'],
427
+ ['inpatientNoteEncounterType', 'ref'],
428
+ ['transferRequestEncounterType', 'ref'],
429
+ ['consultEncounterType', 'ref'],
430
+ ['diagnosisMetadata', 'ref'],
431
+ ['narrowerThanConceptMapType', 'ref'],
432
+ ['clinicianEncounterRole', 'ref'],
433
+ ['conceptSourcesForDiagnosisSearch', 'ref'],
434
+ ['patientDiedConcept', 'ref'],
435
+ ['emrApiConceptSource', 'ref'],
436
+ ['lastViewedPatientSizeLimit', 'ref'],
437
+ ['identifierTypesToSearch', 'ref'],
438
+ ['telephoneAttributeType', 'ref'],
439
+ ['checkInClerkEncounterRole', 'ref'],
440
+ ['dischargeForm', 'ref'],
441
+ ['unknownCauseOfDeathConcept', 'ref'],
442
+ ['visitAssignmentHandlerAdjustEncounterTimeOfDayIfNecessary', 'ref'],
443
+ ['atFacilityVisitType', 'ref'],
444
+ ['visitExpireHours', 'ref'],
445
+ ['admissionEncounterType', 'ref'],
446
+ ['motherChildRelationshipType', 'ref'],
447
+ ['dispositions', 'ref'],
448
+ ['dispositionDescriptor', 'ref'],
449
+ ['highPrivilegeLevel', 'ref'],
450
+ ['supportsLoginLocationTag', '(uuid,display,name,links)'],
451
+ ['unknownPatientPersonAttributeType', 'ref'],
452
+ ['supportsVisitsLocationTag', '(uuid,display,name,links)'],
453
+ ['transferForm', 'ref'],
454
+ ['bedAssignmentEncounterType', 'ref'],
455
+ ['cancelADTRequestEncounterType', 'ref'],
456
+ ['admissionDecisionConcept', 'ref'],
457
+ ['denyAdmissionConcept', 'ref'],
458
+ ];
459
+
460
+ export interface Encounter extends OpenmrsResourceStrict {
461
+ encounterDatetime?: string;
462
+ patient?: Patient;
463
+ location?: Location;
464
+ form?: OpenmrsResource;
465
+ encounterType?: EncounterType;
466
+ obs?: Array<Observation>;
467
+ orders?: any;
468
+ voided?: boolean;
469
+ visit?: Visit;
470
+ encounterProviders?: Array<EncounterProvider>;
471
+ diagnoses?: any;
472
+ }
473
+ export interface EncounterProvider extends OpenmrsResourceStrict {
474
+ provider?: OpenmrsResource;
475
+ encounterRole?: EncounterRole;
476
+ voided?: boolean;
477
+ }
478
+
479
+ export interface EncounterType extends OpenmrsResourceStrict {
480
+ name?: string;
481
+ description?: string;
482
+ retired?: boolean;
483
+ }
484
+
485
+ export interface EncounterRole extends OpenmrsResourceStrict {
486
+ name?: string;
487
+ description?: string;
488
+ retired?: boolean;
489
+ }
490
+ export interface Observation extends OpenmrsResourceStrict {
491
+ concept: OpenmrsResource;
492
+ person: Person;
493
+ obsDatetime: string;
494
+ accessionNumber: string;
495
+ obsGroup: Observation;
496
+ value: number | string | boolean | OpenmrsResource;
497
+ valueCodedName: OpenmrsResource;
498
+ groupMembers: Array<Observation>;
499
+ comment: string;
500
+ location: Location;
501
+ order: OpenmrsResource;
502
+ encounter: Encounter;
503
+ voided: boolean;
504
+ }
505
+
506
+ export interface CurrentLocationEncounterResponse {
507
+ results: {
508
+ visit: {
509
+ patient: {
510
+ uuid: string;
511
+ display: string;
512
+ };
513
+ };
514
+ encounterAssigningToCurrentInpatientLocation: {
515
+ encounterDatetime: string;
516
+ };
517
+ };
518
+ }
519
+
520
+ export interface PatientInfo {
521
+ person?: {
522
+ uuid: string;
523
+ display: string;
524
+ age: number;
525
+ causeOfDeath?: {
526
+ display: string;
527
+ };
528
+ };
529
+ uuid: string;
530
+ attributes: {
531
+ uuid: string;
532
+ display: string;
533
+ }[];
534
+ }
535
+ export interface FHIREncounter {
536
+ resourceType: string;
537
+ id: string;
538
+ meta?: {
539
+ versionId: string;
540
+ lastUpdated: string;
541
+ tag: {
542
+ system: string;
543
+ code: string;
544
+ display: string;
545
+ }[];
546
+ };
547
+ status: string;
548
+ class: {
549
+ system: string;
550
+ code: string;
551
+ };
552
+ type: {
553
+ coding: {
554
+ system: string;
555
+ code: string;
556
+ display: string;
557
+ }[];
558
+ }[];
559
+ subject: {
560
+ reference: string;
561
+ type?: string;
562
+ display?: string;
563
+ };
564
+ participant?: {
565
+ individual: {
566
+ reference: string;
567
+ type?: string;
568
+ identifier?: {
569
+ value: string;
570
+ };
571
+ display?: string;
572
+ };
573
+ }[];
574
+ period: {
575
+ start: string;
576
+ };
577
+ location: {
578
+ location: {
579
+ reference: string;
580
+ type?: string;
581
+ display?: string;
582
+ };
583
+ }[];
584
+ partOf?: {
585
+ reference: string;
586
+ type: string;
587
+ };
588
+ }
@@ -1,5 +1,6 @@
1
1
  import { makeUrl } from '@openmrs/esm-framework';
2
2
  import dayjs from 'dayjs';
3
+ import { z } from 'zod';
3
4
 
4
5
  /**
5
6
  * Generates a URL based on the given path and the current location.
@@ -29,12 +30,22 @@ export const formatDeceasedName = (patient) => {
29
30
  * @param startDate - The starting date in string or Date format.
30
31
  * @returns The number of days from the start date to today.
31
32
  */
32
- export function convertDateToDays(startDate: string | Date): number {
33
- const today = dayjs();
33
+ export function convertDateToDays(startDate?: string | Date): number {
34
+ if (!startDate) {
35
+ return 0;
36
+ }
37
+
34
38
  const start = dayjs(startDate);
35
- return today.diff(start, 'day');
36
- }
39
+ const today = dayjs();
40
+
41
+ const diff = today.diff(start, 'day', true);
37
42
 
43
+ if (Math.abs(diff) < 1 && diff !== 0) {
44
+ return 1;
45
+ }
46
+
47
+ return Math.abs(Math.round(diff));
48
+ }
38
49
  /**
39
50
  * Formats a given date string into "DD-MMM-YYYY, hh:mm A" format.
40
51
  *
@@ -57,3 +68,43 @@ export const getCurrentTime = () => {
57
68
  const period = now.getHours() >= 12 ? 'PM' : 'AM';
58
69
  return { time: `${hours}:${minutes}`, period };
59
70
  };
71
+
72
+ export const patientInfoSchema = z.object({
73
+ dateOfAdmission: z
74
+ .date({ coerce: true })
75
+ .refine((date) => !!date, 'Date of admission is required')
76
+ .refine((date) => date <= new Date(), 'Date of admission cannot be in the future'),
77
+ timeOfDeath: z.string().nonempty('Time of death is required'),
78
+ period: z
79
+ .string()
80
+ .nonempty('AM/PM is required')
81
+ .regex(/^(AM|PM)$/i, 'Invalid period'),
82
+ tagNumber: z.string().nonempty('Tag number is required'),
83
+ obNumber: z.string().optional(),
84
+ policeName: z.string().optional(),
85
+ policeIDNo: z.string().optional(),
86
+ dischargeArea: z.string().optional(),
87
+ visitType: z.string().uuid('invalid visit type'),
88
+ availableCompartment: z.number({ coerce: true }),
89
+ services: z.array(z.string().uuid('invalid service')).nonempty('Must select one service'),
90
+ paymentMethod: z.string().uuid('invalid payment method'),
91
+ insuranceScheme: z.string().optional(),
92
+ policyNumber: z.string().optional(),
93
+ });
94
+
95
+ export const dischargeSchema = z.object({
96
+ dateOfDischarge: z.date({ coerce: true }).refine((date) => !!date, 'Date of discharge is required'),
97
+ timeOfDischarge: z.string().nonempty('Time of discharge is required'),
98
+ period: z
99
+ .string()
100
+ .nonempty('AM/PM is required')
101
+ .regex(/^(AM|PM)$/i, 'Invalid period'),
102
+ burialPermitNumber: z.string().nonempty('Burial Permit Number is required'),
103
+ nextOfKinNames: z.string().nonempty('Next of kin names is required'),
104
+ relationshipType: z.string().nonempty('Next of kin relationship is required'),
105
+ nextOfKinAddress: z.string().nonempty('Next of kin address is required'),
106
+ nextOfKinContact: z
107
+ .string()
108
+ .regex(/^\d{10}$/, 'Phone number must be exactly 10 digits')
109
+ .nonempty('Next of kin phone number is required'),
110
+ });