legco_ai_assistant/frontend/src/components/QueryInput.tsx

103 lines
3.5 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 = displayValue.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 || displayValue.trim() === '' || (!hasUserInput && !!partialText)
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',
showPartialStyle ? 'text-gray-400 italic' : '',
].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 = displayValue.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: &ldquo;{submittedQuestion}&rdquo;
</p>
)}
</div>
</form>
)
}