import { useState, useEffect } from 'react' import { X, AlertCircle } from 'lucide-react' import { ticketsApi } from '@/api/tickets' import { integrationsApi } from '@/api/integrations' import { AiTicketParseForm } from './AiTicketParseForm' import { toast } from '@/lib/toast' import { cn } from '@/lib/utils' import type { TicketCreationPayload, AiParseResponse, PSAPriority } from '@/types/tickets' import type { PSABoard, PSATicketStatusItem } from '@/types/integrations' interface Props { defaultTab?: 'quick' | 'manual' initialValues?: Partial summaryHint?: string onClose: () => void onCreated: (ticketId: number, summary: string) => void } const EMPTY_DRAFT: TicketCreationPayload = { summary: '', company_id: null, board_id: null, status_id: null, priority_id: null, description: '', assigned_member_id: null, } export function NewTicketModal({ defaultTab = 'quick', initialValues, summaryHint, onClose, onCreated }: Props) { const [tab, setTab] = useState<'quick' | 'manual'>(defaultTab) const [draft, setDraft] = useState({ ...EMPTY_DRAFT, ...initialValues }) const [missingFields, setMissingFields] = useState([]) const [warnings, setWarnings] = useState([]) const [boards, setBoards] = useState([]) const [statuses, setStatuses] = useState([]) const [priorities, setPriorities] = useState([]) const [submitting, setSubmitting] = useState(false) const [parsed, setParsed] = useState(false) useEffect(() => { integrationsApi.listBoards().then(setBoards).catch(() => {}) ticketsApi.listPriorities().then(setPriorities).catch(err => console.error('Failed to load priorities', err)) }, []) useEffect(() => { if (draft.board_id) { integrationsApi.getBoardStatuses(draft.board_id) .then(setStatuses).catch(() => {}) } else { setStatuses([]) } }, [draft.board_id]) function handleParsed(values: Partial, result: AiParseResponse) { setDraft(prev => ({ ...prev, ...values })) setMissingFields(result.missing_fields) setWarnings(result.warnings) setParsed(true) } function updateDraft(field: keyof TicketCreationPayload, value: unknown) { setDraft(prev => ({ ...prev, [field]: value })) setMissingFields(prev => prev.filter(f => f !== field)) } async function handleSubmit() { if (!draft.summary.trim() || !draft.company_id || !draft.board_id || !draft.status_id || !draft.priority_id) { toast.warning('Please fill in all required fields') return } setSubmitting(true) try { const result = await ticketsApi.createTicket(draft) toast.success(`Ticket #${result.id} created in ConnectWise`) onCreated(result.id, result.summary) } catch { toast.error('Failed to create ticket') } finally { setSubmitting(false) } } const requiredMissing = (f: string) => missingFields.includes(f) return (
{/* Header */}

New Ticket

{/* Tabs */}
{(['quick', 'manual'] as const).map(t => ( ))}
{/* Content */}
{/* Warnings */} {warnings.length > 0 && (
    {warnings.map((w, i) =>
  • {w}
  • )}
)} {/* Quick Create tab — before parse */} {tab === 'quick' && !parsed && ( )} {/* Form — shown after parse OR in manual tab */} {(tab === 'manual' || parsed) && (
{/* Summary */}
updateDraft('summary', e.target.value)} />
{/* Board */}
{/* Status */}
{/* Priority */}
{/* Company ID */}
updateDraft('company_id', e.target.value ? Number(e.target.value) : null)} />
{/* Description */}