@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.
@@ -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, setPageSize] = useState<number>(50);
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
- <Tile className={styles.tile}>
149
- {/* Header Section */}
150
- <div className={styles.headerRow}>
151
- <h1 className={styles.heading}>
152
- {t('samplecollectionHeading', 'Sample Collection List')}
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
- {/* Filter and Search controls */}
161
- <div className={styles.filterRow}>
162
- <Select
163
- id="sectionFilter"
164
- labelText={t('sectionLabel', 'Section')}
165
- value={sectionFilter}
166
- onChange={(e) => {
167
- setSectionFilter(e.target.value);
168
- setPage(1); // Reset page on filter change
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
- <TextInput
181
- id="searchQuery"
182
- labelText={t('searchLabel', 'Search (PHN, Name, Test)')}
183
- placeholder={t('searchPlaceholder', 'Type to search...')}
184
- value={searchQuery}
185
- onChange={(e) => {
186
- setSearchQuery(e.target.value);
187
- setPage(1); // Reset page on search
188
- }}
189
- />
190
- </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>
191
168
 
192
- {/* Loading Indicator */}
193
- {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>
194
187
 
195
- {/* Main Table */}
196
- <div className={styles.tableContainer}>
197
- <Table>
198
- <TableHead>
199
- <TableRow>
200
- <TableHeader>No</TableHeader>
201
- <TableHeader>Section</TableHeader>
202
- <TableHeader>PHN No</TableHeader>
203
- <TableHeader>Name</TableHeader>
204
- <TableHeader>Gender</TableHeader>
205
- <TableHeader>Age</TableHeader>
206
- <TableHeader>Test Name</TableHeader>
207
- <TableHeader />
208
- </TableRow>
209
- </TableHead>
210
- <TableBody>
211
- {data.length === 0 ? (
212
- <TableRow>
213
- <TableCell colSpan={8} style={{ textAlign: 'center', padding: '2rem' }}>
214
- {loading ? 'Fetching data...' : 'No sample collections found.'}
215
- </TableCell>
216
- </TableRow>
217
- ) : (
218
- data.map((row, index) => {
219
- const phnNo = row[0];
220
- const patientName = row[1];
221
- const testName = row[2];
222
- const gender = row[3];
223
- const age = row[4];
224
- const section = row[7];
225
- const specimenStatus = row[8]; // 'Inadequate', 'Error'
226
- const progress = row[9];
227
- 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;
228
227
 
229
- let trClass = '';
230
- if (specimenStatus === 'Inadequate') {
231
- trClass = styles.rowInadequate;
232
- } else if (specimenStatus === 'Error') {
233
- trClass = styles.rowError;
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
- return (
237
- <TableRow key={`${phnNo}-${section}-${index}`} className={trClass}>
238
- <TableCell>{rowIndex}</TableCell>
239
- <TableCell>{section}</TableCell>
240
- <TableCell>{phnNo}</TableCell>
241
- <TableCell>{patientName}</TableCell>
242
- <TableCell>{gender}</TableCell>
243
- <TableCell>{age}</TableCell>
244
- <TableCell>{testName}</TableCell>
245
- <TableCell>
246
- <Button
247
- kind="primary"
248
- size="sm"
249
- onClick={() => handleSelectPatient(phnNo, section, progress)}
250
- >
251
- {t('selectBtn', 'Select')}
252
- </Button>
253
- </TableCell>
254
- </TableRow>
255
- );
256
- })
257
- )}
258
- </TableBody>
259
- </Table>
260
- </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>
261
261
 
262
- {/* Pagination */}
263
- {filteredRecords > 0 && (
264
- <Pagination
265
- backwardText="Previous page"
266
- forwardText="Next page"
267
- itemsPerPageText="Items per page:"
268
- onChange={({ page, pageSize }) => {
269
- setPage(page);
270
- setPageSize(pageSize);
271
- }}
272
- page={page}
273
- pageSize={pageSize}
274
- pageSizes={[10, 25, 50, 100]}
275
- totalItems={filteredRecords}
276
- />
277
- )}
278
- </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>
279
293
  </div>
280
294
  );
281
295
  };