hts/apps/migrant/components/mpv2/copilot.tsx

183 lines
5.7 KiB
TypeScript

'use client'
import React, { useEffect, useState } from 'react'
import { PartialInquiry } from '@/lib/schema/inquiry'
import { Input } from '../ui-v2/input'
import { Checkbox } from '../ui-v2/checkbox'
import { Button } from '../ui-v2/button'
import { Card } from '../ui-v2/card'
import { ArrowRight, Check, FastForward, Sparkles } from 'lucide-react'
import { useActions, useStreamableValue, useUIState } from '@aigxion/isdk/rsc'
import { AI } from '@/app/[locale]/morphic/action'
import { IconLogo } from '../ui-v2/icons'
import { cn } from '@/lib/utils'
export type CopilotProps = {
inquiry?: PartialInquiry
}
export const Copilot: React.FC<CopilotProps> = ({ inquiry }: CopilotProps) => {
const [completed, setCompleted] = useState(false)
const [query, setQuery] = useState('')
const [skipped, setSkipped] = useState(false)
const [data, error, pending] = useStreamableValue<PartialInquiry>(inquiry)
const [checkedOptions, setCheckedOptions] = useState<{
[key: string]: boolean
}>({})
const [isButtonDisabled, setIsButtonDisabled] = useState(true)
const [messages, setMessages] = useUIState<typeof AI>()
const { submit } = useActions<typeof AI>()
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value)
checkIfButtonShouldBeEnabled()
}
const handleOptionChange = (selectedOption: string) => {
const updatedCheckedOptions = {
...checkedOptions,
[selectedOption]: !checkedOptions[selectedOption]
}
setCheckedOptions(updatedCheckedOptions)
checkIfButtonShouldBeEnabled(updatedCheckedOptions)
}
const checkIfButtonShouldBeEnabled = (currentOptions = checkedOptions) => {
const anyCheckboxChecked = Object.values(currentOptions).some(
checked => checked
)
setIsButtonDisabled(!(anyCheckboxChecked || query))
}
const updatedQuery = () => {
const selectedOptions = Object.entries(checkedOptions)
.filter(([, checked]) => checked)
.map(([option]) => option)
return [...selectedOptions, query].filter(Boolean).join(', ')
}
useEffect(() => {
checkIfButtonShouldBeEnabled()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query])
const onFormSubmit = async (
e: React.FormEvent<HTMLFormElement>,
skip?: boolean
) => {
e.preventDefault()
setCompleted(true)
setSkipped(skip || false)
const formData = skip
? undefined
: new FormData(e.target as HTMLFormElement)
const responseMessage = await submit(formData, skip)
setMessages(currentMessages => [...currentMessages, responseMessage])
}
const handleSkip = (e: React.MouseEvent<HTMLButtonElement>) => {
onFormSubmit(e as unknown as React.FormEvent<HTMLFormElement>, true)
}
if (error) {
return (
<Card className="p-4 w-full flex justify-between items-center">
<div className="flex items-center space-x-2">
<Sparkles className="w-4 h-4" />
<h5 className="text-muted-foreground text-xs truncate">
{`error: ${error}`}
</h5>
</div>
</Card>
)
}
if (skipped) {
return null
}
if (completed) {
return (
<Card className="p-3 md:p-4 w-full flex justify-between items-center">
<div className="flex items-center space-x-2 flex-1 min-w-0">
<IconLogo className="w-4 h-4 flex-shrink-0" />
<h5 className="text-muted-foreground text-xs truncate">
{updatedQuery()}
</h5>
</div>
<Check size={16} className="text-green-500 w-4 h-4" />
</Card>
)
} else {
return (
<Card className="p-4 rounded-lg w-full mx-auto">
<div className="flex items-center mb-4">
<IconLogo
className={cn('w-4 h-4 flex-shrink-0', { 'animate-spin': pending })}
/>
<p className="text-lg text-foreground text-semibold ml-2">
{data?.question}
</p>
</div>
<form onSubmit={onFormSubmit}>
<div className="flex flex-wrap justify-start mb-4">
{data?.options?.map((option, index) => (
<div
key={`option-${index}`}
className="flex items-center space-x-1.5 mb-2"
>
<Checkbox
id={option?.value}
name={option?.value}
onCheckedChange={() =>
handleOptionChange(option?.label as string)
}
/>
<label
className="text-sm whitespace-nowrap pr-4"
htmlFor={option?.value}
>
{option?.label}
</label>
</div>
))}
</div>
{data?.allowsInput && (
<div className="mb-6 flex flex-col space-y-2 text-sm">
<label className="text-muted-foreground" htmlFor="query">
{data?.inputLabel}
</label>
<Input
type="text"
name="additional_query"
className="w-full"
id="query"
placeholder={data?.inputPlaceholder}
value={query}
onChange={handleInputChange}
/>
</div>
)}
<div className="flex justify-end space-x-2">
<Button
type="button"
variant="outline"
onClick={handleSkip}
disabled={pending}
>
<FastForward size={16} className="mr-1" />
Skip
</Button>
<Button type="submit" disabled={isButtonDisabled || pending}>
<ArrowRight size={16} className="mr-1" />
Send
</Button>
</div>
</form>
</Card>
)
}
}