@sasindi/radiologytestscheduling 0.0.0-stage → 1.0.2

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 (72) hide show
  1. package/LICENSE +354 -0
  2. package/README.md +166 -2
  3. package/dist/117.js +1 -0
  4. package/dist/117.js.map +1 -0
  5. package/dist/197.js +1 -0
  6. package/dist/197.js.map +1 -0
  7. package/dist/206.css +1 -0
  8. package/dist/206.css.map +1 -0
  9. package/dist/206.js +39 -0
  10. package/dist/206.js.map +1 -0
  11. package/dist/237.js +1 -0
  12. package/dist/237.js.map +1 -0
  13. package/dist/29.js +1 -0
  14. package/dist/29.js.map +1 -0
  15. package/dist/362.js +1 -0
  16. package/dist/362.js.map +1 -0
  17. package/dist/410.js +1 -0
  18. package/dist/410.js.map +1 -0
  19. package/dist/480.js +1 -0
  20. package/dist/480.js.map +1 -0
  21. package/dist/517.js +1 -0
  22. package/dist/517.js.map +1 -0
  23. package/dist/538.js +1 -0
  24. package/dist/538.js.map +1 -0
  25. package/dist/540.css +1 -0
  26. package/dist/540.css.map +1 -0
  27. package/dist/559.js +1 -0
  28. package/dist/559.js.map +1 -0
  29. package/dist/570.js +60 -0
  30. package/dist/570.js.map +1 -0
  31. package/dist/578.js +1 -0
  32. package/dist/578.js.map +1 -0
  33. package/dist/647.js +1 -0
  34. package/dist/647.js.map +1 -0
  35. package/dist/652.js +1 -0
  36. package/dist/652.js.map +1 -0
  37. package/dist/668.js +1 -0
  38. package/dist/668.js.map +1 -0
  39. package/dist/749.js +1 -0
  40. package/dist/749.js.map +1 -0
  41. package/dist/809.js +22 -0
  42. package/dist/809.js.map +1 -0
  43. package/dist/813.js +1 -0
  44. package/dist/813.js.map +1 -0
  45. package/dist/898.js +1 -0
  46. package/dist/898.js.map +1 -0
  47. package/dist/974.css +1 -0
  48. package/dist/974.css.map +1 -0
  49. package/dist/974.js +1 -0
  50. package/dist/974.js.map +1 -0
  51. package/dist/976.js +1 -0
  52. package/dist/976.js.map +1 -0
  53. package/dist/977.js +1 -0
  54. package/dist/977.js.map +1 -0
  55. package/dist/98.js +1 -0
  56. package/dist/98.js.map +1 -0
  57. package/dist/openmrs-esm-radiologytestscheduling.js +3 -0
  58. package/dist/openmrs-esm-radiologytestscheduling.js.map +1 -0
  59. package/dist/routes.json +1 -0
  60. package/package.json +101 -4
  61. package/src/config-schema.ts +21 -0
  62. package/src/constants.ts +1 -0
  63. package/src/declarations.d.ts +23 -0
  64. package/src/index.ts +28 -0
  65. package/src/radiology-test-scheduling.component.tsx +711 -0
  66. package/src/radiology-test-scheduling.resource.ts +339 -0
  67. package/src/radiology-test-scheduling.scss +396 -0
  68. package/src/root.component.test.tsx +12 -0
  69. package/src/root.component.tsx +13 -0
  70. package/src/root.scss +5 -0
  71. package/src/routes.json +12 -0
  72. package/src/types.ts +100 -0
