@sasindi/samplecollection 1.0.1 → 1.0.3
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/dist/30.js +1 -1
- package/dist/30.js.map +1 -1
- package/dist/425.js +1 -1
- package/dist/425.js.map +1 -1
- package/dist/529.js +1 -1
- package/dist/577.js +1 -1
- package/dist/577.js.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-samplecollection.js +3 -3
- package/dist/openmrs-esm-samplecollection.js.buildmanifest.json +48 -48
- package/dist/openmrs-esm-samplecollection.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/routes.json +1 -1
- package/src/samplecollection.component.tsx +167 -150
- package/src/samplecollection.scss +235 -101
- package/src/samplecollectionroom.component.tsx +233 -217
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import {
|
|
4
|
-
Tile,
|
|
5
|
-
Select,
|
|
6
|
-
SelectItem,
|
|
7
|
-
TextInput,
|
|
8
|
-
Table,
|
|
9
|
-
TableHead,
|
|
10
|
-
TableRow,
|
|
11
|
-
TableHeader,
|
|
12
|
-
TableBody,
|
|
13
|
-
TableCell,
|
|
14
|
-
Button,
|
|
15
|
-
Pagination,
|
|
16
|
-
Loading
|
|
17
|
-
} from '@carbon/react';
|
|
18
3
|
import styles from './samplecollection.scss';
|
|
19
4
|
|
|
20
|
-
const SearchIcon = () => (
|
|
21
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
|
22
|
-
<path d="M15 14.3l-3.3-3.3c.9-1.1 1.4-2.5 1.4-4 0-3.3-2.7-6-6-6S1.1 3.7 1.1 7s2.7 6 6 6c1.5 0 2.9-.5 4-1.4l3.3 3.3.6-.6zM7.1 12c-2.8 0-5-2.2-5-5s2.2-5 5-5 5 2.2 5 5-2.2 5-5 5z" />
|
|
23
|
-
</svg>
|
|
24
|
-
);
|
|
25
|
-
|
|
26
5
|
const navigateTo = (url: string) => {
|
|
27
6
|
window.history.pushState({}, '', url);
|
|
28
7
|
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
@@ -42,8 +21,7 @@ const Samplecollection: React.FC = () => {
|
|
|
42
21
|
|
|
43
22
|
// Pagination state
|
|
44
23
|
const [page, setPage] = useState<number>(1);
|
|
45
|
-
const [pageSize
|
|
46
|
-
const [totalRecords, setTotalRecords] = useState<number>(0);
|
|
24
|
+
const [pageSize] = useState<number>(50);
|
|
47
25
|
const [filteredRecords, setFilteredRecords] = useState<number>(0);
|
|
48
26
|
|
|
49
27
|
// Load Session and Location context
|
|
@@ -64,8 +42,11 @@ const Samplecollection: React.FC = () => {
|
|
|
64
42
|
personData.attributes.forEach((attr: any) => {
|
|
65
43
|
if (attr.display) {
|
|
66
44
|
const [key, value] = attr.display.split('=');
|
|
67
|
-
if (key && value
|
|
68
|
-
|
|
45
|
+
if (key && value) {
|
|
46
|
+
const cleanKey = key.trim();
|
|
47
|
+
if (cleanKey === 'Institute Id' || cleanKey === 'Institute') {
|
|
48
|
+
locUuid = value.trim();
|
|
49
|
+
}
|
|
69
50
|
}
|
|
70
51
|
}
|
|
71
52
|
});
|
|
@@ -119,7 +100,6 @@ const Samplecollection: React.FC = () => {
|
|
|
119
100
|
const resData = await response.json();
|
|
120
101
|
|
|
121
102
|
setData(resData.data || []);
|
|
122
|
-
setTotalRecords(resData.recordsTotal || 0);
|
|
123
103
|
setFilteredRecords(resData.recordsFiltered || 0);
|
|
124
104
|
} catch (err) {
|
|
125
105
|
console.error('Error fetching table data:', err);
|
|
@@ -128,7 +108,6 @@ const Samplecollection: React.FC = () => {
|
|
|
128
108
|
}
|
|
129
109
|
}
|
|
130
110
|
|
|
131
|
-
// Debounce data fetching slightly if search query is changing rapidly
|
|
132
111
|
const delayDebounceFn = setTimeout(() => {
|
|
133
112
|
fetchTableData();
|
|
134
113
|
}, 300);
|
|
@@ -143,139 +122,177 @@ const Samplecollection: React.FC = () => {
|
|
|
143
122
|
navigateTo('/openmrs/spa/samplecollection/room');
|
|
144
123
|
};
|
|
145
124
|
|
|
125
|
+
const startIndex = (page - 1) * pageSize;
|
|
126
|
+
|
|
146
127
|
return (
|
|
147
128
|
<div className={styles.container}>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<div className={styles.
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
</h1>
|
|
154
|
-
<div className={styles.locationBadge}>
|
|
155
|
-
<span className="icon-hospital" />
|
|
156
|
-
<span>{locationName}</span>
|
|
157
|
-
</div>
|
|
129
|
+
{/* Clinic/Hospital Header */}
|
|
130
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '10px' }}>
|
|
131
|
+
<div className={styles.locationBadge} style={{ background: '#e9ecef', color: '#495057' }}>
|
|
132
|
+
<span className="icon-hospital" />
|
|
133
|
+
<span id="lblInstituteName" style={{ fontWeight: 'bold' }}>{locationName}</span>
|
|
158
134
|
</div>
|
|
135
|
+
</div>
|
|
159
136
|
|
|
160
|
-
|
|
161
|
-
<div className={styles.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}}
|
|
170
|
-
style={{ width: '200px' }}
|
|
171
|
-
>
|
|
172
|
-
<SelectItem value="ALL" text={t('allSections', 'All')} />
|
|
173
|
-
<SelectItem value="OPD" text="OPD" />
|
|
174
|
-
<SelectItem value="Clinic" text="Clinic" />
|
|
175
|
-
{wardSections.map((sect) => (
|
|
176
|
-
<SelectItem key={sect} value={sect} text={sect} />
|
|
177
|
-
))}
|
|
178
|
-
</Select>
|
|
137
|
+
<div className={styles.card}>
|
|
138
|
+
<div className={styles.cardBody}>
|
|
139
|
+
|
|
140
|
+
{/* Section title */}
|
|
141
|
+
<div className={styles.col12} style={{ marginBottom: '1rem', padding: 0 }}>
|
|
142
|
+
<h2 className={styles.heading} style={{ margin: 0 }}>
|
|
143
|
+
{t('samplecollectionHeading', 'Sample Collection List')}
|
|
144
|
+
</h2>
|
|
145
|
+
</div>
|
|
179
146
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
147
|
+
{/* Filters & Search Row */}
|
|
148
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem', flexWrap: 'wrap', gap: '10px' }}>
|
|
149
|
+
<div className={`${styles.dFlex} ${styles.alignItemsCenter}`}>
|
|
150
|
+
<label htmlFor="sectionFilter" className={styles.labelCompact} style={{ fontWeight: 'bold', width: 'auto', marginRight: '8px' }}>
|
|
151
|
+
Section:
|
|
152
|
+
</label>
|
|
153
|
+
<select
|
|
154
|
+
id="sectionFilter"
|
|
155
|
+
className={styles.formControl}
|
|
156
|
+
style={{ width: 'auto', minWidth: '160px', height: '30px' }}
|
|
157
|
+
value={sectionFilter}
|
|
158
|
+
onChange={(e) => {
|
|
159
|
+
setSectionFilter(e.target.value);
|
|
160
|
+
setPage(1);
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
<option value="ALL">All</option>
|
|
164
|
+
<option value="OPD">OPD</option>
|
|
165
|
+
<option value="Clinic">Clinic</option>
|
|
166
|
+
{wardSections.map((sect) => (
|
|
167
|
+
<option key={sect} value={sect}>{sect}</option>
|
|
168
|
+
))}
|
|
169
|
+
</select>
|
|
170
|
+
</div>
|
|
191
171
|
|
|
192
|
-
|
|
193
|
-
|
|
172
|
+
<div className={`${styles.dFlex} ${styles.alignItemsCenter}`}>
|
|
173
|
+
<label htmlFor="searchQuery" className={styles.labelCompact} style={{ fontWeight: 'bold', width: 'auto', marginRight: '8px' }}>
|
|
174
|
+
Search:
|
|
175
|
+
</label>
|
|
176
|
+
<input
|
|
177
|
+
id="searchQuery"
|
|
178
|
+
type="text"
|
|
179
|
+
className={styles.formControl}
|
|
180
|
+
placeholder="Type search query..."
|
|
181
|
+
style={{ width: '220px', height: '30px' }}
|
|
182
|
+
value={searchQuery}
|
|
183
|
+
onChange={(e) => {
|
|
184
|
+
setSearchQuery(e.target.value);
|
|
185
|
+
setPage(1);
|
|
186
|
+
}}
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
194
190
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
191
|
+
{/* Table Container */}
|
|
192
|
+
<div className={styles.tableResponsive}>
|
|
193
|
+
<table className={`${styles.table} ${styles.tableBordered} ${styles.tableHover} ${styles.tableStriped}`}>
|
|
194
|
+
<thead>
|
|
195
|
+
<tr>
|
|
196
|
+
<th style={{ width: '50px' }}>No</th>
|
|
197
|
+
<th style={{ whiteSpace: 'nowrap' }}>Section</th>
|
|
198
|
+
<th style={{ whiteSpace: 'nowrap' }}>PHN No</th>
|
|
199
|
+
<th style={{ whiteSpace: 'nowrap' }}>Name</th>
|
|
200
|
+
<th style={{ whiteSpace: 'nowrap' }}>Gender</th>
|
|
201
|
+
<th style={{ whiteSpace: 'nowrap' }}>Age</th>
|
|
202
|
+
<th style={{ whiteSpace: 'nowrap' }}>Test Name</th>
|
|
203
|
+
<th style={{ width: '80px' }}></th>
|
|
204
|
+
</tr>
|
|
205
|
+
</thead>
|
|
206
|
+
<tbody>
|
|
207
|
+
{loading && data.length === 0 ? (
|
|
208
|
+
<tr>
|
|
209
|
+
<td colSpan={8} style={{ textAlign: 'center', padding: '20px' }}>
|
|
210
|
+
Loading data...
|
|
211
|
+
</td>
|
|
212
|
+
</tr>
|
|
213
|
+
) : data.length === 0 ? (
|
|
214
|
+
<tr>
|
|
215
|
+
<td colSpan={8} style={{ textAlign: 'center', padding: '20px' }}>
|
|
216
|
+
No sample collections found.
|
|
217
|
+
</td>
|
|
218
|
+
</tr>
|
|
219
|
+
) : (
|
|
220
|
+
data.map((row, index) => {
|
|
221
|
+
const phnNo = row[0];
|
|
222
|
+
const patientName = row[1];
|
|
223
|
+
const testName = row[2];
|
|
224
|
+
const gender = row[3];
|
|
225
|
+
const age = row[4];
|
|
226
|
+
const section = row[7];
|
|
227
|
+
const specimenStatus = row[8];
|
|
228
|
+
const progress = row[9];
|
|
229
|
+
const rowIndex = startIndex + index + 1;
|
|
228
230
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
let rowClass = '';
|
|
232
|
+
if (specimenStatus === 'Inadequate') {
|
|
233
|
+
rowClass = styles.rowInadequate;
|
|
234
|
+
} else if (specimenStatus === 'Error') {
|
|
235
|
+
rowClass = styles.rowError;
|
|
236
|
+
}
|
|
235
237
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
238
|
+
return (
|
|
239
|
+
<tr key={`${phnNo}-${section}-${index}`} className={rowClass}>
|
|
240
|
+
<td>{rowIndex}</td>
|
|
241
|
+
<td>{section}</td>
|
|
242
|
+
<td>{phnNo}</td>
|
|
243
|
+
<td>{patientName}</td>
|
|
244
|
+
<td>{gender}</td>
|
|
245
|
+
<td>{age}</td>
|
|
246
|
+
<td>{testName}</td>
|
|
247
|
+
<td style={{ textAlign: 'center' }}>
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
className={`${styles.btn} ${styles.btnPrimary}`}
|
|
251
|
+
style={{ padding: '2px 8px', fontSize: '12px' }}
|
|
252
|
+
onClick={() => handleSelectPatient(phnNo, section, progress)}
|
|
253
|
+
>
|
|
254
|
+
Select
|
|
255
|
+
</button>
|
|
256
|
+
</td>
|
|
257
|
+
</tr>
|
|
258
|
+
);
|
|
259
|
+
})
|
|
260
|
+
)}
|
|
261
|
+
</tbody>
|
|
262
|
+
</table>
|
|
263
|
+
</div>
|
|
261
264
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
265
|
+
{/* Simple Bootstrap-like Pagination */}
|
|
266
|
+
{filteredRecords > 0 && (
|
|
267
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: '1rem', flexWrap: 'wrap' }}>
|
|
268
|
+
<div style={{ fontSize: '14px', color: '#6c757d' }}>
|
|
269
|
+
Showing {startIndex + 1} to {Math.min(startIndex + pageSize, filteredRecords)} of {filteredRecords} entries
|
|
270
|
+
</div>
|
|
271
|
+
<div style={{ display: 'flex', gap: '5px' }}>
|
|
272
|
+
<button
|
|
273
|
+
type="button"
|
|
274
|
+
className={`${styles.btn} ${styles.btnPrimary}`}
|
|
275
|
+
style={{ padding: '4px 12px' }}
|
|
276
|
+
disabled={page === 1}
|
|
277
|
+
onClick={() => setPage((p) => p - 1)}
|
|
278
|
+
>
|
|
279
|
+
Previous
|
|
280
|
+
</button>
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
className={`${styles.btn} ${styles.btnPrimary}`}
|
|
284
|
+
style={{ padding: '4px 12px' }}
|
|
285
|
+
disabled={page * pageSize >= filteredRecords}
|
|
286
|
+
onClick={() => setPage((p) => p + 1)}
|
|
287
|
+
>
|
|
288
|
+
Next
|
|
289
|
+
</button>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
)}
|
|
293
|
+
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
279
296
|
</div>
|
|
280
297
|
);
|
|
281
298
|
};
|