@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,26 +1,9 @@
1
1
  import React, { useState, useEffect } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
- import {
4
- Tile,
5
- TextInput,
6
- Select,
7
- SelectItem,
8
- TextArea,
9
- Button,
10
- Table,
11
- TableHead,
12
- TableRow,
13
- TableHeader,
14
- TableBody,
15
- TableCell,
16
- Checkbox,
17
- Loading,
18
- InlineNotification
19
- } from '@carbon/react';
20
3
  import styles from './samplecollection.scss';
21
4
 
22
5
  const UserAvatar = () => (
23
- <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ borderRadius: '50%', border: '3px solid #009384' }}>
6
+ <svg width="60" height="60" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ borderRadius: '16px', border: '3px solid #20e5bf' }}>
24
7
  <circle cx="50" cy="50" r="50" fill="#E0F2F1"/>
25
8
  <circle cx="50" cy="40" r="18" fill="#009384"/>
26
9
  <path d="M18 78C18 62.536 32.3269 50 50 50C67.6731 50 82 62.536 82 78" stroke="#009384" strokeWidth="8" strokeLinecap="round"/>
@@ -419,223 +402,253 @@ const SamplecollectionRoom: React.FC = () => {
419
402
  }
420
403
  };
421
404
 
422
- if (loading) return <Loading description="Loading page details..." withOverlay={true} />;
405
+ if (loading) {
406
+ return (
407
+ <div className={styles.container} style={{ textAlign: 'center', padding: '50px' }}>
408
+ Loading patient details...
409
+ </div>
410
+ );
411
+ }
423
412
 
424
413
  return (
425
414
  <div className={styles.container}>
426
- <Tile className={styles.tile}>
427
- {/* Header */}
428
- <div className={styles.headerRow}>
429
- <h1 className={styles.heading}>
430
- {t('samplecollectionRoomHeading', 'Sample Collection Room')}
431
- </h1>
432
- <div className={styles.locationBadge}>
433
- <span className="icon-hospital" />
434
- <span>{locationName}</span>
435
- </div>
415
+ {/* Clinic/Hospital Header */}
416
+ <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '10px' }}>
417
+ <div className={styles.locationBadge} style={{ background: '#e9ecef', color: '#495057' }}>
418
+ <span className="icon-hospital" />
419
+ <span id="lblInstituteName" style={{ fontWeight: 'bold' }}>{locationName}</span>
436
420
  </div>
421
+ </div>
437
422
 
438
- {/* Notifications */}
439
- {errorMsg && (
440
- <InlineNotification
441
- kind="error"
442
- title="Error"
443
- subtitle={errorMsg}
444
- onClose={() => setErrorMsg('')}
445
- style={{ marginBottom: '1rem' }}
446
- />
447
- )}
448
- {successMsg && (
449
- <InlineNotification
450
- kind="success"
451
- title="Success"
452
- subtitle={successMsg}
453
- onClose={() => setSuccessMsg('')}
454
- style={{ marginBottom: '1rem' }}
455
- />
456
- )}
457
-
458
- {/* Patient Profile Card */}
459
- <div className={styles.patientCard}>
460
- <div className={styles.patientGrid}>
461
- <div className={styles.patientField}>
462
- <span className={styles.fieldLabel}>Full Name</span>
463
- <span className={styles.fieldValue}>{patientName || 'N/A'}</span>
464
- </div>
465
- <div className={styles.patientField}>
466
- <span className={styles.fieldLabel}>Section</span>
467
- <span className={styles.fieldValue}>{initialSection || 'N/A'}</span>
423
+ <div className={styles.card}>
424
+ <div className={styles.cardBody}>
425
+
426
+ {/* Section title */}
427
+ <div className={styles.col12} style={{ marginBottom: '1rem', padding: 0 }}>
428
+ <h2 className={styles.heading} style={{ margin: 0 }}>
429
+ {t('samplecollectionRoomHeading', 'Sample Collection Room')}
430
+ </h2>
431
+ </div>
432
+
433
+ {/* Messages */}
434
+ {errorMsg && (
435
+ <div style={{ padding: '8px', color: '#721c24', backgroundColor: '#f8d7da', border: '1px solid #f5c6cb', borderRadius: '4px', marginBottom: '10px' }}>
436
+ {errorMsg}
468
437
  </div>
469
- <div className={styles.patientField}>
470
- <span className={styles.fieldLabel}>PHN No</span>
471
- <span className={styles.fieldValue}>{phnNo || 'N/A'}</span>
438
+ )}
439
+ {successMsg && (
440
+ <div style={{ padding: '8px', color: '#155724', backgroundColor: '#d4edda', border: '1px solid #c3e6cb', borderRadius: '4px', marginBottom: '10px' }}>
441
+ {successMsg}
472
442
  </div>
473
- <div className={styles.patientField}>
474
- <span className={styles.fieldLabel}>Age</span>
475
- <span className={styles.fieldValue}>{patientAge ? `${patientAge} years` : 'N/A'}</span>
443
+ )}
444
+
445
+ {/* Patient Card Grid (Col-10 fields, Col-2 avatar) */}
446
+ <div className={styles.row} style={{ marginBottom: '1.5rem' }}>
447
+ <div className={styles.col10}>
448
+ <div className={styles.row}>
449
+
450
+ <div className={styles.formGroupCol}>
451
+ <label className={styles.labelCompact}>Full Name</label>
452
+ <input type="text" className={styles.formControl} value={patientName} readOnly />
453
+ </div>
454
+
455
+ <div className={styles.formGroupCol}>
456
+ <label className={styles.labelCompact}>Section</label>
457
+ <input type="text" className={styles.formControl} value={initialSection} readOnly />
458
+ </div>
459
+
460
+ <div className={styles.formGroupCol}>
461
+ <label className={styles.labelCompact}>PHN No</label>
462
+ <input type="text" className={styles.formControl} value={phnNo} readOnly />
463
+ </div>
464
+
465
+ <div className={styles.formGroupCol}>
466
+ <label className={styles.labelCompact}>Age</label>
467
+ <input type="text" className={styles.formControl} value={patientAge} readOnly />
468
+ </div>
469
+
470
+ <div className={styles.formGroupCol}>
471
+ <label className={styles.labelCompact}>Gender</label>
472
+ <input type="text" className={styles.formControl} value={patientGender} readOnly />
473
+ </div>
474
+
475
+ </div>
476
476
  </div>
