scribe-widget 1.0.14 → 1.0.16
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.
- package/dist/scribe-widget.es.js +49 -15
- package/dist/scribe-widget.umd.js +2 -2
- package/package.json +1 -1
- package/src/App.tsx +13 -1
- package/src/components/EMRListState.tsx +50 -0
- package/src/components/ResultsState.tsx +30 -3
- package/src/hooks/useScribeSession.ts +1 -1
- package/src/styles/widget.css +131 -1
- package/src/types.ts +2 -1
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -55,6 +55,12 @@ export function App({ config: initialConfig, onClose }: AppProps) {
|
|
|
55
55
|
setCredentials(null);
|
|
56
56
|
}, [reset]);
|
|
57
57
|
|
|
58
|
+
// Handle EMR selection
|
|
59
|
+
const handleSelectEMR = useCallback((emrId: string) => {
|
|
60
|
+
console.log('Selected EMR:', emrId);
|
|
61
|
+
// TODO: Handle EMR selection logic
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
58
64
|
if (isMinimized) {
|
|
59
65
|
return null;
|
|
60
66
|
}
|
|
@@ -98,7 +104,13 @@ export function App({ config: initialConfig, onClose }: AppProps) {
|
|
|
98
104
|
return <ProcessingState />;
|
|
99
105
|
|
|
100
106
|
case 'results':
|
|
101
|
-
return result ?
|
|
107
|
+
return result ? (
|
|
108
|
+
<ResultsState
|
|
109
|
+
result={result}
|
|
110
|
+
onNewRecording={handleStartNewRecording}
|
|
111
|
+
onSelectEMR={handleSelectEMR}
|
|
112
|
+
/>
|
|
113
|
+
) : null;
|
|
102
114
|
|
|
103
115
|
case 'polling_error':
|
|
104
116
|
return (
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
interface EMRListStateProps {
|
|
2
|
+
onSelectEMR: (emrId: string) => void;
|
|
3
|
+
onNewRecording: () => void;
|
|
4
|
+
onBack: () => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const EMR_LIST = [
|
|
8
|
+
{ id: 'eka_emr', name: 'Eka EMR', description: 'Eka Care Electronic Medical Records' },
|
|
9
|
+
{ id: 'open_emr', name: 'OpenEMR', description: 'Open-source Electronic Health Records' },
|
|
10
|
+
{ id: 'open_mrs', name: 'OpenMRS', description: 'Open Medical Record System' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export function EMRListState({ onSelectEMR, onNewRecording, onBack }: EMRListStateProps) {
|
|
14
|
+
return (
|
|
15
|
+
<div className="emr-list-state">
|
|
16
|
+
<div className="emr-list-header">
|
|
17
|
+
<button className="back-btn" onClick={onBack} title="Back">
|
|
18
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
19
|
+
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
20
|
+
</svg>
|
|
21
|
+
</button>
|
|
22
|
+
<span className="emr-list-title">Integrated EMRs</span>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div className="emr-list-content">
|
|
26
|
+
{EMR_LIST.map((emr) => (
|
|
27
|
+
<button
|
|
28
|
+
key={emr.id}
|
|
29
|
+
className="emr-item"
|
|
30
|
+
onClick={() => onSelectEMR(emr.id)}
|
|
31
|
+
>
|
|
32
|
+
<div className="emr-item-info">
|
|
33
|
+
<span className="emr-item-name">{emr.name}</span>
|
|
34
|
+
<span className="emr-item-desc">{emr.description}</span>
|
|
35
|
+
</div>
|
|
36
|
+
<svg className="emr-item-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
37
|
+
<path d="M9 18l6-6-6-6"/>
|
|
38
|
+
</svg>
|
|
39
|
+
</button>
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div className="emr-list-footer">
|
|
44
|
+
<button className="start-new-recording-btn" onClick={onNewRecording}>
|
|
45
|
+
Start New Recording
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { GetSessionStatusResponse } from 'med-scribe-alliance-ts-sdk';
|
|
2
2
|
|
|
3
|
+
const EMR_LIST = [
|
|
4
|
+
{ id: 'eka_emr', name: 'Eka EMR', description: 'Eka Care Electronic Medical Records' },
|
|
5
|
+
{ id: 'open_emr', name: 'OpenEMR', description: 'Open-source Electronic Health Records' },
|
|
6
|
+
{ id: 'open_mrs', name: 'OpenMRS', description: 'Open Medical Record System' },
|
|
7
|
+
];
|
|
8
|
+
|
|
3
9
|
interface ResultsStateProps {
|
|
4
10
|
result: GetSessionStatusResponse;
|
|
5
11
|
onNewRecording: () => void;
|
|
12
|
+
onSelectEMR: (emrId: string) => void;
|
|
6
13
|
}
|
|
7
14
|
|
|
8
|
-
export function ResultsState({ result, onNewRecording }: ResultsStateProps) {
|
|
15
|
+
export function ResultsState({ result, onNewRecording, onSelectEMR }: ResultsStateProps) {
|
|
16
|
+
console.log('prescription result - WIDGET', result);
|
|
9
17
|
return (
|
|
10
18
|
<div className="results-state">
|
|
11
19
|
<div className="results-header">
|
|
@@ -17,8 +25,27 @@ export function ResultsState({ result, onNewRecording }: ResultsStateProps) {
|
|
|
17
25
|
<div className="results-content">
|
|
18
26
|
<div className="transcript-section">
|
|
19
27
|
<div className="section-title">Transcript</div>
|
|
20
|
-
<div className="transcript-text">
|
|
21
|
-
|
|
28
|
+
<div className="transcript-text">{result.transcript || 'No transcript available.'}</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div className="emr-section">
|
|
32
|
+
<div className="section-title">Integrated EMRs</div>
|
|
33
|
+
<div className="emr-list-inline">
|
|
34
|
+
{EMR_LIST.map((emr) => (
|
|
35
|
+
<button
|
|
36
|
+
key={emr.id}
|
|
37
|
+
className="emr-item"
|
|
38
|
+
onClick={() => onSelectEMR(emr.id)}
|
|
39
|
+
>
|
|
40
|
+
<div className="emr-item-info">
|
|
41
|
+
<span className="emr-item-name">{emr.name}</span>
|
|
42
|
+
<span className="emr-item-desc">{emr.description}</span>
|
|
43
|
+
</div>
|
|
44
|
+
<svg className="emr-item-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
45
|
+
<path d="M9 18l6-6-6-6"/>
|
|
46
|
+
</svg>
|
|
47
|
+
</button>
|
|
48
|
+
))}
|
|
22
49
|
</div>
|
|
23
50
|
</div>
|
|
24
51
|
</div>
|
|
@@ -170,7 +170,7 @@ export function useScribeSession(config: ScribeWidgetConfig): UseScribeSessionRe
|
|
|
170
170
|
setState('recording');
|
|
171
171
|
|
|
172
172
|
await clientRef.current.startRecording({
|
|
173
|
-
templates:
|
|
173
|
+
templates: ['eka_emr_template'],
|
|
174
174
|
languageHint: config.languageHint,
|
|
175
175
|
});
|
|
176
176
|
|
package/src/styles/widget.css
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.05);
|
|
19
19
|
z-index: 2147483647;
|
|
20
20
|
overflow: hidden;
|
|
21
|
-
|
|
21
|
+
width: 380px;
|
|
22
|
+
max-width: calc(100vw - 40px);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
.panel-header {
|
|
@@ -545,3 +546,132 @@
|
|
|
545
546
|
background: #f3f4f6;
|
|
546
547
|
color: #374151;
|
|
547
548
|
}
|
|
549
|
+
|
|
550
|
+
/* EMR Section (inline on results) */
|
|
551
|
+
.emr-section {
|
|
552
|
+
margin-top: 16px;
|
|
553
|
+
padding-top: 16px;
|
|
554
|
+
border-top: 1px solid #e5e7eb;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.emr-list-inline {
|
|
558
|
+
display: flex;
|
|
559
|
+
flex-direction: column;
|
|
560
|
+
gap: 8px;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/* EMR List State */
|
|
564
|
+
.emr-list-state {
|
|
565
|
+
display: flex;
|
|
566
|
+
flex-direction: column;
|
|
567
|
+
gap: 16px;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.emr-list-header {
|
|
571
|
+
display: flex;
|
|
572
|
+
align-items: center;
|
|
573
|
+
gap: 12px;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.back-btn {
|
|
577
|
+
background: none;
|
|
578
|
+
border: none;
|
|
579
|
+
cursor: pointer;
|
|
580
|
+
padding: 4px;
|
|
581
|
+
color: #6b7280;
|
|
582
|
+
display: flex;
|
|
583
|
+
align-items: center;
|
|
584
|
+
justify-content: center;
|
|
585
|
+
width: 32px;
|
|
586
|
+
height: 32px;
|
|
587
|
+
border-radius: 6px;
|
|
588
|
+
transition: all 0.2s;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.back-btn:hover {
|
|
592
|
+
background: #f3f4f6;
|
|
593
|
+
color: #374151;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.back-btn svg {
|
|
597
|
+
width: 20px;
|
|
598
|
+
height: 20px;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.emr-list-title {
|
|
602
|
+
font-size: 16px;
|
|
603
|
+
font-weight: 600;
|
|
604
|
+
color: #111827;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.emr-list-content {
|
|
608
|
+
display: flex;
|
|
609
|
+
flex-direction: column;
|
|
610
|
+
gap: 8px;
|
|
611
|
+
max-height: 240px;
|
|
612
|
+
overflow-y: auto;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.emr-item {
|
|
616
|
+
display: flex;
|
|
617
|
+
align-items: center;
|
|
618
|
+
justify-content: space-between;
|
|
619
|
+
padding: 12px 16px;
|
|
620
|
+
background: #f9fafb;
|
|
621
|
+
border: 1px solid #e5e7eb;
|
|
622
|
+
border-radius: 8px;
|
|
623
|
+
cursor: pointer;
|
|
624
|
+
transition: all 0.2s;
|
|
625
|
+
text-align: left;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.emr-item:hover {
|
|
629
|
+
background: #f3f4f6;
|
|
630
|
+
border-color: #d1d5db;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.emr-item-info {
|
|
634
|
+
display: flex;
|
|
635
|
+
flex-direction: column;
|
|
636
|
+
gap: 2px;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.emr-item-name {
|
|
640
|
+
font-size: 14px;
|
|
641
|
+
font-weight: 500;
|
|
642
|
+
color: #111827;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.emr-item-desc {
|
|
646
|
+
font-size: 12px;
|
|
647
|
+
color: #6b7280;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.emr-item-arrow {
|
|
651
|
+
width: 20px;
|
|
652
|
+
height: 20px;
|
|
653
|
+
color: #9ca3af;
|
|
654
|
+
flex-shrink: 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.emr-list-footer {
|
|
658
|
+
padding-top: 12px;
|
|
659
|
+
border-top: 1px solid #e5e7eb;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.start-new-recording-btn {
|
|
663
|
+
width: 100%;
|
|
664
|
+
background: #2563eb;
|
|
665
|
+
color: white;
|
|
666
|
+
border: none;
|
|
667
|
+
padding: 12px 20px;
|
|
668
|
+
border-radius: 8px;
|
|
669
|
+
font-size: 14px;
|
|
670
|
+
font-weight: 500;
|
|
671
|
+
cursor: pointer;
|
|
672
|
+
transition: background 0.2s;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.start-new-recording-btn:hover {
|
|
676
|
+
background: #1d4ed8;
|
|
677
|
+
}
|