64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
import { useState } from 'react'
|
|
import { Sparkles, Loader2 } from 'lucide-react'
|
|
import { ticketsApi } from '@/api/tickets'
|
|
import type { AiParseResponse, TicketCreationPayload } from '@/types/tickets'
|
|
|
|
interface Props {
|
|
initialHint?: string
|
|
onParsed: (values: Partial<TicketCreationPayload>, parseResponse: AiParseResponse) => void
|
|
}
|
|
|
|
export function AiTicketParseForm({ initialHint = '', onParsed }: Props) {
|
|
const [prompt, setPrompt] = useState(initialHint)
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
async function handleParse() {
|
|
if (!prompt.trim()) return
|
|
setLoading(true)
|
|
setError(null)
|
|
try {
|
|
const result = await ticketsApi.aiParse(prompt)
|
|
const values: Partial<TicketCreationPayload> = {
|
|
summary: result.summary ?? undefined,
|
|
company_id: result.company_id,
|
|
board_id: result.board_id,
|
|
status_id: result.status_id,
|
|
priority_id: result.priority_id,
|
|
assigned_member_id: result.assigned_member_id,
|
|
description: result.description ?? undefined,
|
|
}
|
|
onParsed(values, result)
|
|
} catch {
|
|
setError('AI parsing failed. Please try again or use the full form.')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
<p className="text-sm text-muted-foreground">
|
|
Describe the ticket in plain language — who, what, which client, and priority.
|
|
</p>
|
|
<textarea
|
|
aria-label="Ticket description"
|
|
className="w-full bg-input border border-default rounded-[5px] px-3 py-2 text-sm text-primary placeholder:text-muted-foreground focus:border-accent focus:outline-none resize-none"
|
|
rows={4}
|
|
placeholder="e.g. Create a high priority ticket for Acme Corp — Outlook not syncing for jsmith, assign to me"
|
|
value={prompt}
|
|
onChange={e => setPrompt(e.target.value)}
|
|
/>
|
|
{error && <p className="text-xs text-danger">{error}</p>}
|
|
<button
|
|
onClick={handleParse}
|
|
disabled={!prompt.trim() || loading}
|
|
className="flex items-center gap-1.5 px-4 py-2 bg-accent text-white text-sm font-medium rounded-[5px] hover:bg-accent/90 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
>
|
|
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
|
|
{loading ? 'Parsing…' : 'Parse with AI'}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|