@sasindi/printlabtestresults 1.0.1

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.
Files changed (63) hide show
  1. package/LICENSE +354 -0
  2. package/README.md +167 -0
  3. package/dist/117.js +1 -0
  4. package/dist/117.js.map +1 -0
  5. package/dist/127.js +43 -0
  6. package/dist/127.js.map +1 -0
  7. package/dist/145.js +22 -0
  8. package/dist/145.js.map +1 -0
  9. package/dist/197.js +1 -0
  10. package/dist/197.js.map +1 -0
  11. package/dist/237.js +1 -0
  12. package/dist/237.js.map +1 -0
  13. package/dist/340.js +1 -0
  14. package/dist/340.js.map +1 -0
  15. package/dist/362.js +1 -0
  16. package/dist/362.js.map +1 -0
  17. package/dist/480.js +1 -0
  18. package/dist/480.js.map +1 -0
  19. package/dist/517.js +1 -0
  20. package/dist/517.js.map +1 -0
  21. package/dist/529.js +1 -0
  22. package/dist/529.js.map +1 -0
  23. package/dist/538.js +1 -0
  24. package/dist/538.js.map +1 -0
  25. package/dist/578.js +1 -0
  26. package/dist/578.js.map +1 -0
  27. package/dist/652.js +1 -0
  28. package/dist/652.js.map +1 -0
  29. package/dist/668.js +1 -0
  30. package/dist/668.js.map +1 -0
  31. package/dist/7.js +1 -0
  32. package/dist/7.js.map +1 -0
  33. package/dist/749.js +1 -0
  34. package/dist/749.js.map +1 -0
  35. package/dist/898.js +1 -0
  36. package/dist/898.js.map +1 -0
  37. package/dist/964.js +1 -0
  38. package/dist/964.js.map +1 -0
  39. package/dist/976.js +1 -0
  40. package/dist/976.js.map +1 -0
  41. package/dist/977.js +1 -0
  42. package/dist/977.js.map +1 -0
  43. package/dist/98.js +1 -0
  44. package/dist/98.js.map +1 -0
  45. package/dist/997.js +1 -0
  46. package/dist/997.js.map +1 -0
  47. package/dist/main.js +6 -0
  48. package/dist/main.js.map +1 -0
  49. package/dist/openmrs-esm-printlabtestresults.js +6 -0
  50. package/dist/openmrs-esm-printlabtestresults.js.buildmanifest.json +771 -0
  51. package/dist/openmrs-esm-printlabtestresults.js.map +1 -0
  52. package/dist/routes.json +1 -0
  53. package/package.json +102 -0
  54. package/src/config-schema.ts +21 -0
  55. package/src/constants.ts +1 -0
  56. package/src/declarations.d.ts +23 -0
  57. package/src/index.ts +25 -0
  58. package/src/print-lab-test-results.component.tsx +608 -0
  59. package/src/print-lab-test-results.scss +299 -0
  60. package/src/root.component.test.tsx +12 -0
  61. package/src/root.component.tsx +13 -0
  62. package/src/root.scss +5 -0
  63. package/src/routes.json +12 -0
