@sasindi/samplecollection 1.0.0 → 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/src/routes.json CHANGED
@@ -6,6 +6,12 @@
6
6
  "component": "root",
7
7
  "online": true,
8
8
  "offline": true
9
+ },
10
+ {
11
+ "route": "samplecollection/room",
12
+ "component": "root",
13
+ "online": true,
14
+ "offline": true
9
15
  }
10
16
  ],
11
17
  "extensions": [
@@ -1,32 +1,14 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { useNavigate } from 'react-router-dom';
3
2
  import { useTranslation } from 'react-i18next';
4
- import {
5
- Tile,
6
- Select,
7
- SelectItem,
8
- TextInput,
9
- Table,
10
- TableHead,
11
- TableRow,
12
- TableHeader,
13
- TableBody,
14
- TableCell,
15
- Button,
16
- Pagination,
17
- Loading
18
- } from '@carbon/react';
19
3
  import styles from './samplecollection.scss';
20
4
 
21
- const SearchIcon = () => (
22
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
23
- <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" />
24
- </svg>
25
- );
5
+ const navigateTo = (url: string) => {
6
+ window.history.pushState({}, '', url);
7
+ window.dispatchEvent(new PopStateEvent('popstate'));
8
+ };
26
9
 
27
10
  const Samplecollection: React.FC = () => {
28
11
  const { t } = useTranslation();
29
- const navigate = useNavigate();
30
12
 
31
13
  // State variables
32
14
  const [locationUuid, setLocationUuid] = useState<string>('');
@@ -39,8 +21,7 @@ const Samplecollection: React.FC = () => {
39
21
 
40
22
  // Pagination state
41
23
  const [page, setPage] = useState<number>(1);
42
- const [pageSize, setPageSize] = useState<number>(50);
43
- const [totalRecords, setTotalRecords] = useState<number>(0);
24
+ const [pageSize] = useState<number>(50);
44
25
  const [filteredRecords, setFilteredRecords] = useState<number>(0);
45
26
 
46
27
  // Load Session and Location context
@@ -116,7 +97,6 @@ const Samplecollection: React.FC = () => {
116
97
  const resData = await response.json();
117
98
 
118
99
  setData(resData.data || []);
119
- setTotalRecords(resData.recordsTotal || 0);
120
100
  setFilteredRecords(resData.recordsFiltered || 0);
121
101
  } catch (err) {
122
102
  console.error('Error fetching table data:', err);
@@ -125,7 +105,6 @@ const Samplecollection: React.FC = () => {
125
105
  }
126
106
  }
127
107
 
128
- // Debounce data fetching slightly if search query is changing rapidly
129
108
  const delayDebounceFn = setTimeout(() => {
130
109
  fetchTableData();
131
110
  }, 300);
@@ -137,142 +116,180 @@ const Samplecollection: React.FC = () => {
137
116
  localStorage.setItem('phnNo', phnNo);
138
117
  localStorage.setItem('section', section);
139
118
  localStorage.setItem('progress', progress || '');
140
- navigate('/samplecollection/room');
119
+ navigateTo('/openmrs/spa/samplecollection/room');
141
120
  };
142
121
 
122
+ const startIndex = (page - 1) * pageSize;
123
+
143
124
  return (
144
125
  <div className={styles.container}>
145
- <Tile className={styles.tile}>
146
- {/* Header Section */}
147
- <div className={styles.headerRow}>
148
- <h1 className={styles.heading}>
149
- {t('samplecollectionHeading', 'Sample Collection List')}
150
- </h1>
151
- <div className={styles.locationBadge}>
152
- <span className="icon-hospital" />
153
- <span>{locationName}</span>
154
- </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>
155
131
  </div>
132
+ </div>
156
133
 
157
- {/* Filter and Search controls */}
158
- <div className={styles.filterRow}>
159
- <Select
160
- id="sectionFilter"
161
- labelText={t('sectionLabel', 'Section')}
162
- value={sectionFilter}
163
- onChange={(e) => {
164
- setSectionFilter(e.target.value);
165
- setPage(1); // Reset page on filter change
166
- }}
167
- style={{ width: '200px' }}
168
- >
169
- <SelectItem value="ALL" text={t('allSections', 'All')} />
170
- <SelectItem value="OPD" text="OPD" />
171
- <SelectItem value="Clinic" text="Clinic" />
172
- {wardSections.map((sect) => (
173
- <SelectItem key={sect} value={sect} text={sect} />
174
- ))}
175
- </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>
176
143
 
177
- <TextInput
178
- id="searchQuery"
179
- labelText={t('searchLabel', 'Search (PHN, Name, Test)')}
180
- placeholder={t('searchPlaceholder', 'Type to search...')}
181
- value={searchQuery}
182
- onChange={(e) => {
183
- setSearchQuery(e.target.value);
184
- setPage(1); // Reset page on search
185
- }}
186
- />
187
- </div>
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>
188
168
 
189
- {/* Loading Indicator */}
190
- {loading && <Loading description="Loading data..." withOverlay={false} />}
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>
191
187
 
192
- {/* Main Table */}
193
- <div className={styles.tableContainer}>
194
- <Table>
195
- <TableHead>
196
- <TableRow>
197
- <TableHeader>No</TableHeader>
198
- <TableHeader>Section</TableHeader>
199
- <TableHeader>PHN No</TableHeader>
200
- <TableHeader>Name</TableHeader>
201
- <TableHeader>Gender</TableHeader>
202
- <TableHeader>Age</TableHeader>
203
- <TableHeader>Test Name</TableHeader>
204
- <TableHeader />
205
- </TableRow>
206
- </TableHead>
207
- <TableBody>
208
- {data.length === 0 ? (
209
- <TableRow>
210
- <TableCell colSpan={8} style={{ textAlign: 'center', padding: '2rem' }}>
211
- {loading ? 'Fetching data...' : 'No sample collections found.'}
212
- </TableCell>
213
- </TableRow>
214
- ) : (
215
- data.map((row, index) => {
216
- const phnNo = row[0];
217
- const patientName = row[1];
218
- const testName = row[2];
219
- const gender = row[3];
220
- const age = row[4];
221
- const section = row[7];
222
- const specimenStatus = row[8]; // 'Inadequate', 'Error'
223
- const progress = row[9];
224
- const rowIndex = (page - 1) * pageSize + index + 1;
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;
225
227
 
226
- let trClass = '';
227
- if (specimenStatus === 'Inadequate') {
228
- trClass = styles.rowInadequate;
229
- } else if (specimenStatus === 'Error') {
230
- trClass = styles.rowError;
231
- }
228
+ let rowClass = '';
229
+ if (specimenStatus === 'Inadequate') {
230
+ rowClass = styles.rowInadequate;
231
+ } else if (specimenStatus === 'Error') {
232
+ rowClass = styles.rowError;
233
+ }
232
234
 
233
- return (
234
- <TableRow key={`${phnNo}-${section}-${index}`} className={trClass}>
235
- <TableCell>{rowIndex}</TableCell>
236
- <TableCell>{section}</TableCell>
237
- <TableCell>{phnNo}</TableCell>
238
- <TableCell>{patientName}</TableCell>
239
- <TableCell>{gender}</TableCell>
240
- <TableCell>{age}</TableCell>
241
- <TableCell>{testName}</TableCell>
242
- <TableCell>
243
- <Button
244
- kind="primary"
245
- size="sm"
246
- onClick={() => handleSelectPatient(phnNo, section, progress)}
247
- >
248
- {t('selectBtn', 'Select')}
249
- </Button>
250
- </TableCell>
251
- </TableRow>
252
- );
253
- })
254
- )}
255
- </TableBody>
256
- </Table>
257
- </div>
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>
258
261
 
259
- {/* Pagination */}
260
- {filteredRecords > 0 && (
261
- <Pagination
262
- backwardText="Previous page"
263
- forwardText="Next page"
264
- itemsPerPageText="Items per page:"
265
- onChange={({ page, pageSize }) => {
266
- setPage(page);
267
- setPageSize(pageSize);
268
- }}
269
- page={page}
270
- pageSize={pageSize}
271
- pageSizes={[10, 25, 50, 100]}
272
- totalItems={filteredRecords}
273
- />
274
- )}
275
- </Tile>
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>
276
293
  </div>
277
294
  );
278
295
  };