@@ -0,0 +1,396 @@
1
+ /* Radiology Test Scheduling Unified Design System - OpenMRS 3 */
2
+
3
+ :root {
4
+ --primary-color: #009384;
5
+ --primary-hover: #007a6d;
6
+ --secondary-color: #00bfa5;
7
+ --bg-color: #f4f7f6;
8
+ --card-bg: #ffffff;
9
+ --text-dark: #212529;
10
+ --text-light: #6c757d;
11
+ --border-color: #dee2e6;
12
+ --danger-color: #da1e28;
13
+ --urgent-row-bg: #f5a4a4;
14
+ --success-color: #198038;
15
+ --info-color: #0f62fe;
16
+ --warning-color: #f1c21b;
17
+ --selected-bg: #d0e7ff;
18
+ }
19
+
20
+ .bodyWrapper {
21
+ background-color: var(--bg-color);
22
+ padding: 1.5rem;
23
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
24
+ color: var(--text-dark);
25
+ min-height: 100vh;
26
+ }
27
+
28
+ .card {
29
+ background: var(--card-bg);
30
+ border-radius: 8px;
31
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
32
+ padding: 1.5rem;
33
+ margin-bottom: 2rem;
34
+ border: 1px solid var(--border-color);
35
+ }
36
+
37
+ .cardHeader {
38
+ display: flex;
39
+ justify-content: space-between;
40
+ align-items: center;
41
+ margin-bottom: 1.25rem;
42
+ padding-bottom: 0.75rem;
43
+ border-bottom: 2px solid var(--primary-color);
44
+ }
45
+
46
+ .formHeading {
47
+ font-size: 1.4rem;
48
+ font-weight: 700;
49
+ color: var(--primary-color);
50
+ margin: 0;
51
+ letter-spacing: -0.2px;
52
+ }
53
+
54
+ .serviceBox {
55
+ background: #f5f6f9;
56
+ border: 1px solid var(--border-color);
57
+ border-radius: 6px;
58
+ padding: 1.25rem;
59
+ }
60
+
61
+ .row {
62
+ display: flex;
63
+ flex-wrap: wrap;
64
+ margin-right: -0.75rem;
65
+ margin-left: -0.75rem;
66
+ }
67
+
68
+ .col12 { flex: 0 0 100%; max-width: 100%; padding: 0 0.75rem; box-sizing: border-box; }
69
+ .col10 { flex: 0 0 83.333333%; max-width: 83.333333%; padding: 0 0.75rem; box-sizing: border-box; }
70
+ .col8 { flex: 0 0 66.666667%; max-width: 66.666667%; padding: 0 0.75rem; box-sizing: border-box; }
71
+ .col6 { flex: 0 0 50%; max-width: 50%; padding: 0 0.75rem; box-sizing: border-box; }
72
+ .col4 { flex: 0 0 33.333333%; max-width: 33.333333%; padding: 0 0.75rem; box-sizing: border-box; }
73
+ .col3 { flex: 0 0 25%; max-width: 25%; padding: 0 0.75rem; box-sizing: border-box; }
74
+ .col2 { flex: 0 0 16.666667%; max-width: 16.666667%; padding: 0 0.75rem; box-sizing: border-box; }
75
+
76
+ .formGroup {
77
+ margin-bottom: 1rem;
78
+ }
79
+
80
+ .label {
81
+ display: block;
82
+ margin-bottom: 0.4rem;
83
+ font-weight: 600;
84
+ font-size: 0.88rem;
85
+ color: #004080;
86
+ }
87
+
88
+ .formControl {
89
+ display: block;
90
+ width: 100%;
91
+ height: 2.4rem;
92
+ padding: 0.4rem 0.75rem;
93
+ font-size: 0.9rem;
94
+ font-weight: 400;
95
+ line-height: 1.5;
96
+ color: #495057;
97
+ background-color: #fff;
98
+ border: 1px solid #ced4da;
99
+ border-radius: 4px;
100
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
101
+ box-sizing: border-box;
102
+
103
+ &:focus {
104
+ color: #495057;
105
+ background-color: #fff;
106
+ border-color: var(--primary-color);
107
+ outline: 0;
108
+ box-shadow: 0 0 0 0.2rem rgba(0, 147, 132, 0.2);
109
+ }
110
+ }
111
+
112
+ textarea.formControl {
113
+ height: auto;
114
+ min-height: 70px;
115
+ }
116
+
117
+ /* Filter controls row */
118
+ .filterRow {
119
+ display: flex;
120
+ align-items: center;
121
+ flex-wrap: wrap;
122
+ gap: 1rem;
123
+ margin-bottom: 1.25rem;
124
+ }
125
+
126
+ .instituteSelectGroup {
127
+ display: flex;
128
+ align-items: center;
129
+ gap: 0.75rem;
130
+ }
131
+
132
+ .instituteLabel {
133
+ font-size: 1.1rem;
134
+ font-weight: 700;
135
+ color: #333;
136
+ margin: 0;
137
+ white-space: nowrap;
138
+ }
139
+
140
+ .searchGroup {
141
+ flex: 1;
142
+ min-width: 260px;
143
+ }
144
+
145
+ /* Table styling matching team's DataTable style */
146
+ .tableContainer {
147
+ width: 100%;
148
+ overflow-x: auto;
149
+ margin-top: 0.5rem;
150
+ margin-bottom: 1.75rem;
151
+ border: 1px solid var(--border-color);
152
+ border-radius: 4px;
153
+ background: #fff;
154
+ }
155
+
156
+ .table {
157
+ width: 100%;
158
+ border-collapse: collapse;
159
+ white-space: nowrap;
160
+ font-size: 0.85rem;
161
+
162
+ thead th {
163
+ background-color: #f1f3f5;
164
+ color: #212529;
165
+ font-weight: 600;
166
+ padding: 0.65rem 0.75rem;
167
+ text-align: left;
168
+ border-bottom: 2px solid var(--border-color);
169
+ border-right: 1px solid #dee2e6;
170
+
171
+ &:last-child {
172
+ border-right: none;
173
+ }
174
+ }
175
+
176
+ tbody td {
177
+ padding: 0.55rem 0.75rem;
178
+ border-bottom: 1px solid #e9ecef;
179
+ border-right: 1px solid #f1f3f5;
180
+ color: #333;
181
+ vertical-align: middle;
182
+
183
+ &:last-child {
184
+ border-right: none;
185
+ }
186
+ }
187
+
188
+ tbody tr {
189
+ transition: background-color 0.15s ease;
190
+
191
+ &:hover {
192
+ background-color: #f8f9fa;
193
+ }
194
+ }
195
+ }
196
+
197
+ /* Urgent rows are highlighted in #f5a4a4 just like team's code */
198
+ .urgentRow {
199
+ background-color: #f5a4a4 !important;
200
+
201
+ &:hover {
202
+ background-color: #f28b8b !important;
203
+ }
204
+ }
205
+
206
+ .selectedRow {
207
+ background-color: var(--selected-bg) !important;
208
+ font-weight: 600;
209
+ outline: 2px solid #0f62fe;
210
+ }
211
+
212
+ .badge {
213
+ display: inline-block;
214
+ padding: 0.2rem 0.5rem;
215
+ font-size: 0.72rem;
216
+ font-weight: 700;
217
+ line-height: 1;
218
+ text-align: center;
219
+ white-space: nowrap;
220
+ vertical-align: baseline;
221
+ border-radius: 4px;
222
+ }
223
+
224
+ .badgeUrgent {
225
+ color: #fff;
226
+ background-color: #c62828;
227
+ }
228
+
229
+ .badgeRoutine {
230
+ color: #212529;
231
+ background-color: #e9ecef;
232
+ }
233
+
234
+ .badgeType {
235
+ color: #fff;
236
+ background-color: var(--primary-color);
237
+ }
238
+
239
+ .btnSelect {
240
+ background-color: #ffc107;
241
+ color: #212529;
242
+ border: 1px solid #d39e00;
243
+ border-radius: 4px;
244
+ padding: 0.2rem 0.65rem;
245
+ font-size: 0.8rem;
246
+ font-weight: 600;
247
+ cursor: pointer;
248
+ transition: background-color 0.15s;
249
+
250
+ &:hover {
251
+ background-color: #e0a800;
252
+ }
253
+ }
254
+
255
+ .btnSelected {
256
+ background-color: #0f62fe !important;
257
+ color: #ffffff !important;
258
+ border-color: #0353e9 !important;
259
+ }
260
+
261
+ /* Demographics Panel */
262
+ .demographicsSection {
263
+ margin-top: 1rem;
264
+ }
265
+
266
+ .fieldItem {
267
+ margin-bottom: 0.55rem;
268
+ font-size: 0.88rem;
269
+ display: flex;
270
+ align-items: baseline;
271
+ color: #333;
272
+ }
273
+
274
+ .fieldIcon {
275
+ color: #009384;
276
+ margin-right: 0.4rem;
277
+ font-weight: bold;
278
+ }
279
+
280
+ .fieldTitle {
281
+ font-weight: 600;
282
+ color: #004080;
283
+ margin-right: 0.35rem;
284
+ white-space: nowrap;
285
+ }
286
+
287
+ .fieldVal {
288
+ color: #111;
289
+ font-weight: 500;
290
+ word-break: break-word;
291
+ }
292
+
293
+ /* Patient Image (upload-img matching team's style) */
294
+ .uploadImgWrapper {
295
+ display: flex;
296
+ justify-content: center;
297
+ align-items: center;
298
+ padding: 0.5rem;
299
+ }
300
+
301
+ .uploadImg {
302
+ border-radius: 16px;
303
+ display: inline-block;
304
+ height: 100px;
305
+ width: 100px;
306
+ object-fit: cover;
307
+ object-position: 50% 50%;
308
+ border: 3px solid #20e5bf;
309
+ margin-right: 15px;
310
+ vertical-align: middle;
311
+ background-color: #e9ecef;
312
+ }
313
+
314
+ /* Investigation Details Row */
315
+ .investigationRow {
316
+ border-bottom: 2px solid #808080;
317
+ padding-bottom: 0.75rem;
318
+ margin-top: 0.5rem;
319
+ margin-bottom: 1.25rem;
320
+ }
321
+
322
+ /* Scheduling Inputs & Action Buttons */
323
+ .schedulingFormArea {
324
+ margin-top: 0.5rem;
325
+ }
326
+
327
+ .buttonGroup {
328
+ display: flex;
329
+ gap: 1rem;
330
+ margin-top: 1rem;
331
+ }
332
+
333
+ /* Big blue button as explicitly requested by user */
334
+ .btnPrimary {
335
+ background-color: #0f62fe;
336
+ color: #ffffff;
337
+ border: none;
338
+ border-radius: 4px;
339
+ padding: 0.7rem 1.8rem;
340
+ font-size: 1rem;
341
+ font-weight: 700;
342
+ cursor: pointer;
343
+ display: inline-flex;
344
+ align-items: center;
345
+ justify-content: center;
346
+ gap: 0.5rem;
347
+ box-shadow: 0 2px 6px rgba(15, 98, 254, 0.25);
348
+ transition: background-color 0.2s, box-shadow 0.2s;
349
+
350
+ &:hover:not(:disabled) {
351
+ background-color: #0353e9;
352
+ box-shadow: 0 4px 10px rgba(15, 98, 254, 0.4);
353
+ }
354
+
355
+ &:disabled {
356
+ background-color: #8d8d8d;
357
+ cursor: not-allowed;
358
+ opacity: 0.7;
359
+ }
360
+ }
361
+
362
+ .btnCancel {
363
+ background-color: #dc3545;
364
+ color: #ffffff;
365
+ border: none;
366
+ border-radius: 4px;
367
+ padding: 0.7rem 1.5rem;
368
+ font-size: 0.95rem;
369
+ font-weight: 600;
370
+ cursor: pointer;
371
+ transition: background-color 0.2s;
372
+
373
+ &:hover {
374
+ background-color: #c82333;
375
+ }
376
+ }
377
+
378
+ .emptyState {
379
+ text-align: center;
380
+ padding: 2.5rem 1rem;
381
+ color: var(--text-light);
382
+ font-style: italic;
383
+ font-size: 0.95rem;
384
+ }
385
+
386
+ @media (max-width: 992px) {
387
+ .col10, .col8, .col6, .col4, .col3, .col2 {
388
+ flex: 0 0 100%;
389
+ max-width: 100%;
390
+ }
391
+
392
+ .uploadImgWrapper {
393
+ justify-content: flex-start;
394
+ margin-bottom: 1rem;
395
+ }
396
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { render, screen } from '@testing-library/react';
4
+ import Root from './root.component';
5
+
6
+ describe('Root', () => {
7
+ it('mounts the route shell without crashing', () => {
8
+ // No route matches the default jsdom URL, so the shell mounts but renders
9
+ // no route content; this asserts the wiring (BrowserRouter + Routes) works.
10
+ expect(() => render(<Root />)).not.toThrow();
11
+ });
12
+ });
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
3
+ import RadiologyTestScheduling from './radiology-test-scheduling.component';
4
+
5
+ const Root: React.FC = () => (
6
+ <BrowserRouter basename={window.getOpenmrsSpaBase()}>
7
+ <Routes>
8
+ <Route path="radiologytestscheduling" element={<RadiologyTestScheduling />} />
9
+ </Routes>
10
+ </BrowserRouter>
11
+ );
12
+
13
+ export default Root;
package/src/root.scss ADDED
@@ -0,0 +1,5 @@
1
+ @use '@carbon/layout';
2
+
3
+ .container {
4
+ padding: layout.$spacing-05;
5
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json.openmrs.org/routes.schema.json",
3
+ "pages": [
4
+ {
5
+ "route": "radiologytestscheduling",
6
+ "component": "root",
7
+ "online": true
8
+ }
9
+ ],
10
+ "extensions": [
11
+ ]
12
+ }
package/src/types.ts ADDED
@@ -0,0 +1,100 @@
1
+ export interface RadiologyOrder {
2
+ id: number;
3
+ patientId?: string;
4
+ patientPhn: string;
5
+ patientSystem?: string;
6
+ encounter?: string;
7
+ location: string;
8
+ section?: string;
9
+ schedulingreq?: string;
10
+ approvalreq?: string;
11
+ identifier?: string;
12
+ basedOn?: string;
13
+ replaces?: string;
14
+ requisition?: string;
15
+ status?: string;
16
+ intent?: string;
17
+ priority?: string;
18
+ doNotPerform?: string;
19
+ categoryCode?: string;
20
+ categoryDisplay?: string; // 'Xray' | 'CT' | 'MRI' | etc.
21
+ codeCode?: string;
22
+ codeDisplay?: string; // Test Name
23
+ quantity?: string;
24
+ quantityRatio?: string;
25
+ quantityRange?: string;
26
+ focusCode?: string;
27
+ focusDisplay?: string;
28
+ occurrenceDateTime?: string;
29
+ occurrencePeriod?: string;
30
+ occurrenceTiming?: string;
31
+ asNeededBoolean?: string;
32
+ authoredOn?: string;
33
+ requesterReference?: string;
34
+ requesterDisplay?: string;
35
+ performerTypeCode?: string;
36
+ performerTypeDisplay?: string;
37
+ performerReference?: string;
38
+ performerDisplay?: string;
39
+ reason?: string;
40
+ supportingInfo?: string; // Exam Room
41
+ specimen?: string; // Localization
42
+ bodySiteCode?: string;
43
+ bodySiteDisplay?: string; // Position
44
+ bodySiteText?: string; // Region
45
+ bodyStructureId?: string;
46
+ bodyStructureIdentifier?: string;
47
+ bodyStructureCode?: string;
48
+ bodyStructureDisplay?: string;
49
+ bodyStructureText?: string;
50
+ bodyStructureDescription?: string;
51
+ note?: string; // Comment
52
+ scheduledDateTime?: string;
53
+ remarks?: string;
54
+ noty?: string;
55
+ cby?: string;
56
+ cdate?: string;
57
+ }
58
+
59
+ export interface SchedulingOrderDTO {
60
+ radiology: RadiologyOrder;
61
+ type: 'OPD' | 'Clinic' | 'Ward';
62
+ patientName?: string;
63
+ patientGender?: string;
64
+ patientAge?: string | number;
65
+ locationName?: string;
66
+ orderedByName?: string;
67
+ }
68
+
69
+ export interface PatientDemographics {
70
+ fullName: string;
71
+ gender: string;
72
+ nicNo: string;
73
+ guardianInfo: string;
74
+ address: string;
75
+ years: string | number;
76
+ months: string | number;
77
+ days: string | number;
78
+ dateOfBirth: string;
79
+ profileImage: string;
80
+ telephoneHome: string;
81
+ telephoneMobile: string;
82
+ }
83
+
84
+ export interface ExamRoom {
85
+ id?: number | string;
86
+ name: string;
87
+ }
88
+
89
+ export interface InstituteOption {
90
+ uuid: string;
91
+ name: string;
92
+ }
93
+
94
+ export interface CalendarEvent {
95
+ title: string;
96
+ date: string; // YYYY-MM-DD
97
+ category: string;
98
+ count: number;
99
+ breakdown: Record<string, number>;
100
+ }