@@ -0,0 +1,608 @@
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import styles from './print-lab-test-results.scss';
4
+
5
+ interface LabResultRow {
6
+ phnNo: string;
7
+ givenName: string;
8
+ testName: string;
9
+ gender: string;
10
+ age: string | number;
11
+ orderBy: string;
12
+ orderDateTime: string;
13
+ section: string;
14
+ barcodeNumber: string;
15
+ collectBy: string;
16
+ collectDate: string;
17
+ serial: number | string;
18
+ remark: string;
19
+ }
20
+
21
+ interface ParameterRow {
22
+ parameterName: string;
23
+ value: string;
24
+ units: string;
25
+ ranges: string;
26
+ flag?: string;
27
+ }
28
+
29
+ const PrintLabTestResults: React.FC = () => {
30
+ const { t } = useTranslation();
31
+
32
+ // Location & Session state
33
+ const [locationUuid, setLocationUuid] = useState<string>('');
34
+ const [instituteName, setInstituteName] = useState<string>('Loading location...');
35
+ const [data, setData] = useState<LabResultRow[]>([]);
36
+ const [loading, setLoading] = useState<boolean>(true);
37
+
38
+ // Filter & Search state
39
+ const [sectionFilter, setSectionFilter] = useState<string>('ALL');
40
+ const [searchQuery, setSearchQuery] = useState<string>('');
41
+
42
+ // Modal state
43
+ const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
44
+ const [selectedRow, setSelectedRow] = useState<LabResultRow | null>(null);
45
+ const [patientDetails, setPatientDetails] = useState<{
46
+ fullName: string;
47
+ section: string;
48
+ phn: string;
49
+ age: string;
50
+ gender: string;
51
+ testName: string;
52
+ remark: string;
53
+ date: string;
54
+ time: string;
55
+ } | null>(null);
56
+
57
+ const [parameters, setParameters] = useState<ParameterRow[]>([]);
58
+ const [loadingParams, setLoadingParams] = useState<boolean>(false);
59
+
60
+ // Pagination state
61
+ const [currentPage, setCurrentPage] = useState<number>(1);
62
+ const [pageSize, setPageSize] = useState<number>(25);
63
+
64
+ // Initialize Session and Location Context
65
+ useEffect(() => {
66
+ async function initSession() {
67
+ try {
68
+ const sessionRes = await fetch('/openmrs/ws/rest/v1/session');
69
+ if (!sessionRes.ok) throw new Error('Session request failed');
70
+ const sessionData = await sessionRes.json();
71
+ const personUuid = sessionData.user?.person?.uuid;
72
+
73
+ let locUuid = '';
74
+
75
+ if (personUuid) {
76
+ const personRes = await fetch(`/openmrs/ws/rest/v1/person/${personUuid}`);
77
+ if (personRes.ok) {
78
+ const personData = await personRes.json();
79
+ if (personData.attributes) {
80
+ personData.attributes.forEach((attr: any) => {
81
+ if (attr.display) {
82
+ const [key, value] = attr.display.split('=');
83
+ if (key && value) {
84
+ const cleanKey = key.trim();
85
+ if (cleanKey === 'Institute Id' || cleanKey === 'Institute') {
86
+ locUuid = value.trim();
87
+ }
88
+ }
89
+ }
90
+ });
91
+ }
92
+ }
93
+ }
94
+
95
+ if (!locUuid && sessionData.sessionLocation?.uuid) {
96
+ locUuid = sessionData.sessionLocation.uuid;
97
+ }
98
+
99
+ if (locUuid) {
100
+ setLocationUuid(locUuid);
101
+ const locRes = await fetch(`/openmrs/ws/rest/v1/location/${locUuid}`);
102
+ if (locRes.ok) {
103
+ const locData = await locRes.json();
104
+ setInstituteName(locData.display || 'OpenMRS Location');
105
+ }
106
+ } else {
107
+ setInstituteName('No Location Assigned');
108
+ setLoading(false);
109
+ }
110
+ } catch (err) {
111
+ console.error('Error initializing location context:', err);
112
+ setInstituteName('Location Load Error');
113
+ setLoading(false);
114
+ }
115
+ }
116
+
117
+ initSession();
118
+ }, []);
119
+
120
+ // Fetch Table Data when locationUuid is set
121
+ useEffect(() => {
122
+ if (!locationUuid) return;
123
+
124
+ async function loadTableData() {
125
+ setLoading(true);
126
+ try {
127
+ const response = await fetch(`/openmrs/ws/rest/v1/printlabreportresults/loaddetails/${locationUuid}`);
128
+ if (!response.ok) throw new Error('Failed to load details');
129
+ const rawData = await response.json();
130
+
131
+ // Map array response to LabResultRow objects
132
+ // Index mapping from legacy query:
133
+ // [0: phn, 1: givenname, 2: testname, 3: gender, 4: age, 5: orderby, 6: orderdatetime, 7: section, 8: barcodenumber, 9: labdocname, 10: collectioncdate, 11: id, 12: remark]
134
+ const mappedRows: LabResultRow[] = (rawData || []).map((row: any) => ({
135
+ phnNo: row[0] || '',
136
+ givenName: row[1] || '',
137
+ testName: row[2] || '',
138
+ gender: row[3] || '',
139
+ age: row[4] || '',
140
+ orderBy: row[5] || '',
141
+ orderDateTime: row[6] || '',
142
+ section: row[7] || '',
143
+ barcodeNumber: row[8] || '',
144
+ collectBy: row[9] || '',
145
+ collectDate: row[10] || '',
146
+ serial: row[11] || 0,
147
+ remark: row[12] || '',
148
+ }));
149
+
150
+ setData(mappedRows);
151
+ } catch (err) {
152
+ console.error('Error fetching print lab test results:', err);
153
+ setData([]);
154
+ } finally {
155
+ setLoading(false);
156
+ }
157
+ }
158
+
159
+ loadTableData();
160
+ }, [locationUuid]);
161
+
162
+ // Filtered & Searched Data
163
+ const filteredData = useMemo(() => {
164
+ return data.filter((row) => {
165
+ // Section Filter
166
+ if (sectionFilter !== 'ALL' && row.section.toLowerCase() !== sectionFilter.toLowerCase()) {
167
+ return false;
168
+ }
169
+ // Search Filter
170
+ if (searchQuery.trim() !== '') {
171
+ const query = searchQuery.toLowerCase();
172
+ const matchBarcode = row.barcodeNumber.toLowerCase().includes(query);
173
+ const matchPhn = row.phnNo.toLowerCase().includes(query);
174
+ const matchName = row.givenName.toLowerCase().includes(query);
175
+ const matchTest = row.testName.toLowerCase().includes(query);
176
+ const matchOrderBy = row.orderBy.toLowerCase().includes(query);
177
+ const matchCollectBy = row.collectBy.toLowerCase().includes(query);
178
+ return matchBarcode || matchPhn || matchName || matchTest || matchOrderBy || matchCollectBy;
179
+ }
180
+ return true;
181
+ });
182
+ }, [data, sectionFilter, searchQuery]);
183
+
184
+ // Pagination slicing
185
+ const paginatedData = useMemo(() => {
186
+ const start = (currentPage - 1) * pageSize;
187
+ return filteredData.slice(start, start + pageSize);
188
+ }, [filteredData, currentPage, pageSize]);
189
+
190
+ const totalPages = Math.ceil(filteredData.length / pageSize) || 1;
191
+
192
+ // Open "View Results" Modal
193
+ const handleView = async (row: LabResultRow) => {
194
+ setSelectedRow(row);
195
+
196
+ const now = new Date();
197
+ const dateStr = now.toISOString().split('T')[0];
198
+ const timeStr = `${('0' + now.getHours()).slice(-2)}:${('0' + now.getMinutes()).slice(-2)}`;
199
+
200
+ setPatientDetails({
201
+ fullName: row.givenName,
202
+ section: row.section,
203
+ phn: row.phnNo,
204
+ age: String(row.age),
205
+ gender: row.gender,
206
+ testName: row.testName,
207
+ remark: row.remark,
208
+ date: dateStr,
209
+ time: timeStr,
210
+ });
211
+
212
+ setIsModalOpen(true);
213
+ setLoadingParams(true);
214
+ setParameters([]);
215
+
216
+ try {
217
+ let endpoint = '';
218
+ const testEnc = encodeURIComponent(row.testName);
219
+ if (row.section === 'OPD') {
220
+ endpoint = `/openmrs/ws/rest/v1/printlabreportresults/loadparameter/${testEnc}/${locationUuid}/${row.serial}`;
221
+ } else if (row.section === 'Clinic') {
222
+ endpoint = `/openmrs/ws/rest/v1/printlabreportresults/loadparameterclinic/${testEnc}/${locationUuid}/${row.serial}`;
223
+ } else if (row.section === 'Ward') {
224
+ endpoint = `/openmrs/ws/rest/v1/printlabreportresults/loadparameterward/${testEnc}/${locationUuid}/${row.serial}`;
225
+ }
226
+
227
+ if (endpoint) {
228
+ const res = await fetch(endpoint);
229
+ if (res.ok) {
230
+ const paramData = await res.json();
231
+ // Array mapping: [0: parameterName, 1: value, 2: flag, 3: ranges, 4: units]
232
+ const mappedParams: ParameterRow[] = (paramData || []).map((p: any) => ({
233
+ parameterName: p[0] || '',
234
+ value: p[1] || '',
235
+ flag: p[2] || '',
236
+ ranges: p[3] || '',
237
+ units: p[4] || '',
238
+ }));
239
+ setParameters(mappedParams);
240
+ }
241
+ }
242
+ } catch (err) {
243
+ console.error('Error loading parameters:', err);
244
+ } finally {
245
+ setLoadingParams(false);
246
+ }
247
+ };
248
+
249
+ // Open PDF Report
250
+ const handlePrintReport = async (row: LabResultRow) => {
251
+ try {
252
+ let detailsEndpoint = '';
253
+ if (row.section === 'OPD') {
254
+ detailsEndpoint = `/openmrs/ws/rest/v1/printlabreportresults/labdetailsopd/${row.serial}`;
255
+ } else if (row.section === 'Clinic') {
256
+ detailsEndpoint = `/openmrs/ws/rest/v1/printlabreportresults/labdetailsclinic/${row.serial}`;
257
+ } else if (row.section === 'Ward') {
258
+ detailsEndpoint = `/openmrs/ws/rest/v1/printlabreportresults/labdetailsward/${row.serial}`;
259
+ }
260
+
261
+ if (detailsEndpoint) {
262
+ const response = await fetch(detailsEndpoint);
263
+ if (response.ok) {
264
+ const labData = await response.json();
265
+ const encounterUuid = labData.encounteruuid || '';
266
+ const documentName = `${row.phnNo}@@@${row.testName}@@@${encounterUuid}`;
267
+ const filename = `${documentName}.pdf`;
268
+ const fileUrl = `${window.location.origin}/openmrs/owa/LabReportFileUploaded/${filename}`;
269
+ window.open(fileUrl, '_blank');
270
+ } else {
271
+ alert('Could not retrieve lab details for report PDF generation.');
272
+ }
273
+ }
274
+ } catch (err) {
275
+ console.error('Error printing lab report:', err);
276
+ alert('Error fetching report PDF.');
277
+ }
278
+ };
279
+
280
+ const getSectionBadgeClass = (section: string) => {
281
+ switch (section.toLowerCase()) {
282
+ case 'opd':
283
+ return styles.tagOpd;
284
+ case 'clinic':
285
+ return styles.tagClinic;
286
+ case 'ward':
287
+ return styles.tagWard;
288
+ default:
289
+ return styles.tagOpd;
290
+ }
291
+ };
292
+
293
+ return (
294
+ <div className={styles.container}>
295
+ <div className={styles.card}>
296
+ <div className={styles.cardBody}>
297
+
298
+ {/* Header & Institute Name */}
299
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
300
+ <h2 className={styles.heading}>
301
+ {t('viewTestResultsHeading', 'View Test Results')}
302
+ </h2>
303
+ <div style={{ fontSize: '14px', fontWeight: 'bold', color: '#161616' }}>
304
+ <span>🏥 {instituteName}</span>
305
+ </div>
306
+ </div>
307
+
308
+ {/* Filters & Search Row */}
309
+ <div className={styles.filterRow}>
310
+ <div className={styles.filterGroup}>
311
+ <label htmlFor="sectionFilter" className={styles.labelCompact}>
312
+ Section:
313
+ </label>
314
+ <select
315
+ id="sectionFilter"
316
+ className={styles.selectCompact}
317
+ value={sectionFilter}
318
+ onChange={(e) => {
319
+ setSectionFilter(e.target.value);
320
+ setCurrentPage(1);
321
+ }}
322
+ >
323
+ <option value="ALL">All Sections</option>
324
+ <option value="OPD">OPD</option>
325
+ <option value="Clinic">Clinic</option>
326
+ <option value="Ward">Ward</option>
327
+ </select>
328
+ </div>
329
+
330
+ <div className={styles.filterGroup}>
331
+ <label htmlFor="searchInput" className={styles.labelCompact}>
332
+ Search:
333
+ </label>
334
+ <input
335
+ id="searchInput"
336
+ type="text"
337
+ placeholder="Search PHN, Barcode, Test Name, Patient..."
338
+ className={styles.searchInput}
339
+ value={searchQuery}
340
+ onChange={(e) => {
341
+ setSearchQuery(e.target.value);
342
+ setCurrentPage(1);
343
+ }}
344
+ />
345
+ </div>
346
+ </div>
347
+
348
+ {/* Table */}
349
+ <div className={styles.tableWrapper}>
350
+ <table className={styles.dataTable}>
351
+ <thead>
352
+ <tr>
353
+ <th>No</th>
354
+ <th>Section</th>
355
+ <th>Barcode No</th>
356
+ <th>PHN No</th>
357
+ <th>Patient Name</th>
358
+ <th>Gender</th>
359
+ <th>Age</th>
360
+ <th>Test Name</th>
361
+ <th>Order By</th>
362
+ <th>Order Date</th>
363
+ <th>Collect By</th>
364
+ <th>Collect Date</th>
365
+ <th>Actions</th>
366
+ </tr>
367
+ </thead>
368
+ <tbody>
369
+ {loading ? (
370
+ <tr>
371
+ <td colSpan={13} style={{ textAlign: 'center', padding: '24px' }}>
372
+ Loading test results...
373
+ </td>
374
+ </tr>
375
+ ) : paginatedData.length === 0 ? (
376
+ <tr>
377
+ <td colSpan={13} style={{ textAlign: 'center', padding: '24px', color: '#6f6f6f' }}>
378
+ No approved test results found.
379
+ </td>
380
+ </tr>
381
+ ) : (
382
+ paginatedData.map((row, idx) => (
383
+ <tr key={`${row.section}-${row.serial}-${idx}`}>
384
+ <td>{(currentPage - 1) * pageSize + idx + 1}</td>
385
+ <td>
386
+ <span className={getSectionBadgeClass(row.section)}>
387
+ {row.section}
388
+ </span>
389
+ </td>
390
+ <td style={{ fontWeight: '500' }}>{row.barcodeNumber}</td>
391
+ <td>{row.phnNo}</td>
392
+ <td>{row.givenName}</td>
393
+ <td>{row.gender}</td>
394
+ <td>{row.age}</td>
395
+ <td style={{ fontWeight: '600' }}>{row.testName}</td>
396
+ <td>{row.orderBy}</td>
397
+ <td>{row.orderDateTime}</td>
398
+ <td>{row.collectBy}</td>
399
+ <td>{row.collectDate}</td>
400
+ <td>
401
+ <div className={styles.actionBtnGroup}>
402
+ <button
403
+ type="button"
404
+ className={styles.btnPrimary}
405
+ onClick={() => handleView(row)}
406
+ >
407
+ View
408
+ </button>
409
+ <button
410
+ type="button"
411
+ className={styles.btnSuccess}
412
+ onClick={() => handlePrintReport(row)}
413
+ >
414
+ Print
415
+ </button>
416
+ </div>
417
+ </td>
418
+ </tr>
419
+ ))
420
+ )}
421
+ </tbody>
422
+ </table>
423
+ </div>
424
+
425
+ {/* Pagination */}
426
+ {!loading && filteredData.length > 0 && (
427
+ <div className={styles.pagination}>
428
+ <div>
429
+ Showing {(currentPage - 1) * pageSize + 1} to{' '}
430
+ {Math.min(currentPage * pageSize, filteredData.length)} of {filteredData.length} records
431
+ </div>
432
+
433
+ <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
434
+ <button
435
+ type="button"
436
+ className={styles.btnSecondary}
437
+ disabled={currentPage === 1}
438
+ onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
439
+ >
440
+ Previous
441
+ </button>
442
+ <span>
443
+ Page {currentPage} of {totalPages}
444
+ </span>
445
+ <button
446
+ type="button"
447
+ className={styles.btnSecondary}
448
+ disabled={currentPage >= totalPages}
449
+ onClick={() => setCurrentPage((p) => Math.min(p + 1, totalPages))}
450
+ >
451
+ Next
452
+ </button>
453
+
454
+ <select
455
+ className={styles.selectCompact}
456
+ value={pageSize}
457
+ onChange={(e) => {
458
+ setPageSize(Number(e.target.value));
459
+ setCurrentPage(1);
460
+ }}
461
+ style={{ marginLeft: '12px' }}
462
+ >
463
+ <option value={10}>10 per page</option>
464
+ <option value={25}>25 per page</option>
465
+ <option value={50}>50 per page</option>
466
+ <option value={100}>100 per page</option>
467
+ </select>
468
+ </div>
469
+ </div>
470
+ )}
471
+
472
+ </div>
473
+ </div>
474
+
475
+ {/* View Results Modal */}
476
+ {isModalOpen && patientDetails && (
477
+ <div className={styles.modalOverlay}>
478
+ <div className={styles.modalContainer}>
479
+ <div className={styles.modalHeader}>
480
+ <h3>View Test Results</h3>
481
+ <button
482
+ type="button"
483
+ className={styles.btnClose}
484
+ onClick={() => setIsModalOpen(false)}
485
+ >
486
+ &times;
487
+ </button>
488
+ </div>
489
+
490
+ <div className={styles.modalBody}>
491
+ <div className={styles.gridForm}>
492
+ <div className={styles.formField}>
493
+ <label>Full Name</label>
494
+ <input type="text" value={patientDetails.fullName} readOnly />
495
+ </div>
496
+
497
+ <div className={styles.formField}>
498
+ <label>Section</label>
499
+ <input type="text" value={patientDetails.section} readOnly />
500
+ </div>
501
+
502
+ <div className={styles.formField}>
503
+ <label>PHN No</label>
504
+ <input type="text" value={patientDetails.phn} readOnly />
505
+ </div>
506
+
507
+ <div className={styles.formField}>
508
+ <label>Age</label>
509
+ <input type="text" value={patientDetails.age} readOnly />
510
+ </div>
511
+
512
+ <div className={styles.formField}>
513
+ <label>Gender</label>
514
+ <input type="text" value={patientDetails.gender} readOnly />
515
+ </div>
516
+
517
+ <div className={styles.formField}>
518
+ <label>Test Name</label>
519
+ <input type="text" value={patientDetails.testName} readOnly />
520
+ </div>
521
+
522
+ <div className={`${styles.formField} ${styles.formFieldFull}`}>
523
+ <label>Remark / Notes</label>
524
+ <textarea rows={2} value={patientDetails.remark} readOnly />
525
+ </div>
526
+
527
+ <div className={styles.formField}>
528
+ <label>Date</label>
529
+ <input type="text" value={patientDetails.date} readOnly />
530
+ </div>
531
+
532
+ <div className={styles.formField}>
533
+ <label>Time</label>
534
+ <input type="text" value={patientDetails.time} readOnly />
535
+ </div>
536
+ </div>
537
+
538
+ {/* Parameter Table */}
539
+ <h4 style={{ fontSize: '15px', fontWeight: '600', color: '#161616', marginTop: '16px', marginBottom: '8px' }}>
540
+ Test Parameters & Values
541
+ </h4>
542
+
543
+ <div className={styles.tableWrapper}>
544
+ <table className={styles.dataTable}>
545
+ <thead>
546
+ <tr>
547
+ <th>No</th>
548
+ <th>Parameter Name</th>
549
+ <th>Value</th>
550
+ <th>Units</th>
551
+ <th>Ranges</th>
552
+ </tr>
553
+ </thead>
554
+ <tbody>
555
+ {loadingParams ? (
556
+ <tr>
557
+ <td colSpan={5} style={{ textAlign: 'center', padding: '16px' }}>
558
+ Loading parameters...
559
+ </td>
560
+ </tr>
561
+ ) : parameters.length === 0 ? (
562
+ <tr>
563
+ <td colSpan={5} style={{ textAlign: 'center', padding: '16px', color: '#6f6f6f' }}>
564
+ No parameter details recorded for this test.
565
+ </td>
566
+ </tr>
567
+ ) : (
568
+ parameters.map((param, index) => (
569
+ <tr key={`param-${index}`}>
570
+ <td>{index + 1}</td>
571
+ <td style={{ fontWeight: '500' }}>{param.parameterName}</td>
572
+ <td>{param.value}</td>
573
+ <td>{param.units}</td>
574
+ <td>{param.ranges}</td>
575
+ </tr>
576
+ ))
577
+ )}
578
+ </tbody>
579
+ </table>
580
+ </div>
581
+ </div>
582
+
583
+ <div className={styles.modalFooter}>
584
+ <button
585
+ type="button"
586
+ className={styles.btnSecondary}
587
+ onClick={() => setIsModalOpen(false)}
588
+ >
589
+ Close
590
+ </button>
591
+ {selectedRow && (
592
+ <button
593
+ type="button"
594
+ className={styles.btnSuccess}
595
+ onClick={() => handlePrintReport(selectedRow)}
596
+ >
597
+ Print Report PDF
598
+ </button>
599
+ )}
600
+ </div>
601
+ </div>
602
+ </div>
603
+ )}
604
+ </div>
605
+ );
606
+ };
607
+
608
+ export default PrintLabTestResults;