import { useState, useEffect } from 'react' import { Loader2, Copy, AlertTriangle, AlertCircle, RefreshCw } from 'lucide-react' import { Modal } from '@/components/common/Modal' import { Textarea } from '@/components/ui/Textarea' import { cn } from '@/lib/utils' import { toast } from '@/lib/toast' import { sessionPsaApi } from '@/api/integrations' import type { PsaPreviewResponse } from '@/types/integrations' interface Props { open: boolean onClose: () => void sessionId: string onPosted?: () => void } type NoteType = 'internal_analysis' | 'resolution' | 'description' const NOTE_TYPE_OPTIONS: { value: NoteType; label: string; description: string; warning?: string }[] = [ { value: 'internal_analysis', label: 'Internal Analysis', description: 'Internal only, no notifications' }, { value: 'resolution', label: 'Resolution', description: 'Internal only, triggers notifications' }, { value: 'description', label: 'Description', description: 'Visible to the customer', warning: 'This note will be visible to the customer' }, ] const CHAR_WARNING_THRESHOLD = 15000 export function UpdateTicketModal({ open, onClose, sessionId, onPosted }: Props) { const [preview, setPreview] = useState(null) const [isLoading, setIsLoading] = useState(false) const [loadError, setLoadError] = useState(null) const [content, setContent] = useState('') const [noteType, setNoteType] = useState('internal_analysis') const [selectedStatusId, setSelectedStatusId] = useState(null) const [isPosting, setIsPosting] = useState(false) const [postError, setPostError] = useState(null) useEffect(() => { if (open) { loadPreview() } else { // Reset state when closed setPreview(null) setContent('') setNoteType('internal_analysis') setSelectedStatusId(null) setPostError(null) setLoadError(null) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, sessionId]) const loadPreview = async () => { setIsLoading(true) setLoadError(null) try { const data = await sessionPsaApi.getPostPreview(sessionId) setPreview(data) setContent(data.content) // Find current status ID from available_statuses matching ticket status_name const currentStatus = data.available_statuses.find( (s) => s.name === data.ticket.status_name ) setSelectedStatusId(currentStatus?.id ?? null) } catch (err) { const axiosErr = err as { response?: { data?: { detail?: string } } } setLoadError(axiosErr.response?.data?.detail || 'Failed to load preview') console.error(err) } finally { setIsLoading(false) } } const handleCopyContent = async () => { try { await navigator.clipboard.writeText(content) toast.success('Content copied to clipboard') } catch { toast.error('Failed to copy content') } } const handlePost = async () => { setIsPosting(true) setPostError(null) try { // Determine if status should be updated const currentStatus = preview?.available_statuses.find( (s) => s.name === preview?.ticket.status_name ) const statusChanged = selectedStatusId !== null && selectedStatusId !== currentStatus?.id await sessionPsaApi.postToTicket(sessionId, { note_type: noteType, content, ...(statusChanged ? { update_status_id: selectedStatusId } : {}), }) toast.success('Note posted to ticket successfully') onPosted?.() onClose() } catch (err) { const axiosErr = err as { response?: { data?: { detail?: string } } } setPostError(axiosErr.response?.data?.detail || 'Failed to post to ticket') console.error(err) } finally { setIsPosting(false) } } const currentStatusId = preview?.available_statuses.find( (s) => s.name === preview?.ticket.status_name )?.id const footer = (
{postError && (
{postError}
)}
) return ( {isLoading && (
)} {loadError && (
{loadError}
)} {preview && !isLoading && (
{/* Left panel - Content editor */}

Note Content