@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.
@@ -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
@@ -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 && key.trim() === 'Institute Id') {
68
- locUuid = value.trim();
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
- <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>
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
- {/* 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>
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
- <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>
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
- {/* Loading Indicator */}
193
- {loading && <Loading description="Loading data..." withOverlay={false} />}
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
- {/* 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;
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
- let trClass = '';
230
- if (specimenStatus === 'Inadequate') {
231
- trClass = styles.rowInadequate;
232
- } else if (specimenStatus === 'Error') {
233
- trClass = styles.rowError;
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
- 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>
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
- {/* 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>
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
  };