@digihmis/esm-home-app 1.0.0
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.
- package/README.md +5 -0
- package/dist/117.js +1 -0
- package/dist/117.js.map +1 -0
- package/dist/132.js +1 -0
- package/dist/132.js.map +1 -0
- package/dist/137.js +1 -0
- package/dist/137.js.map +1 -0
- package/dist/150.js +1 -0
- package/dist/150.js.map +1 -0
- package/dist/152.js +1 -0
- package/dist/152.js.map +1 -0
- package/dist/209.js +1 -0
- package/dist/209.js.map +1 -0
- package/dist/248.js +1 -0
- package/dist/248.js.map +1 -0
- package/dist/252.js +12 -0
- package/dist/252.js.map +1 -0
- package/dist/317.js +1 -0
- package/dist/317.js.map +1 -0
- package/dist/349.js +1 -0
- package/dist/349.js.map +1 -0
- package/dist/371.js +1 -0
- package/dist/371.js.map +1 -0
- package/dist/378.js +1 -0
- package/dist/378.js.map +1 -0
- package/dist/442.js +1 -0
- package/dist/442.js.map +1 -0
- package/dist/45.js +1 -0
- package/dist/45.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/49.js +1 -0
- package/dist/49.js.map +1 -0
- package/dist/508.js +1 -0
- package/dist/508.js.map +1 -0
- package/dist/567.js +1 -0
- package/dist/567.js.map +1 -0
- package/dist/60.js +1 -0
- package/dist/60.js.map +1 -0
- package/dist/61.js +1 -0
- package/dist/61.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/712.js +1 -0
- package/dist/712.js.map +1 -0
- package/dist/720.js +1 -0
- package/dist/720.js.map +1 -0
- package/dist/742.js +1 -0
- package/dist/742.js.map +1 -0
- package/dist/771.js +1 -0
- package/dist/771.js.map +1 -0
- package/dist/806.js +1 -0
- package/dist/806.js.map +1 -0
- package/dist/912.js +1 -0
- package/dist/912.js.map +1 -0
- package/dist/913.js +1 -0
- package/dist/913.js.map +1 -0
- package/dist/940.js +1 -0
- package/dist/940.js.map +1 -0
- package/dist/987.js +15 -0
- package/dist/987.js.map +1 -0
- package/dist/digihmis-esm-home-app.js +6 -0
- package/dist/digihmis-esm-home-app.js.buildmanifest.json +896 -0
- package/dist/digihmis-esm-home-app.js.map +1 -0
- package/dist/main.js +6 -0
- package/dist/main.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.js +26 -0
- package/package.json +51 -0
- package/rspack.config.js +1 -0
- package/src/config-schema.ts +358 -0
- package/src/dashboards/administration-dashboard/administration-dashboard.component.tsx +68 -0
- package/src/dashboards/billing-dashboard/billing-dashboard.component.tsx +71 -0
- package/src/dashboards/outpatient-dashboard/outpatient-dashboard.component.tsx +71 -0
- package/src/dashboards/registration-dashboard/registration-dashboard.component.tsx +58 -0
- package/src/dashboards/triage-dashboard/triage-dashboard.component.tsx +72 -0
- package/src/declarations.d.ts +6 -0
- package/src/generic-clock-in-modal/clock-in.scss +41 -0
- package/src/generic-clock-in-modal/generic-clock-in.modal.tsx +295 -0
- package/src/generic-clock-in-modal/generic-clock-in.resource.ts +107 -0
- package/src/generic-clock-in-modal/type/index.ts +45 -0
- package/src/generic-clock-in-modal/util/session-cache.ts +48 -0
- package/src/generic-dashboard/generic-dashboard-view.component.tsx +23 -0
- package/src/generic-dashboard/generic-dashboard-view.scss +5 -0
- package/src/generic-dashboard/generic-dashboard.scss +32 -0
- package/src/home-grid-dashboard/home-grid-dashboard.component.tsx +33 -0
- package/src/home-grid-dashboard/home-grid-dashboard.scss +56 -0
- package/src/index.ts +31 -0
- package/src/module-card/module-card-utils.component.tsx +175 -0
- package/src/module-card/module-card.component.tsx +48 -0
- package/src/module-card/module-card.scss +103 -0
- package/src/provider-banner/page-header.extension.tsx +86 -0
- package/src/provider-banner/page-header.scss +48 -0
- package/src/root.component.tsx +43 -0
- package/src/root.scss +15 -0
- package/src/routes.json +74 -0
- package/src/setup-tests.ts +1 -0
- package/src/side-nav/generic-side-nav.component.tsx +17 -0
- package/src/type/index.ts +5 -0
- package/src/welcome-back-banner/welcome-back-banner.component.tsx +112 -0
- package/src/welcome-back-banner/welcome-back-banner.resource.ts +29 -0
- package/src/welcome-back-banner/welcome-back-banner.scss +123 -0
- package/translations/am.json +33 -0
- package/translations/ar.json +33 -0
- package/translations/en.json +33 -0
- package/translations/es.json +33 -0
- package/translations/fr.json +33 -0
- package/translations/he.json +33 -0
- package/translations/km.json +33 -0
- package/translations/pt.json +33 -0
- package/translations/sw.json +33 -0
- package/translations/vi.json +33 -0
- package/translations/zh.json +33 -0
- package/tsconfig.json +5 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getAsyncLifecycle, defineConfigSchema, getSyncLifecycle } from '@openmrs/esm-framework';
|
|
2
|
+
import { configSchema } from './config-schema';
|
|
3
|
+
|
|
4
|
+
import ProviderHeader from './provider-banner/page-header.extension';
|
|
5
|
+
import { GenericClockIn } from './generic-clock-in-modal/generic-clock-in.modal';
|
|
6
|
+
import SideMenu from './side-nav/generic-side-nav.component';
|
|
7
|
+
|
|
8
|
+
const moduleName = '@digihmis/esm-home-app';
|
|
9
|
+
const pageName = 'home';
|
|
10
|
+
|
|
11
|
+
const options = {
|
|
12
|
+
featureName: pageName,
|
|
13
|
+
moduleName,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
17
|
+
|
|
18
|
+
export const root = getAsyncLifecycle(() => import('./root.component'), options);
|
|
19
|
+
export const providerBanner = getSyncLifecycle(ProviderHeader, options);
|
|
20
|
+
export const registrationNavMenu = getSyncLifecycle(SideMenu, options);
|
|
21
|
+
export const administrationNavMenu = getSyncLifecycle(SideMenu, options);
|
|
22
|
+
export const triageNavMenu = getSyncLifecycle(SideMenu, options);
|
|
23
|
+
export const outpatientNavMenu = getSyncLifecycle(SideMenu, options);
|
|
24
|
+
export const billingNavMenu = getSyncLifecycle(SideMenu, options);
|
|
25
|
+
|
|
26
|
+
// t('clockIn', 'Clock In')
|
|
27
|
+
export const genericClockInModal = getSyncLifecycle(GenericClockIn, options);
|
|
28
|
+
|
|
29
|
+
export function startupApp() {
|
|
30
|
+
defineConfigSchema(moduleName, configSchema);
|
|
31
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { useConfig } from '@openmrs/esm-framework';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { ConfigObject } from '../config-schema';
|
|
4
|
+
const openmrsSpaBase = window['getOpenmrsSpaBase']();
|
|
5
|
+
|
|
6
|
+
export const useModuleLinks = () => {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
const {
|
|
9
|
+
excludeLinks,
|
|
10
|
+
administrationIcon,
|
|
11
|
+
appointmentsIcon,
|
|
12
|
+
billingIcon,
|
|
13
|
+
dentalIcon,
|
|
14
|
+
inpatientIcon,
|
|
15
|
+
inventoryIcon,
|
|
16
|
+
laboratoryIcon,
|
|
17
|
+
mchIcon,
|
|
18
|
+
pharmacyIcon,
|
|
19
|
+
outpatientIcon,
|
|
20
|
+
proceduresIcon,
|
|
21
|
+
radiologyIcon,
|
|
22
|
+
triageIcon,
|
|
23
|
+
referralIcon,
|
|
24
|
+
registrationIcon,
|
|
25
|
+
specialClinicIcon,
|
|
26
|
+
staffManagementIcon,
|
|
27
|
+
reportsIcon,
|
|
28
|
+
facilityDashboardIcon,
|
|
29
|
+
legacyUIIcon,
|
|
30
|
+
} = useConfig<ConfigObject>();
|
|
31
|
+
const moduleLinks = [
|
|
32
|
+
{
|
|
33
|
+
label: t('registration', 'Registration'),
|
|
34
|
+
url: `${openmrsSpaBase}home/registration`,
|
|
35
|
+
imgSrc: registrationIcon.src,
|
|
36
|
+
imgAlt: registrationIcon.alt,
|
|
37
|
+
privilege: 'o3: View Registration Module',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: t('appointments', 'Appointments'),
|
|
41
|
+
url: `${openmrsSpaBase}home/appointments`,
|
|
42
|
+
imgSrc: appointmentsIcon.src,
|
|
43
|
+
imgAlt: appointmentsIcon.alt,
|
|
44
|
+
privilege: 'o3: View Appointments Module',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: t('triage', 'Triage'),
|
|
48
|
+
url: `${openmrsSpaBase}home/triage`,
|
|
49
|
+
imgSrc: triageIcon.src,
|
|
50
|
+
imgAlt: triageIcon.alt,
|
|
51
|
+
privilege: 'o3: View Triage Module',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: t('outpatient', 'Outpatient'),
|
|
55
|
+
url: `${openmrsSpaBase}home/outpatient`,
|
|
56
|
+
imgSrc: outpatientIcon.src,
|
|
57
|
+
imgAlt: outpatientIcon.alt,
|
|
58
|
+
privilege: 'o3: View Outpatient Module',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: t('dental', 'Dental'),
|
|
62
|
+
url: `${openmrsSpaBase}home/dental`,
|
|
63
|
+
imgSrc: dentalIcon.src,
|
|
64
|
+
imgAlt: dentalIcon.alt,
|
|
65
|
+
privilege: 'o3: View Dental Module',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: t('specialClinics', 'Special Clinics'),
|
|
69
|
+
url: `${openmrsSpaBase}home/special-clinics`,
|
|
70
|
+
imgSrc: specialClinicIcon.src,
|
|
71
|
+
imgAlt: specialClinicIcon.alt,
|
|
72
|
+
privilege: 'o3: View Special Clinics Module',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: t('mch', 'Maternal Child Health'),
|
|
76
|
+
url: `${openmrsSpaBase}home/mch`,
|
|
77
|
+
imgSrc: mchIcon.src,
|
|
78
|
+
imgAlt: mchIcon.alt,
|
|
79
|
+
privilege: 'o3: View Maternal Child Health Module',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
label: t('laboratory', 'Laboratory'),
|
|
83
|
+
url: `${openmrsSpaBase}home/laboratory`,
|
|
84
|
+
imgSrc: laboratoryIcon.src,
|
|
85
|
+
imgAlt: laboratoryIcon.alt,
|
|
86
|
+
privilege: 'o3: View Laboratory Module',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: t('procedures', 'Procedures'),
|
|
90
|
+
url: `${openmrsSpaBase}home/procedures`,
|
|
91
|
+
imgSrc: proceduresIcon.src,
|
|
92
|
+
imgAlt: proceduresIcon.alt,
|
|
93
|
+
privilege: 'o3: View Procedures Module',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
label: t('radiology', 'Radiology'),
|
|
97
|
+
url: `${openmrsSpaBase}home/radiology`,
|
|
98
|
+
imgSrc: radiologyIcon.src,
|
|
99
|
+
imgAlt: radiologyIcon.alt,
|
|
100
|
+
privilege: 'o3: View Radiology Module',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: t('inpatient', 'Inpatient'),
|
|
104
|
+
url: `${openmrsSpaBase}/home/inpatient`,
|
|
105
|
+
imgSrc: inpatientIcon.src,
|
|
106
|
+
imgAlt: inpatientIcon.alt,
|
|
107
|
+
privilege: 'o3: View Inpatient Module',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: t('referral', 'Referral'),
|
|
111
|
+
url: `${openmrsSpaBase}home/referral`,
|
|
112
|
+
imgSrc: referralIcon.src,
|
|
113
|
+
imgAlt: referralIcon.alt,
|
|
114
|
+
privilege: 'o3: View Referral Module',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: t('billing', 'Billing'),
|
|
118
|
+
url: `${openmrsSpaBase}home/billing`,
|
|
119
|
+
imgSrc: billingIcon.src,
|
|
120
|
+
imgAlt: billingIcon.alt,
|
|
121
|
+
privilege: 'o3: View Billing Module',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
label: t('pharmacy', 'Pharmacy'),
|
|
125
|
+
url: `${openmrsSpaBase}home/pharmacy`,
|
|
126
|
+
imgSrc: pharmacyIcon.src,
|
|
127
|
+
imgAlt: pharmacyIcon.alt,
|
|
128
|
+
privilege: 'o3: View Pharmacy Module',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
label: t('inventory', 'Inventory'),
|
|
132
|
+
url: `${openmrsSpaBase}home/inventory`,
|
|
133
|
+
imgSrc: inventoryIcon.src,
|
|
134
|
+
imgAlt: inventoryIcon.alt,
|
|
135
|
+
privilege: 'o3: View Inventory Module',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
label: t('reports', 'Reports'),
|
|
139
|
+
url: `${openmrsSpaBase}home/reports`,
|
|
140
|
+
imgSrc: reportsIcon.src,
|
|
141
|
+
imgAlt: reportsIcon.alt,
|
|
142
|
+
privilege: 'o3: View Reports Module',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
label: t('facilityAnalytics', 'Facility Analytics'),
|
|
146
|
+
url: `${openmrsSpaBase}home/facility-analytics`,
|
|
147
|
+
imgSrc: facilityDashboardIcon.src,
|
|
148
|
+
imgAlt: facilityDashboardIcon.alt,
|
|
149
|
+
privilege: 'o3: View Facility Analytics Module',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
label: t('administration', 'Administration'),
|
|
153
|
+
url: `${openmrsSpaBase}home/administration`,
|
|
154
|
+
imgSrc: administrationIcon.src,
|
|
155
|
+
imgAlt: administrationIcon.alt,
|
|
156
|
+
privilege: 'o3: View Administration Module',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
label: t('staffManagement', 'Staff Management'),
|
|
160
|
+
url: `${openmrsSpaBase}home/staff-management`,
|
|
161
|
+
imgSrc: staffManagementIcon.src,
|
|
162
|
+
imgAlt: staffManagementIcon.alt,
|
|
163
|
+
privilege: 'o3: View Staff Management Module',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
label: t('legacyUI', 'Legacy UI'),
|
|
167
|
+
url: `/openmrs/admin`,
|
|
168
|
+
imgSrc: legacyUIIcon.src,
|
|
169
|
+
imgAlt: legacyUIIcon.alt,
|
|
170
|
+
privilege: 'o3: View Legacy Admin Dashboard',
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
return moduleLinks.filter((link) => !excludeLinks.some((excludeLink) => excludeLink === link.label));
|
|
175
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { navigate } from '@openmrs/esm-framework';
|
|
3
|
+
import styles from './module-card.scss';
|
|
4
|
+
import { invalidateAllSessionData } from '../generic-clock-in-modal/util/session-cache';
|
|
5
|
+
|
|
6
|
+
type ModuleCardProps = {
|
|
7
|
+
imgSrc: string;
|
|
8
|
+
alt: string;
|
|
9
|
+
label: string;
|
|
10
|
+
url?: string;
|
|
11
|
+
onClick?: () => void;
|
|
12
|
+
privilege?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const ModuleCard: React.FC<ModuleCardProps> = ({ imgSrc, alt, label, url, onClick, privilege }) => {
|
|
16
|
+
const handleClick = async () => {
|
|
17
|
+
await invalidateAllSessionData();
|
|
18
|
+
|
|
19
|
+
if (onClick) {
|
|
20
|
+
onClick();
|
|
21
|
+
} else if (url) {
|
|
22
|
+
navigate({ to: url });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
27
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
handleClick();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
className={styles.moduleCard}
|
|
36
|
+
role="button"
|
|
37
|
+
tabIndex={0}
|
|
38
|
+
onClick={handleClick}
|
|
39
|
+
onKeyDown={handleKeyDown}
|
|
40
|
+
aria-label={`Navigate to ${label}`}>
|
|
41
|
+
<div className={styles.cardIcon}>{imgSrc}</div>
|
|
42
|
+
<img alt={alt} className={styles.logo} key={imgSrc} src={imgSrc} />
|
|
43
|
+
<div className={styles.cardLabel}>{label}</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default ModuleCard;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@carbon/colors';
|
|
4
|
+
|
|
5
|
+
.moduleCard {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
padding: layout.$spacing-05;
|
|
11
|
+
background-color: colors.$white;
|
|
12
|
+
border: 1px solid colors.$gray-20;
|
|
13
|
+
border-radius: layout.$spacing-03;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
aspect-ratio: 1;
|
|
16
|
+
min-height: 160px;
|
|
17
|
+
position: relative;
|
|
18
|
+
|
|
19
|
+
&:hover {
|
|
20
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:global(.omrs-breakpoint-lt-desktop) & {
|
|
24
|
+
padding: layout.$spacing-04;
|
|
25
|
+
min-height: 140px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
29
|
+
padding: layout.$spacing-04;
|
|
30
|
+
min-height: 120px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:global(.omrs-breakpoint-lt-phone) & {
|
|
34
|
+
aspect-ratio: auto;
|
|
35
|
+
padding: layout.$spacing-03;
|
|
36
|
+
min-height: 80px;
|
|
37
|
+
flex-direction: row;
|
|
38
|
+
justify-content: flex-start;
|
|
39
|
+
text-align: left;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.cardIcon {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.logo {
|
|
48
|
+
width: 100px;
|
|
49
|
+
height: 100px;
|
|
50
|
+
margin-bottom: layout.$spacing-03;
|
|
51
|
+
object-fit: contain;
|
|
52
|
+
filter: brightness(1.1) contrast(1.1);
|
|
53
|
+
|
|
54
|
+
:global(.omrs-breakpoint-lt-desktop) & {
|
|
55
|
+
width: 100px;
|
|
56
|
+
height: 100px;
|
|
57
|
+
margin-bottom: layout.$spacing-03;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
61
|
+
width: 118px;
|
|
62
|
+
height: 118px;
|
|
63
|
+
margin-bottom: layout.$spacing-02;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.moduleCard:hover & {
|
|
67
|
+
filter: brightness(1.2) contrast(1.2);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.cardLabel {
|
|
72
|
+
@include type.type-style('body-short-01');
|
|
73
|
+
color: colors.$gray-100;
|
|
74
|
+
margin-bottom: 12px;
|
|
75
|
+
text-align: center;
|
|
76
|
+
font-weight: 500;
|
|
77
|
+
line-height: 1.3;
|
|
78
|
+
word-wrap: break-word;
|
|
79
|
+
hyphens: auto;
|
|
80
|
+
|
|
81
|
+
:global(.omrs-breakpoint-lt-desktop) & {
|
|
82
|
+
@include type.type-style('body-compact-01');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
86
|
+
@include type.type-style('helper-text-01');
|
|
87
|
+
font-weight: 500;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:global(.omrs-breakpoint-lt-phone) & {
|
|
91
|
+
text-align: left;
|
|
92
|
+
flex: 1;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.moduleCard:hover & {
|
|
96
|
+
color: #2e6b8a;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.moduleCard:focus & {
|
|
101
|
+
color: colors.$blue-70;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { useSession } from '@openmrs/esm-framework';
|
|
4
|
+
import styles from './page-header.scss';
|
|
5
|
+
import { Calendar, RuleTest } from '@carbon/react/icons';
|
|
6
|
+
import { Tag } from '@carbon/react';
|
|
7
|
+
import { Attribute, useProviderAttributes } from '../welcome-back-banner/welcome-back-banner.resource';
|
|
8
|
+
|
|
9
|
+
const ProviderHeader: React.FC = () => {
|
|
10
|
+
const { t } = useTranslation();
|
|
11
|
+
const { currentProvider } = useSession();
|
|
12
|
+
const currentProviderUuid = currentProvider?.uuid;
|
|
13
|
+
const { providerAttributes, isLoading, error } = useProviderAttributes(currentProviderUuid);
|
|
14
|
+
|
|
15
|
+
const getAttributeValue = (attributes: Array<Attribute>, displayName: string): string => {
|
|
16
|
+
const attribute = attributes?.find((attr) => attr.attributeType.display === displayName);
|
|
17
|
+
return attribute?.value || '';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getLicenseStatus = (expiryDate: string) => {
|
|
21
|
+
if (!expiryDate || expiryDate === '0000-00-00') {
|
|
22
|
+
return { status: 'unknown', message: t('unlicensed', 'Unlicensed'), tagType: 'magenta' as const };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const today = new Date();
|
|
26
|
+
const expiry = new Date(expiryDate);
|
|
27
|
+
const timeDiff = expiry.getTime() - today.getTime();
|
|
28
|
+
const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
|
29
|
+
|
|
30
|
+
if (daysDiff < 0) {
|
|
31
|
+
return { status: 'expired', message: t('expired', 'Expired'), tagType: 'red' as const };
|
|
32
|
+
} else if (daysDiff <= 30) {
|
|
33
|
+
return { status: 'warning', message: t('expiring', 'Expiring'), tagType: 'blue' as const };
|
|
34
|
+
} else if (daysDiff <= 90) {
|
|
35
|
+
return { status: 'caution', message: t('caution', 'Caution'), tagType: 'teal' as const };
|
|
36
|
+
} else {
|
|
37
|
+
return { status: 'valid', message: t('valid', 'Valid'), tagType: 'green' as const };
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const formatDate = (dateString: string): string => {
|
|
42
|
+
if (!dateString || dateString === '0000-00-00') {
|
|
43
|
+
return '0000-00-00';
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const date = new Date(dateString);
|
|
47
|
+
if (isNaN(date.getTime())) {
|
|
48
|
+
return '0000-00-00';
|
|
49
|
+
}
|
|
50
|
+
return date.toISOString().split('T')[0];
|
|
51
|
+
} catch {
|
|
52
|
+
return '0000-00-00';
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const hwi = getAttributeValue(providerAttributes?.attributes, 'Practising License Number') || 'NONE';
|
|
57
|
+
const licenseExpiry = getAttributeValue(providerAttributes?.attributes, 'License Expiry Date');
|
|
58
|
+
const formattedExpiry = formatDate(licenseExpiry);
|
|
59
|
+
const licenseStatus = getLicenseStatus(licenseExpiry);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className={`omrs-main-content`}>
|
|
63
|
+
<div className={styles.rightSection}>
|
|
64
|
+
<div className={styles.medicalInfo}>
|
|
65
|
+
<div className={styles.licenseInfo}>
|
|
66
|
+
<RuleTest className={styles.infoIcon} />
|
|
67
|
+
{t('license', 'License')}: {hwi}
|
|
68
|
+
</div>
|
|
69
|
+
<div className={styles.expiryInfo}>
|
|
70
|
+
<Calendar className={styles.infoIcon} />
|
|
71
|
+
<span className={styles.expiryText}>
|
|
72
|
+
{t('expires', 'Expires')}: {formattedExpiry}
|
|
73
|
+
</span>
|
|
74
|
+
<span className={styles.statusBadge}>
|
|
75
|
+
<Tag size="md" type={licenseStatus.tagType}>
|
|
76
|
+
{licenseStatus.message}
|
|
77
|
+
</Tag>
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default ProviderHeader;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@carbon/colors';
|
|
4
|
+
|
|
5
|
+
.rightSection {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: flex-end;
|
|
9
|
+
gap: layout.$spacing-02;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.subtitle {
|
|
13
|
+
@include type.type-style('body-01');
|
|
14
|
+
color: colors.$gray-80;
|
|
15
|
+
margin: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.loginInfo,
|
|
19
|
+
.licenseInfo {
|
|
20
|
+
@include type.type-style('caption-01');
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: layout.$spacing-02;
|
|
24
|
+
color: colors.$gray-80;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.medicalInfo {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: layout.$spacing-04;
|
|
31
|
+
padding-right: 1rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.expiryInfo {
|
|
35
|
+
@include type.type-style('caption-01');
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: layout.$spacing-02;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.expiryText {
|
|
42
|
+
color: colors.$gray-80;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.infoIcon {
|
|
46
|
+
width: layout.$spacing-04;
|
|
47
|
+
height: layout.$spacing-04;
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
|
|
3
|
+
import GridDashboard from './home-grid-dashboard/home-grid-dashboard.component';
|
|
4
|
+
import RegistrationDashboard from './dashboards/registration-dashboard/registration-dashboard.component';
|
|
5
|
+
import TriageDashboard from './dashboards/triage-dashboard/triage-dashboard.component';
|
|
6
|
+
import AdministrationDashboard from './dashboards/administration-dashboard/administration-dashboard.component';
|
|
7
|
+
import OutpatientDashboard from './dashboards/outpatient-dashboard/outpatient-dashboard.component';
|
|
8
|
+
import BillingDashboard from './dashboards/billing-dashboard/billing-dashboard.component';
|
|
9
|
+
|
|
10
|
+
const Root: React.FC = () => {
|
|
11
|
+
const spaBasePath = window.spaBase;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<main className="omrs-main-content">
|
|
15
|
+
<BrowserRouter basename={spaBasePath}>
|
|
16
|
+
<Routes>
|
|
17
|
+
{/* homepage */}
|
|
18
|
+
<Route path="/home" element={<GridDashboard />} />
|
|
19
|
+
{/* registration */}
|
|
20
|
+
<Route path="/home/registration/*" element={<RegistrationDashboard />} />
|
|
21
|
+
{/* administration */}
|
|
22
|
+
<Route
|
|
23
|
+
path="/home/administration"
|
|
24
|
+
element={<Navigate to="/home/administration/system-info-management" replace />}
|
|
25
|
+
/>
|
|
26
|
+
<Route path="/home/administration/:dashboard" element={<AdministrationDashboard />} />
|
|
27
|
+
{/* triage */}
|
|
28
|
+
<Route path="/home/triage" element={<TriageDashboard />} />
|
|
29
|
+
<Route path="/home/triage/patient/:patientUuid/vitals" element={<TriageDashboard />} />
|
|
30
|
+
{/* outpatient */}
|
|
31
|
+
<Route path="/home/outpatient" element={<OutpatientDashboard />} />
|
|
32
|
+
<Route path="/home/outpatient/patient/:patientUuid/file" element={<OutpatientDashboard />} />
|
|
33
|
+
|
|
34
|
+
{/* billing */}
|
|
35
|
+
<Route path="/home/billing" element={<BillingDashboard />} />
|
|
36
|
+
<Route path="/home/billing/patient/:patientUuid/bill" element={<BillingDashboard />} />
|
|
37
|
+
</Routes>
|
|
38
|
+
</BrowserRouter>
|
|
39
|
+
</main>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default Root;
|
package/src/root.scss
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/spacing';
|
|
2
|
+
@use '@carbon/styles/scss/type';
|
|
3
|
+
|
|
4
|
+
.container {
|
|
5
|
+
padding: spacing.$spacing-07;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.welcome {
|
|
9
|
+
@include type.type-style('heading-04');
|
|
10
|
+
margin: spacing.$spacing-05 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.explainer {
|
|
14
|
+
margin-bottom: 2rem;
|
|
15
|
+
}
|
package/src/routes.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
|
+
"backendDependencies": {
|
|
4
|
+
"fhir2": ">=1.2",
|
|
5
|
+
"webservices.rest": "^2.24.0"
|
|
6
|
+
},
|
|
7
|
+
"extensions": [
|
|
8
|
+
{
|
|
9
|
+
"name": "registration-nav-menu",
|
|
10
|
+
"slot": "registration-sidebar-slot",
|
|
11
|
+
"component": "registrationNavMenu",
|
|
12
|
+
"online": true,
|
|
13
|
+
"offline": true
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "administration-nav-menu",
|
|
17
|
+
"slot": "administration-sidebar-slot",
|
|
18
|
+
"component": "administrationNavMenu",
|
|
19
|
+
"online": true,
|
|
20
|
+
"offline": true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "triage-nav-menu",
|
|
24
|
+
"slot": "triage-sidebar-slot",
|
|
25
|
+
"component": "triageNavMenu",
|
|
26
|
+
"online": true,
|
|
27
|
+
"offline": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "outpatient-nav-menu",
|
|
31
|
+
"slot": "outpatient-sidebar-slot",
|
|
32
|
+
"component": "outpatientNavMenu",
|
|
33
|
+
"online": true,
|
|
34
|
+
"offline": true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "billing-nav-menu",
|
|
38
|
+
"slot": "billing-sidebar-slot",
|
|
39
|
+
"component": "billingNavMenu",
|
|
40
|
+
"online": true,
|
|
41
|
+
"offline": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "patient-banner",
|
|
45
|
+
"slot": "patient-banner-slot",
|
|
46
|
+
"component": "patientBanner",
|
|
47
|
+
"online": true,
|
|
48
|
+
"offline": true
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "provider-banner",
|
|
52
|
+
"slot": "provider-banner-slot",
|
|
53
|
+
"component": "providerBanner",
|
|
54
|
+
"online": true,
|
|
55
|
+
"offline": true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
],
|
|
59
|
+
"modals": [
|
|
60
|
+
{
|
|
61
|
+
"name": "generic-clock-in-modal",
|
|
62
|
+
"component": "genericClockInModal"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"pages": [
|
|
66
|
+
{
|
|
67
|
+
"component": "root",
|
|
68
|
+
"route": "home",
|
|
69
|
+
"online": true,
|
|
70
|
+
"offline": true
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LeftNavMenu } from '@openmrs/esm-framework';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
const SideMenu = () => {
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<style>{`
|
|
8
|
+
.cds--side-nav {
|
|
9
|
+
background-color:var(--cds-field);
|
|
10
|
+
}
|
|
11
|
+
`}</style>
|
|
12
|
+
<LeftNavMenu isChildOfHeader={true} />
|
|
13
|
+
</>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default SideMenu;
|