477
- <div className={styles.patientField}>
478
- <span className={styles.fieldLabel}>Gender</span>
479
- <span className={styles.fieldValue}>{patientGender || 'N/A'}</span>
477
+
478
+ <div className={styles.col2}>
479
+ <div className={styles.avatarContainer}>
480
+ {profileImgName ? (
481
+ <img
482
+ src={`${window.location.origin}/openmrs/owa/PatientImagesUploaded/${profileImgName}`}
483
+ alt="Patient avatar"
484
+ className={styles.uploadImg}
485
+ onError={(e) => {
486
+ (e.target as HTMLElement).style.display = 'none';
487
+ }}
488
+ />
489
+ ) : (
490
+ <UserAvatar />
491
+ )}
492
+ </div>
480
493
  </div>
481
494
  </div>
482
- <div className={styles.avatarContainer}>
483
- {profileImgName ? (
484
- <img
485
- src={`${window.location.origin}/openmrs/owa/PatientImagesUploaded/${profileImgName}`}
486
- alt="Patient profile"
487
- className={styles.avatarImg}
488
- onError={(e) => {
489
- // Fallback if image not found
490
- (e.target as HTMLElement).style.display = 'none';
491
- }}
492
- />
493
- ) : (
494
- <UserAvatar />
495
- )}
495
+
496
+ {/* Pending Tests Table */}
497
+ <div className={styles.tableResponsive} style={{ maxHeight: '200px', overflowY: 'auto', border: '1px solid #dee2e6', marginBottom: '1.5rem' }}>
498
+ <table className={`${styles.table} ${styles.tableBordered} ${styles.tableHover} ${styles.tableStriped}`}>
499
+ <thead>
500
+ <tr>
501
+ <th style={{ width: '50px' }}>No</th>
502
+ <th style={{ whiteSpace: 'nowrap' }}>Test Name</th>
503
+ <th style={{ whiteSpace: 'nowrap' }}>Order By</th>
504
+ <th style={{ whiteSpace: 'nowrap' }}>Order Date/Time</th>
505
+ <th style={{ width: '40px' }}></th>
506
+ </tr>
507
+ </thead>
508
+ <tbody>
509
+ {testList.length === 0 ? (
510
+ <tr>
511
+ <td colSpan={5} style={{ textAlign: 'center', padding: '10px' }}>
512
+ No active tests pending collection.
513
+ </td>
514
+ </tr>
515
+ ) : (
516
+ testList.map((row, index) => {
517
+ const id = row[8];
518
+ const testName = row[2];
519
+ const orderBy = row[5];
520
+ const orderDateTime = row[6];
521
+ const urgent = row[9];
522
+ const isChecked = selectedIds.includes(String(id));
523
+
524
+ return (
525
+ <tr key={id || index}>
526
+ <td>{index + 1}</td>
527
+ <td>{testName}</td>
528
+ <td>{orderBy}</td>
529
+ <td>{orderDateTime}</td>
530
+ <td style={{ textAlign: 'center' }}>
531
+ <input
532
+ type="checkbox"
533
+ className={styles.formControl}
534
+ style={{ width: '16px', height: '16px', margin: 0 }}
535
+ checked={isChecked}
536
+ onChange={(e) =>
537
+ handleCheckboxChange(e.target.checked, String(id), testName, String(urgent), row)
538
+ }
539
+ />
540
+ </td>
541
+ </tr>
542
+ );
543
+ })
544
+ )}
545
+ </tbody>
546
+ </table>
496
547
  </div>
