File size: 4,738 Bytes
05b4ab1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
121
122
123
124
125
126
127
128
129
130
// src/components/InputPanel.jsx
import React, { useRef, useState } from 'react'

export default function InputPanel({ text, onTextChange, onImageChange, onAnalyze, loading, imagePreview }) {
  const fileRef = useRef(null)
  const [dragOver, setDragOver] = useState(false)

  function handleDrop(e) {
    e.preventDefault()
    setDragOver(false)
    const file = e.dataTransfer.files[0]
    if (file && file.type.startsWith('image/')) onImageChange(file)
  }

  function handleFile(e) {
    const file = e.target.files[0]
    if (file) onImageChange(file)
  }

  return (
    <div className="card overflow-hidden">
      <div className="panel-header">
        <span className="w-[7px] h-[7px] rounded-full bg-[#cfe2f7] flex-shrink-0" />
        <span>Input Stream</span>
        <span className="ml-auto font-mono text-[9px] text-muted">Vision + NLP</span>
      </div>

      <div className="p-4 flex flex-col gap-4">
        {/* Image drop zone */}
        <div
          className={`relative border rounded-lg transition-all duration-200 cursor-pointer overflow-hidden
            ${dragOver
              ? 'border-teal bg-[#0ec9a815]'
              : 'border-[#1a3a6e50] hover:border-[#1a3a6e] bg-[#081527]'}`}
          style={{ minHeight: '120px' }}
          onClick={() => fileRef.current?.click()}
          onDragOver={e => { e.preventDefault(); setDragOver(true) }}
          onDragLeave={() => setDragOver(false)}
          onDrop={handleDrop}
        >
          <input
            ref={fileRef}
            type="file"
            accept="image/*"
            className="hidden"
            onChange={handleFile}
          />
          {imagePreview ? (
            <img
              src={imagePreview}
              alt="Face preview"
              className="w-full h-full object-cover"
              style={{ maxHeight: '200px' }}
            />
          ) : (
            <div className="flex flex-col items-center justify-center gap-2 py-8 text-center">
              <div className="font-mono text-3xl opacity-30"></div>
              <p className="font-mono text-[11px] text-muted">Drop face image or click to upload</p>
              <p className="font-mono text-[9px] text-[#3a5a7a]">JPEG · PNG · WEBP</p>
            </div>
          )}
          {/* scanline overlay while no image */}
          {!imagePreview && (
            <div className="absolute inset-0 pointer-events-none overflow-hidden">
              <div className="scanline-animate absolute left-0 right-0 h-px bg-teal opacity-20" />
            </div>
          )}
        </div>

        {/* Text area */}
        <div className="flex flex-col gap-1">
          <label className="font-mono text-[10px] text-muted tracking-[0.1em] uppercase">
            Transcript / Message
          </label>
          <textarea
            value={text}
            onChange={e => onTextChange(e.target.value)}
            placeholder="Enter customer speech or chat message…"
            rows={4}
            className="
              w-full bg-[#081527] border border-[#1a3a6e40] rounded-lg p-3
              font-mono text-[12px] text-[#cfe2f7] leading-[1.8] resize-none
              placeholder:text-[#2a4a6a] outline-none
              focus:border-teal focus:bg-[#08182e]
              transition-all duration-200
            "
          />
        </div>

        {/* Analyze button */}
        <button
          onClick={onAnalyze}
          disabled={loading || (!text.trim())}
          className="
            relative overflow-hidden w-full py-3 rounded-md
            font-display font-semibold text-sm
            transition-all duration-200
            disabled:opacity-40 disabled:cursor-not-allowed
            enabled:cursor-pointer
          "
          style={{
            background: loading
              ? 'linear-gradient(135deg, #0a6b5b, #0a4b7a)'
              : 'linear-gradient(135deg, #0ec9a8, #0a8aff)',
            color: '#050d1b',
          }}
        >
          {loading ? (
            <span className="flex items-center justify-center gap-2">
              <span className="inline-block w-3 h-3 border-2 border-[#050d1b] border-t-transparent rounded-full animate-spin" />
              Analyzing…
            </span>
          ) : (
            '▶  Analyze Emotion'
          )}
          {/* shimmer on idle */}
          {!loading && (
            <div className="absolute inset-0 bg-white opacity-0 hover:opacity-10 transition-opacity duration-200" />
          )}
        </button>

        {/* API status hint */}
        <p className="font-mono text-[9px] text-[#2a4a6a] text-center">
          Demo mode active — connects to FastAPI on :8000 when available
        </p>
      </div>
    </div>
  )
}