components/question-confirmation.tsx
Rama main · réplica de lectura del repositorio original.
'use client'
import { useState } from 'react'
import {
IconArrowRight as ArrowRight,
IconCheck as Check,
IconPlayerTrackNext as SkipForward
} from '@tabler/icons-react'
import type { ToolPart } from '@/lib/types/ai'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Checkbox } from '@/components/ui/checkbox'
import { Input } from '@/components/ui/input'
interface QuestionConfirmationProps {
toolInvocation: ToolPart<'askQuestion'>
onConfirm: (toolCallId: string, approved: boolean, response?: any) => void
isCompleted?: boolean
}
interface QuestionOption {
value: string
label: string
}
interface QuestionInput {
question: string
options: QuestionOption[]
allowsInput?: boolean
inputLabel?: string
inputPlaceholder?: string
}
interface QuestionOutput {
selectedOptions?: string[]
inputText?: string
skipped?: boolean
}
export function QuestionConfirmation({
toolInvocation,
onConfirm,
isCompleted = false
}: QuestionConfirmationProps) {
const input = (toolInvocation.input || {}) as QuestionInput
const {
question = '',
options = [],
allowsInput = false,
inputLabel = '',
inputPlaceholder = ''
} = input
// Get result data if available
const resultData =
toolInvocation.state === 'output-available' && toolInvocation.output
? toolInvocation.output
: null
const [selectedOptions, setSelectedOptions] = useState<string[]>([])
const [inputText, setInputText] = useState('')
const [completed, setCompleted] = useState(isCompleted)
const [skipped, setSkipped] = useState(false)
const isButtonDisabled =
selectedOptions.length === 0 && (!allowsInput || inputText.trim() === '')
const handleOptionChange = (label: string) => {
setSelectedOptions(prev => {
if (prev.includes(label)) {
return prev.filter(item => item !== label)
} else {
return [...prev, label]
}
})
}
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputText(e.target.value)
}
const handleSkip = () => {
setSkipped(true)
setCompleted(true)
onConfirm(toolInvocation.toolCallId, false, { skipped: true })
}
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
const response = {
selectedOptions,
inputText: inputText.trim(),
question
}
onConfirm(toolInvocation.toolCallId, true, response)
setCompleted(true)
}
// Get options to display (from result or local state)
const getDisplayedOptions = (): string[] => {
const result = resultData as QuestionOutput | null
if (result && Array.isArray(result.selectedOptions)) {
return result.selectedOptions
}
return selectedOptions
}
// Get input text to display (from result or local state)
const getDisplayedInputText = (): string => {
const result = resultData as QuestionOutput | null
if (result && result.inputText) {
return result.inputText
}
return inputText
}
// Check if question was skipped
const wasSkipped = (): boolean => {
const result = resultData as QuestionOutput | null
if (result && result.skipped) {
return true
}
return skipped
}
const updatedQuery = () => {
// If skipped, show skipped message
if (wasSkipped()) {
return 'Question skipped'
}
const displayOptions = getDisplayedOptions()
const displayInputText = getDisplayedInputText()
const optionsText =
displayOptions.length > 0 ? `Selected: ${displayOptions.join(', ')}` : ''
const inputTextDisplay =
displayInputText.trim() !== '' ? `Input: ${displayInputText}` : ''
return [optionsText, inputTextDisplay].filter(Boolean).join(' | ')
}
// Show result view if completed or if tool has result state
if (completed || toolInvocation.state === 'output-available') {
const isSkipped = wasSkipped()
return (
<Card className="p-3 md:p-4 w-full flex flex-col justify-between items-center gap-2">
<CardTitle className="text-base font-medium text-muted-foreground w-full">
{question}
</CardTitle>
<div className="flex items-center justify-start gap-1 w-full">
{isSkipped ? (
<SkipForward size={16} className="text-yellow-500 w-4 h-4" />
) : (
<Check size={16} className="text-green-500 w-4 h-4" />
)}
<h5 className="text-muted-foreground text-xs truncate">
{updatedQuery()}
</h5>
</div>
</Card>
)
}
return (
<Card>
<CardHeader>
<CardTitle className="text-lg">{question}</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="flex flex-wrap justify-start mb-4">
{options &&
options.map((option: QuestionOption, index: number) => (
<div
key={`option-${index}`}
className="flex items-center space-x-1.5 mb-2"
>
<Checkbox
id={option.value}
checked={selectedOptions.includes(option.label)}
onCheckedChange={() => handleOptionChange(option.label)}
/>
<label
className="text-sm whitespace-nowrap pr-4"
htmlFor={option.value}
>
{option.label}
</label>
</div>
))}
</div>
{allowsInput && (
<div className="mb-6 flex flex-col space-y-2 text-sm">
<label className="text-muted-foreground" htmlFor="query">
{inputLabel}
</label>
<Input
type="text"
name="additional_query"
className="w-full"
id="query"
placeholder={inputPlaceholder}
value={inputText}
onChange={handleInputChange}
/>
</div>
)}
<div className="flex justify-end space-x-2">
<Button type="button" variant="outline" onClick={handleSkip}>
<SkipForward size={16} className="mr-1" />
Skip
</Button>
<Button type="submit" disabled={isButtonDisabled}>
<ArrowRight size={16} className="mr-1" />
Send
</Button>
</div>
</form>
</CardContent>
</Card>
)
}