497
- </div>
498
548
 
499
- {/* Tests List Table */}
500
- <div style={{ maxHeight: '250px', overflowY: 'auto', marginBottom: '2rem' }}>
501
- <Table>
502
- <TableHead>
503
- <TableRow>
504
- <TableHeader>No</TableHeader>
505
- <TableHeader>Test Name</TableHeader>
506
- <TableHeader>Order By</TableHeader>
507
- <TableHeader>Order Date/Time</TableHeader>
508
- <TableHeader className={styles.checkboxCell} />
509
- </TableRow>
510
- </TableHead>
511
- <TableBody>
512
- {testList.length === 0 ? (
513
- <TableRow>
514
- <TableCell colSpan={5} style={{ textAlign: 'center' }}>
515
- No active tests pending collection.
516
- </TableCell>
517
- </TableRow>
518
- ) : (
519
- testList.map((row, index) => {
520
- const id = row[8];
521
- const testName = row[2];
522
- const orderBy = row[5];
523
- const orderDateTime = row[6];
524
- const urgent = row[9]; // 'true' or 'false'
525
- const isChecked = selectedIds.includes(String(id));
526
-
527
- return (
528
- <TableRow key={id || index}>
529
- <TableCell>{index + 1}</TableCell>
530
- <TableCell>{testName}</TableCell>
531
- <TableCell>{orderBy}</TableCell>
532
- <TableCell>{orderDateTime}</TableCell>
533
- <TableCell className={styles.checkboxCell}>
534
- <Checkbox
535
- id={`chk-${id}`}
536
- labelText=""
537
- checked={isChecked}
538
- onChange={(e, { checked }) =>
539
- handleCheckboxChange(checked, String(id), testName, String(urgent), row)
540
- }
541
- />
542
- </TableCell>
543
- </TableRow>
544
- );
545
- })
546
- )}
547
- </TableBody>
548
- </Table>
549
- </div>
549
+ {/* Test Detail Display inputs */}
550
+ <div className={styles.row} style={{ marginBottom: '1rem' }}>
551
+
552
+ <div className={styles.formGroupCol}>
553
+ <label className={styles.labelCompact}>Test Name</label>
554
+ <input type="text" className={styles.formControl} value={displayTestNames} readOnly />
555
+ </div>
550
556
 
551
- {/* Detail Panel */}
552
- <div className={styles.roomFormSection}>
553
- <div className={styles.formGrid}>
554
- <TextInput
555
- id="txttestname"
556
- labelText="Test Name"
557
- value={displayTestNames}
558
- readOnly
559
- />
560
- <TextInput
561
- id="txtorderby"
562
- labelText="Order By"
563
- value={displayOrderBy}
564
- readOnly
565
- />
566
- <TextInput
567
- id="txtorderdate"
568
- labelText="Order Date"
569
- value={displayOrderDate}
570
- readOnly
571
- />
572
- <TextInput
573
- id="txtordertime"
574
- labelText="Order Time"
575
- value={displayOrderTime}
576
- readOnly
577
- />
578
- </div>
557
+ <div className={styles.formGroupCol}>
558
+ <label className={styles.labelCompact}>Order By</label>
559
+ <input type="text" className={styles.formControl} value={displayOrderBy} readOnly />
560
+ </div>
561
+
562
+ <div className={styles.formGroupCol}>
563
+ <label className={styles.labelCompact}>Order Date</label>
564
+ <input type="text" className={styles.formControl} value={displayOrderDate} readOnly />
565
+ </div>
566
+
567
+ <div className={styles.formGroupCol}>
568
+ <label className={styles.labelCompact}>Order Time</label>
569
+ <input type="text" className={styles.formControl} value={displayOrderTime} readOnly />
570
+ </div>
579
571
 
