File size: 5,260 Bytes
764531e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// ChatCard.jsx
import { useState, useRef, useEffect } from 'react'

export default function ChatCard({ chatHistory, onSend, loading }) {
  const [input, setInput]     = useState('')
  const [collapsed, setCollapsed] = useState(false)
  const bottomRef             = useRef(null)

  // Auto-scroll to bottom on new message
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [chatHistory])

  function handleSend() {
    const msg = input.trim()
    if (!msg || loading) return
    setInput('')
    onSend(msg)
  }

  function handleKeyDown(e) {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault()
      handleSend()
    }
  }

  return (
    <div style={s.card}>
      <div style={s.header} onClick={() => setCollapsed(c => !c)}>
        <span style={s.title}>Chat</span>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={s.chevron}>{collapsed ? '▶' : '▼'}</span>
        </div>
      </div>

      {!collapsed && (
        <>
          <div style={s.messages}>
            {chatHistory.length === 0 && (
              <div style={s.empty}>
                Stellen Sie Fragen zum Patienten auf Basis dieses Dokuments.
              </div>
            )}
            {chatHistory.map((msg, i) => (
              <div key={i} style={s.msgWrapper}>
                <div style={msg.role === 'user' ? s.labelUser : s.labelAi}>
                  {msg.role === 'user' ? 'Arzt' : 'System'}
                </div>
                <div style={msg.role === 'user' ? s.bubbleUser : s.bubbleAi}>
                  {msg.content}
                </div>
              </div>
            ))}

            {/* Loading bubble */}
            {loading && (
              <div style={s.msgWrapper}>
                <div style={s.labelAi}>System</div>
                <div style={s.bubbleAi}>
                  <div style={s.typingDots}>
                    <span style={{ ...s.dot, animationDelay: '0ms' }} />
                    <span style={{ ...s.dot, animationDelay: '150ms' }} />
                    <span style={{ ...s.dot, animationDelay: '300ms' }} />
                  </div>
                </div>
              </div>
            )}

            <div ref={bottomRef} />
          </div>

          <div style={s.note}>
            Die Antworten basieren ausschließlich auf Patientendokumente und extrahierten Daten. (In dieser Version ohne Abruf medizinischer Leitlinien oder Fachliteratur)
          </div>

          <div style={s.inputRow}>
            <textarea
              style={s.input}
              placeholder="Frage zum Patienten stellen… (Enter zum Senden)"
              value={input}
              onChange={e => setInput(e.target.value)}
              onKeyDown={handleKeyDown}
              rows={2}
            />
            <button
              style={{ ...s.sendBtn, opacity: !input.trim() || loading ? 0.5 : 1 }}
              disabled={!input.trim() || !!loading}
              onClick={handleSend}
            >

            </button>
          </div>
        </>
      )}
    </div>
  )
}

// Styles 
const s = {
  card:       { borderRadius:10, border:'0.5px solid #E2E0D8', background:'#fff', overflow:'hidden' },
  header:     { display:'flex', alignItems:'center', justifyContent:'space-between', padding:'10px 14px', cursor:'pointer', userSelect:'none' },
  title:      { fontSize:13, fontWeight:500 },
  hint:       { fontSize:10, color:'#A09D94', fontStyle:'italic' },
  chevron:    { fontSize:10, color:'#A09D94' },
  messages:   { maxHeight:320, overflowY:'auto', padding:'12px 14px', display:'flex', flexDirection:'column', gap:10, borderTop:'0.5px solid #E2E0D8' },
  empty:      { fontSize:12, color:'#A09D94', textAlign:'center', padding:'16px 0' },
  msgWrapper: { display:'flex', flexDirection:'column', gap:3 },
  labelUser:  { fontSize:10, color:'#A09D94', textTransform:'uppercase', letterSpacing:'0.04em' },
  labelAi:    { fontSize:10, color:'#A09D94', textTransform:'uppercase', letterSpacing:'0.04em' },
  bubbleUser: { fontSize:12, lineHeight:1.6, padding:'8px 11px', borderRadius:8, background:'#EFF6FF', color:'#1E40AF', alignSelf:'flex-start', maxWidth:'90%' },
  bubbleAi:   { fontSize:12, lineHeight:1.6, padding:'8px 11px', borderRadius:8, background:'#F7F6F3', color:'#1A1916', border:'0.5px solid #E2E0D8', alignSelf:'flex-start', maxWidth:'90%' },
  note:       { fontSize:10, color:'#A09D94', fontStyle:'italic', padding:'6px 14px', borderTop:'0.5px solid #E2E0D8' },
  inputRow:   { display:'flex', gap:8, padding:'10px 14px', borderTop:'0.5px solid #E2E0D8', alignItems:'flex-end' },
  input:      { flex:1, fontFamily:'inherit', fontSize:12, padding:'7px 10px', borderRadius:6, border:'0.5px solid #C8C6BC', background:'#F7F6F3', color:'#1A1916', resize:'none', lineHeight:1.5, outline:'none' },
  sendBtn:    { fontFamily:'inherit', fontSize:13, padding:'7px 14px', borderRadius:6, border:'0.5px solid #1A1916', background:'#1A1916', color:'#fff', cursor:'pointer', flexShrink:0 },
  typingDots: { display:'flex', gap:4, alignItems:'center', padding:'2px 0' },
  dot:        { width:6, height:6, borderRadius:'50%', background:'#A09D94', animation:'pulse 1s ease-in-out infinite' },
}