@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,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"/>
@@ -141,8 +124,11 @@ const SamplecollectionRoom: React.FC = () => {
141
124
  personAttrData.attributes.forEach((attr: any) => {
142
125
  if (attr.display) {
143
126
  const [key, value] = attr.display.split('=');
144
- if (key && value && key.trim() === 'Institute Id') {
145
- locUuid = value.trim();
127
+ if (key && value) {
128
+ const cleanKey = key.trim();
129
+ if (cleanKey === 'Institute Id' || cleanKey === 'Institute') {
130
+ locUuid = value.trim();
131
+ }
146
132
  }
147
133
  }
148
134
  });
@@ -419,223 +405,253 @@ const SamplecollectionRoom: React.FC = () => {
419
405
  }
420
406
  };
421
407
 
422
- if (loading) return <Loading description="Loading page details..." withOverlay={true} />;
408
+ if (loading) {
409
+ return (
410
+ <div className={styles.container} style={{ textAlign: 'center', padding: '50px' }}>
411
+ Loading patient details...
412
+ </div>
413
+ );
414
+ }
423
415
 
424
416
  return (
425
417
  <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>
418
+ {/* Clinic/Hospital Header */}
419
+ <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '10px' }}>
420
+ <div className={styles.locationBadge} style={{ background: '#e9ecef', color: '#495057' }}>
421
+ <span className="icon-hospital" />
422
+ <span id="lblInstituteName" style={{ fontWeight: 'bold' }}>{locationName}</span>
436
423
  </div>
424
+ </div>
437
425
 
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>
426
+ <div className={styles.card}>
427
+ <div className={styles.cardBody}>
428
+
429
+ {/* Section title */}
430
+ <div className={styles.col12} style={{ marginBottom: '1rem', padding: 0 }}>
431
+ <h2 className={styles.heading} style={{ margin: 0 }}>
432
+ {t('samplecollectionRoomHeading', 'Sample Collection Room')}
433
+ </h2>
434
+ </div>
435
+
436
+ {/* Messages */}
437
+ {errorMsg && (
438
+ <div style={{ padding: '8px', color: '#721c24', backgroundColor: '#f8d7da', border: '1px solid #f5c6cb', borderRadius: '4px', marginBottom: '10px' }}>
439
+ {errorMsg}
468
440
  </div>
469
- <div className={styles.patientField}>
470
- <span className={styles.fieldLabel}>PHN No</span>
471
- <span className={styles.fieldValue}>{phnNo || 'N/A'}</span>
441
+ )}
442
+ {successMsg && (
443
+ <div style={{ padding: '8px', color: '#155724', backgroundColor: '#d4edda', border: '1px solid #c3e6cb', borderRadius: '4px', marginBottom: '10px' }}>
444
+ {successMsg}
472
445
  </div>
473
- <div className={styles.patientField}>
474
- <span className={styles.fieldLabel}>Age</span>
475
- <span className={styles.fieldValue}>{patientAge ? `${patientAge} years` : 'N/A'}</span>
446
+ )}
447
+
448
+ {/* Patient Card Grid (Col-10 fields, Col-2 avatar) */}
449
+ <div className={styles.row} style={{ marginBottom: '1.5rem' }}>
450
+ <div className={styles.col10}>
451
+ <div className={styles.row}>
452
+
453
+ <div className={styles.formGroupCol}>
454
+ <label className={styles.labelCompact}>Full Name</label>
455
+ <input type="text" className={styles.formControl} value={patientName} readOnly />
456
+ </div>
457
+
458
+ <div className={styles.formGroupCol}>
459
+ <label className={styles.labelCompact}>Section</label>
460
+ <input type="text" className={styles.formControl} value={initialSection} readOnly />
461
+ </div>
462
+
463
+ <div className={styles.formGroupCol}>
464
+ <label className={styles.labelCompact}>PHN No</label>
465
+ <input type="text" className={styles.formControl} value={phnNo} readOnly />
466
+ </div>
467
+
468
+ <div className={styles.formGroupCol}>
469
+ <label className={styles.labelCompact}>Age</label>
470
+ <input type="text" className={styles.formControl} value={patientAge} readOnly />
471
+ </div>
472
+
473
+ <div className={styles.formGroupCol}>
474
+ <label className={styles.labelCompact}>Gender</label>
475
+ <input type="text" className={styles.formControl} value={patientGender} readOnly />
476
+ </div>
477
+
478
+ </div>
476
479
  </div>
477
- <div className={styles.patientField}>
478
- <span className={styles.fieldLabel}>Gender</span>
479
- <span className={styles.fieldValue}>{patientGender || 'N/A'}</span>
480
+
481
+ <div className={styles.col2}>
482
+ <div className={styles.avatarContainer}>
483
+ {profileImgName ? (
484
+ <img
485
+ src={`${window.location.origin}/openmrs/owa/PatientImagesUploaded/${profileImgName}`}
486
+ alt="Patient avatar"
487
+ className={styles.uploadImg}
488
+ onError={(e) => {
489
+ (e.target as HTMLElement).style.display = 'none';
490
+ }}
491
+ />
492
+ ) : (
493
+ <UserAvatar />
494
+ )}
495
+ </div>
480
496
  </div>
481
497
  </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