580
- <hr style={{ border: 'none', height: '1px', backgroundColor: '#e0e0e0', margin: '20px 0' }} />
581
-
582
- {/* User inputs */}
583
- <div className={styles.formGrid}>
584
- <TextInput
585
- id="txtdate"
586
- labelText="Date"
587
- type="date"
588
- value={saveDate}
589
- onChange={(e) => setSaveDate(e.target.value)}
590
- />
591
- <TextInput
592
- id="txttime"
593
- labelText="Time"
594
- type="time"
595
- value={saveTime}
596
- onChange={(e) => setSaveTime(e.target.value)}
597
- />
598
- <Select
599
- id="drpstatus"
600
- labelText="Collection Status"
601
- value={saveStatus}
602
- onChange={(e) => setSaveStatus(e.target.value)}
603
- >
604
- <SelectItem value="0" text="--Select--" />
605
- <SelectItem value="Done" text="Done" />
606
- <SelectItem value="Not Done" text="Not Done" />
607
- </Select>
608
572
  </div>
609
573
 
610
- <div style={{ marginBottom: '2rem' }}>
611
- <TextArea
612
- id="txtnote"
613
- labelText="Remark"
614
- rows={3}
615
- value={saveRemark}
616
- onChange={(e) => setSaveRemark(e.target.value)}
617
- />
574
+ <hr style={{ border: 'none', height: '1px', backgroundColor: '#000', margin: '20px 0' }} />
575
+
576
+ {/* Date, Time, Status fields */}
577
+ <div className={styles.row} style={{ marginBottom: '1rem' }}>
578
+
579
+ <div className={styles.formGroupCol}>
580
+ <label className={styles.labelCompact}>Date</label>
581
+ <input
582
+ type="date"
583
+ className={styles.formControl}
584
+ value={saveDate}
585
+ onChange={(e) => setSaveDate(e.target.value)}
586
+ />
587
+ </div>
588
+
589
+ <div className={styles.formGroupCol}>
590
+ <label className={styles.labelCompact}>Time</label>
591
+ <input
592
+ type="time"
593
+ className={styles.formControl}
594
+ value={saveTime}
595
+ onChange={(e) => setSaveTime(e.target.value)}
596
+ />
597
+ </div>
598
+
599
+ <div className={styles.formGroupCol}>
600
+ <label className={styles.labelCompact}>Collection Status</label>
601
+ <select
602
+ className={styles.formControl}
603
+ value={saveStatus}
604
+ onChange={(e) => setSaveStatus(e.target.value)}
605
+ >
606
+ <option value="0">--Select--</option>
607
+ <option value="Done">Done</option>
608
+ <option value="Not Done">Not Done</option>
609
+ </select>
610
+ </div>
611
+
618
612
  </div>
619
613
 
620
- {/* Action buttons */}
621
- <div className={styles.actionsContainer}>
622
- <Button
623
- kind="danger"
624
- onClick={() => navigateTo('/openmrs/spa/samplecollection')}
625
- disabled={saving}
626
- >
627
- Back
628
- </Button>
629
- <Button
630
- kind="primary"
631
- onClick={handleSave}
632
- disabled={saving || testList.length === 0}
633
- >
634
- {saving ? 'Saving...' : 'Save'}
635
- </Button>
614
+ {/* Remark text box and Action buttons row */}
615
+ <div className={styles.row}>
616
+
617
+ <div className={styles.formGroupCol} style={{ flex: '0 0 66.6666%', maxWidth: '66.6666%', alignItems: 'flex-start' }}>
618
+ <label className={styles.labelCompact}>Remark</label>
619
+ <textarea
620
+ className={`${styles.formControl} ${styles.textareaControl}`}
621
+ rows={3}
622
+ value={saveRemark}
623
+ onChange={(e) => setSaveRemark(e.target.value)}
624
+ />
625
+ </div>
626
+
627
+ <div className={`${styles.formGroupCol} ${styles.justifyContentEnd}`} style={{ flex: '0 0 33.3333%', maxWidth: '33.3333%', alignSelf: 'flex-end' }}>
628
+ <button
629
+ type="button"
630
+ className={`${styles.btn} ${styles.btnSuccess} ${styles.mx2}`}
631
+ style={{ height: '40px', width: '100px' }}
632
+ disabled={saving || testList.length === 0}
633
+ onClick={handleSave}
634
+ >
635
+ {saving ? 'Saving...' : 'Save'}
636
+ </button>
637
+ <button
638
+ type="button"
639
+ className={`${styles.btn} ${styles.btnDanger}`}
640
+ style={{ height: '40px', width: '100px' }}
641
+ disabled={saving}
642
+ onClick={() => navigateTo('/openmrs/spa/samplecollection')}
643
+ >
644
+ Back
645
+ </button>
646
+ </div>
647
+
636
648
  </div>
649
+
637
650
  </div>
638
- </Tile>
651
+ </div>
639
652
  </div>
640
653
  );
641
654
  };