103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import React, { useState, useEffect, type FormEvent, type KeyboardEvent } from 'react'
|
|
|
|
export interface QueryInputProps {
|
|
onSubmit: (question: string) => void
|
|
onHalfQuestion?: (question: string) => void
|
|
isLoading: boolean
|
|
partialText?: string
|
|
value?: string
|
|
}
|
|
|
|
export const QueryInput: React.FC<QueryInputProps> = ({ onSubmit, onHalfQuestion, isLoading, partialText, value }) => {
|
|
const [question, setQuestion] = useState<string>('')
|
|
const [submittedQuestion, setSubmittedQuestion] = useState<string | null>(null)
|
|
const [hasUserInput, setHasUserInput] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (value !== undefined) {
|
|
setQuestion(value)
|
|
setHasUserInput(false)
|
|
}
|
|
}, [value])
|
|
|
|
const displayValue = hasUserInput ? question : (partialText || question)
|
|
const showPartialStyle = !hasUserInput && !!partialText
|
|
|
|
const handleSubmit = (e: FormEvent): void => {
|
|
e.preventDefault()
|
|
const trimmed = question.trim()
|
|
if (trimmed && !isLoading) {
|
|
onSubmit(trimmed)
|
|
setSubmittedQuestion(trimmed)
|
|
setQuestion('')
|
|
setHasUserInput(false)
|
|
}
|
|
}
|
|
|
|
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>): void => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault()
|
|
handleSubmit(e)
|
|
}
|
|
}
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>): void => {
|
|
setQuestion(e.target.value)
|
|
setHasUserInput(true)
|
|
if (e.target.value.trim() !== '') {
|
|
setSubmittedQuestion(null)
|
|
}
|
|
}
|
|
|
|
const isDisabled = isLoading || question.trim() === ''
|
|
|
|
const textareaClassName = [
|
|
'w-full rounded border border-gray-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed',
|
|
'',
|
|
].filter(Boolean).join(' ')
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="space-y-3">
|
|
<textarea
|
|
value={displayValue}
|
|
onChange={handleChange}
|
|
onKeyDown={handleKeyDown}
|
|
placeholder="Ask a question about your documents..."
|
|
disabled={isLoading}
|
|
rows={3}
|
|
className={textareaClassName}
|
|
/>
|
|
<div className="flex items-center gap-3">
|
|
{onHalfQuestion && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
const trimmed = question.trim()
|
|
if (trimmed && !isLoading) {
|
|
onHalfQuestion(trimmed)
|
|
setSubmittedQuestion(trimmed)
|
|
}
|
|
}}
|
|
disabled={isDisabled}
|
|
className="shrink-0 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
Half Question
|
|
</button>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={isDisabled}
|
|
className="shrink-0 px-4 py-2 bg-blue-600 text-white font-medium rounded hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-blue-600 transition-all duration-200"
|
|
>
|
|
{isLoading ? 'Processing...' : 'Final Submit'}
|
|
</button>
|
|
{submittedQuestion && (
|
|
<p data-testid="submitted-question" className="text-sm text-gray-500 italic break-words">
|
|
Your question: “{submittedQuestion}”
|
|
</p>
|
|
)}
|
|
</div>
|
|
</form>
|
|
)
|
|
}
|