- )}
498
+
499
+ {/* Pending Tests Table */}
500
+ <div className={styles.tableResponsive} style={{ maxHeight: '200px', overflowY: 'auto', border: '1px solid #dee2e6', marginBottom: '1.5rem' }}>
501
+ <table className={`${styles.table} ${styles.tableBordered} ${styles.tableHover} ${styles.tableStriped}`}>
502
+ <thead>
503
+ <tr>
504
+ <th style={{ width: '50px' }}>No</th>
505
+ <th style={{ whiteSpace: 'nowrap' }}>Test Name</th>
506
+ <th style={{ whiteSpace: 'nowrap' }}>Order By</th>
507
+ <th style={{ whiteSpace: 'nowrap' }}>Order Date/Time</th>
508
+ <th style={{ width: '40px' }}></th>
509
+ </tr>
510
+ </thead>
511
+ <tbody>
512
+ {testList.length === 0 ? (
513
+ <tr>
514
+ <td colSpan={5} style={{ textAlign: 'center', padding: '10px' }}>
515
+ No active tests pending collection.
516
+ </td>
517
+ </tr>
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];
525
+ const isChecked = selectedIds.includes(String(id));
526
+
527
+ return (
528
+ <tr key={id || index}>
529
+ <td>{index + 1}</td>
530
+ <td>{testName}</td>
531
+ <td>{orderBy}</td>
532
+ <td>{orderDateTime}</td>
533
+ <td style={{ textAlign: 'center' }}>
534
+ <input
535
+ type="checkbox"
536
+ className={styles.formControl}
537
+ style={{ width: '16px', height: '16px', margin: 0 }}
538
+ checked={isChecked}
539
+ onChange={(e) =>
540
+ handleCheckboxChange(e.target.checked, String(id), testName, String(urgent), row)
541
+ }
542
+ />
543
+ </td>
544
+ </tr>
545
+ );
546
+ })
547
+ )}
548
+ </tbody>
549
+ </table>
496
550
  </div>
497
- </div>
498
551
 
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>
552
+ {/* Test Detail Display inputs */}
553
+ <div className={styles.row} style={{ marginBottom: '1rem' }}>
554
+
555
+ <div className={styles.formGroupCol}>
556
+ <label className={styles.labelCompact}>Test Name</label>
557
+ <input type="text" className={styles.formControl} value={displayTestNames} readOnly />
558
+ </div>
550
559
 
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>
560
+ <div className={styles.formGroupCol}>
561
+ <label className={styles.labelCompact}>Order By</label>
562
+ <input type="text" className={styles.formControl} value={displayOrderBy} readOnly />
563
+ </div>
564
+
565
+ <div className={styles.formGroupCol}>
566
+ <label className={styles.labelCompact}>Order Date</label>
567
+ <input type="text" className={styles.formControl} value={displayOrderDate} readOnly />
568
+ </div>
569
+
570
+ <div className={styles.formGroupCol}>
571
+ <label className={styles.labelCompact}>Order Time</label>
572
+ <input type="text" className={styles.formControl} value={displayOrderTime} readOnly />
573
+ </div>
579
574
 
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
575
  </div>
609
576
 
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
- />
577
+ <hr style={{ border: 'none', height: '1px', backgroundColor: '#000', margin: '20px 0' }} />
578
+
579
+ {/* Date, Time, Status fields */}
580
+ <div className={styles.row} style={{ marginBottom: '1rem' }}>
581
+
582
+ <div className={styles.formGroupCol}>
583
+ <label className={styles.labelCompact}>Date</label>
584
+ <input
585
+ type="date"
586
+ className={styles.formControl}
587
+ value={saveDate}
588
+ onChange={(e) => setSaveDate(e.target.value)}
589
+ />
590
+ </div>
591
+
592
+ <div className={styles.formGroupCol}>
593
+ <label className={styles.labelCompact}>Time</label>
594
+ <input
595
+ type="time"
596
+ className={styles.formControl}
597
+ value={saveTime}
598
+ onChange={(e) => setSaveTime(e.target.value)}
599
+ />
600
+ </div>
601
+
602
+ <div className={styles.formGroupCol}>
603
+ <label className={styles.labelCompact}>Collection Status</label>
604
+ <select
605
+ className={styles.formControl}
606
+ value={saveStatus}
607
+ onChange={(e) => setSaveStatus(e.target.value)}
608
+ >
609
+ <option value="0">--Select--</option>
610
+ <option value="Done">Done</option>
611
+ <option value="Not Done">Not Done</option>
612
+ </select>
613
+ </div>
614
+
618
615
  </div>
619
616
 
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>
617
+ {/* Remark text box and Action buttons row */}
618
+ <div className={styles.row}>
619
+
620
+ <div className={styles.formGroupCol} style={{ flex: '0 0 66.6666%', maxWidth: '66.6666%', alignItems: 'flex-start' }}>
621
+ <label className={styles.labelCompact}>Remark</label>
622
+ <textarea
623
+ className={`${styles.formControl} ${styles.textareaControl}`}
624
+ rows={3}
625
+ value={saveRemark}
626
+ onChange={(e) => setSaveRemark(e.target.value)}
627
+ />
628
+ </div>
629
+
630
+ <div className={`${styles.formGroupCol} ${styles.justifyContentEnd}`} style={{ flex: '0 0 33.3333%', maxWidth: '33.3333%', alignSelf: 'flex-end' }}>
631
+ <button
632
+ type="button"
633
+ className={`${styles.btn} ${styles.btnSuccess} ${styles.mx2}`}
634
+ style={{ height: '40px', width: '100px' }}
635
+ disabled={saving || testList.length === 0}
636
+ onClick={handleSave}
637
+ >
638
+ {saving ? 'Saving...' : 'Save'}
639
+ </button>
640
+ <button
641
+ type="button"
642
+ className={`${styles.btn} ${styles.btnDanger}`}
643
+ style={{ height: '40px', width: '100px' }}
644
+ disabled={saving}
645
+ onClick={() => navigateTo('/openmrs/spa/samplecollection')}
646
+ >
647
+ Back
648
+ </button>
649
+ </div>
650
+
636
651
  </div>
652
+
637
653
  </div>
638
- </Tile>
654
+ </div>
639
655
  </div>
640
656
  );
641
657
  };