@sasindi/samplecollection 1.0.1 → 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.
- 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 +162 -148
- package/src/samplecollection.scss +235 -101
- package/src/samplecollectionroom.component.tsx +228 -215
|
@@ -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
|
|
@@ -119,7 +97,6 @@ const Samplecollection: React.FC = () => {
|
|
|
119
97
|
const resData = await response.json();
|
|
120
98
|
|
|
121
99
|
setData(resData.data || []);
|
|
122
|
-
setTotalRecords(resData.recordsTotal || 0);
|
|
123
100
|
setFilteredRecords(resData.recordsFiltered || 0);
|
|
124
101
|
} catch (err) {
|
|
125
102
|
console.error('Error fetching table data:', err);
|
|
@@ -128,7 +105,6 @@ const Samplecollection: React.FC = () => {
|
|
|
128
105
|
}
|
|
129
106
|
}
|
|
130
107
|
|
|
131
|
-
// Debounce data fetching slightly if search query is changing rapidly
|
|
132
108
|
const delayDebounceFn = setTimeout(() => {
|
|
133
109
|
fetchTableData();
|
|
134
110
|
}, 300);
|
|
@@ -143,139 +119,177 @@ const Samplecollection: React.FC = () => {
|
|
|
143
119
|
navigateTo('/openmrs/spa/samplecollection/room');
|
|
144
120
|
};
|
|
145
121
|
|
|
122
|
+
const startIndex = (page - 1) * pageSize;
|
|
123
|
+
|
|
146
124
|
return (
|
|
147
125
|
<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>
|
|
126
|
+
{/* Clinic/Hospital Header */}
|
|
127
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '10px' }}>
|
|
128
|
+
<div className={styles.locationBadge} style={{ background: '#e9ecef', color: '#495057' }}>
|
|
129
|
+
<span className="icon-hospital" />
|
|
130
|
+
<span id="lblInstituteName" style={{ fontWeight: 'bold' }}>{locationName}</span>
|
|
158
131
|
</div>
|
|
132
|
+
</div>
|
|
159
133
|
|
|
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>
|
|
134
|
+
<div className={styles.card}>
|
|
135
|
+
<div className={styles.cardBody}>
|
|
136
|
+
|
|
137
|
+
{/* Section title */}
|
|
138
|
+
<div className={styles.col12} style={{ marginBottom: '1rem', padding: 0 }}>
|
|
139
|
+
<h2 className={styles.heading} style={{ margin: 0 }}>
|
|
140
|
+
{t('samplecollectionHeading', 'Sample Collection List')}
|
|
141
|
+
</h2>
|
|
142
|
+
</div>
|
|
179
143
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
144
|
+
{/* Filters & Search Row */}
|
|
145
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem', flexWrap: 'wrap', gap: '10px' }}>
|
|
146
|
+
<div className={`${styles.dFlex} ${styles.alignItemsCenter}`}>
|
|
147
|
+
<label htmlFor="sectionFilter" className={styles.labelCompact} style={{ fontWeight: 'bold', width: 'auto', marginRight: '8px' }}>
|
|
148
|
+
Section:
|
|
149
|
+
</label>
|
|
150
|
+
<select
|
|
151
|
+
id="sectionFilter"
|
|
152
|
+
className={styles.formControl}
|
|
153
|
+
style={{ width: 'auto', minWidth: '160px', height: '30px' }}
|
|
154
|
+
value={sectionFilter}
|
|
155
|
+
onChange={(e) => {
|
|
156
|
+
setSectionFilter(e.target.value);
|
|
157
|
+
setPage(1);
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
<option value="ALL">All</option>
|
|
161
|
+
<option value="OPD">OPD</option>
|
|
162
|
+
<option value="Clinic">Clinic</option>
|
|
163
|
+
{wardSections.map((sect) => (
|
|
164
|
+
<option key={sect} value={sect}>{sect}</option>
|
|
165
|
+
))}
|
|
166
|
+
</select>
|
|
167
|
+
</div>
|
|
191
168
|
|
|
192
|
-
|
|
193
|
-
|
|
169
|
+
<div className={`${styles.dFlex} ${styles.alignItemsCenter}`}>
|
|
170
|
+
<label htmlFor="searchQuery" className={styles.labelCompact} style={{ fontWeight: 'bold', width: 'auto', marginRight: '8px' }}>
|
|
171
|
+
Search:
|
|
172
|
+
</label>
|
|
173
|
+
<input
|
|
174
|
+
id="searchQuery"
|
|
175
|
+
type="text"
|
|
176
|
+
className={styles.formControl}
|
|
177
|
+
placeholder="Type search query..."
|
|
178
|
+
style={{ width: '220px', height: '30px' }}
|
|
179
|
+
value={searchQuery}
|
|
180
|
+
onChange={(e) => {
|
|
181
|
+
setSearchQuery(e.target.value);
|
|
182
|
+
setPage(1);
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
194
187
|
|
|
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
|
-
|
|
188
|
+
{/* Table Container */}
|
|
189
|
+
<div className={styles.tableResponsive}>
|
|
190
|
+
<table className={`${styles.table} ${styles.tableBordered} ${styles.tableHover} ${styles.tableStriped}`}>
|
|
191
|
+
<thead>
|
|
192
|
+
<tr>
|
|
193
|
+
<th style={{ width: '50px' }}>No</th>
|
|
194
|
+
<th style={{ whiteSpace: 'nowrap' }}>Section</th>
|
|
195
|
+
<th style={{ whiteSpace: 'nowrap' }}>PHN No</th>
|
|
196
|
+
<th style={{ whiteSpace: 'nowrap' }}>Name</th>
|
|
197
|
+
<th style={{ whiteSpace: 'nowrap' }}>Gender</th>
|
|
198
|
+
<th style={{ whiteSpace: 'nowrap' }}>Age</th>
|
|
199
|
+
<th style={{ whiteSpace: 'nowrap' }}>Test Name</th>
|
|
200
|
+
<th style={{ width: '80px' }}></th>
|
|
201
|
+
</tr>
|
|
202
|
+
</thead>
|
|
203
|
+
<tbody>
|
|
204
|
+
{loading && data.length === 0 ? (
|
|
205
|
+
<tr>
|
|
206
|
+
<td colSpan={8} style={{ textAlign: 'center', padding: '20px' }}>
|
|
207
|
+
Loading data...
|
|
208
|
+
</td>
|
|
209
|
+
</tr>
|
|
210
|
+
) : data.length === 0 ? (
|
|
211
|
+
<tr>
|
|
212
|
+
<td colSpan={8} style={{ textAlign: 'center', padding: '20px' }}>
|
|
213
|
+
No sample collections found.
|
|
214
|
+
</td>
|
|
215
|
+
</tr>
|
|
216
|
+
) : (
|
|
217
|
+
data.map((row, index) => {
|
|
218
|
+
const phnNo = row[0];
|
|
219
|
+
const patientName = row[1];
|
|
220
|
+
const testName = row[2];
|
|
221
|
+
const gender = row[3];
|
|
222
|
+
const age = row[4];
|
|
223
|
+
const section = row[7];
|
|
224
|
+
const specimenStatus = row[8];
|
|
225
|
+
const progress = row[9];
|
|
226
|
+
const rowIndex = startIndex + index + 1;
|
|
228
227
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
228
|
+
let rowClass = '';
|
|
229
|
+
if (specimenStatus === 'Inadequate') {
|
|
230
|
+
rowClass = styles.rowInadequate;
|
|
231
|
+
} else if (specimenStatus === 'Error') {
|
|
232
|
+
rowClass = styles.rowError;
|
|
233
|
+
}
|
|
235
234
|
|
|
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
|
-
|
|
235
|
+
return (
|
|
236
|
+
<tr key={`${phnNo}-${section}-${index}`} className={rowClass}>
|
|
237
|
+
<td>{rowIndex}</td>
|
|
238
|
+
<td>{section}</td>
|
|
239
|
+
<td>{phnNo}</td>
|
|
240
|
+
<td>{patientName}</td>
|
|
241
|
+
<td>{gender}</td>
|
|
242
|
+
<td>{age}</td>
|
|
243
|
+
<td>{testName}</td>
|
|
244
|
+
<td style={{ textAlign: 'center' }}>
|
|
245
|
+
<button
|
|
246
|
+
type="button"
|
|
247
|
+
className={`${styles.btn} ${styles.btnPrimary}`}
|
|
248
|
+
style={{ padding: '2px 8px', fontSize: '12px' }}
|
|
249
|
+
onClick={() => handleSelectPatient(phnNo, section, progress)}
|
|
250
|
+
>
|
|
251
|
+
Select
|
|
252
|
+
</button>
|
|
253
|
+
</td>
|
|
254
|
+
</tr>
|
|
255
|
+
);
|
|
256
|
+
})
|
|
257
|
+
)}
|
|
258
|
+
</tbody>
|
|
259
|
+
</table>
|
|
260
|
+
</div>
|
|
261
261
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
262
|
+
{/* Simple Bootstrap-like Pagination */}
|
|
263
|
+
{filteredRecords > 0 && (
|
|
264
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: '1rem', flexWrap: 'wrap' }}>
|
|
265
|
+
<div style={{ fontSize: '14px', color: '#6c757d' }}>
|
|
266
|
+
Showing {startIndex + 1} to {Math.min(startIndex + pageSize, filteredRecords)} of {filteredRecords} entries
|
|
267
|
+
</div>
|
|
268
|
+
<div style={{ display: 'flex', gap: '5px' }}>
|
|
269
|
+
<button
|
|
270
|
+
type="button"
|
|
271
|
+
className={`${styles.btn} ${styles.btnPrimary}`}
|
|
272
|
+
style={{ padding: '4px 12px' }}
|
|
273
|
+
disabled={page === 1}
|
|
274
|
+
onClick={() => setPage((p) => p - 1)}
|
|
275
|
+
>
|
|
276
|
+
Previous
|
|
277
|
+
</button>
|
|
278
|
+
<button
|
|
279
|
+
type="button"
|
|
280
|
+
className={`${styles.btn} ${styles.btnPrimary}`}
|
|
281
|
+
style={{ padding: '4px 12px' }}
|
|
282
|
+
disabled={page * pageSize >= filteredRecords}
|
|
283
|
+
onClick={() => setPage((p) => p + 1)}
|
|
284
|
+
>
|
|
285
|
+
Next
|
|
286
|
+
</button>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
)}
|
|
290
|
+
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
279
293
|
</div>
|
|
280
294
|
);
|
|
281
295
|
};
|