'use client'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { FileText, ExternalLink, Calendar, Building2, User, DollarSign, RefreshCw, Ticket } from 'lucide-react'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { TicketDetailModal } from '@/components/quotes/ticket-detail-modal'; interface Quote { id: string; title: string; number: string; status: string; createdAt: string; sentAt?: string; company?: { name: string; }; contact?: { name: string; }; owner?: { name: string; }; items?: Array<{ price?: number; quantity: number; }>; link?: string; } export default function QuotesPage() { const [quotes, setQuotes] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedTicketNumber, setSelectedTicketNumber] = useState(null); const [ticketModalOpen, setTicketModalOpen] = useState(false); useEffect(() => { fetchQuotes(); }, []); const fetchQuotes = async () => { setLoading(true); setError(null); try { const response = await fetch('/api/salesbldr/quotes?status=open&size=100'); if (!response.ok) { throw new Error('Failed to fetch quotes'); } const data = await response.json(); setQuotes(data.results || []); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); console.error('Error fetching quotes:', err); } finally { setLoading(false); } }; const getStatusBadge = (status: string) => { const variants: Record = { draft: { variant: "secondary", label: "Draft" }, sent: { variant: "default", label: "Sent" }, approved: { variant: "outline", label: "Approved" }, declined: { variant: "destructive", label: "Declined" }, }; const config = variants[status] || { variant: "outline", label: status }; return {config.label}; }; const calculateTotal = (quote: Quote) => { if (!quote.items || quote.items.length === 0) return 0; return quote.items.reduce((sum, item) => { return sum + ((item.price || 0) * item.quantity); }, 0); }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', }).format(amount); }; const formatDate = (dateString?: string) => { if (!dateString) return 'N/A'; return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }); }; const extractTicketNumber = (title: string): { ticketNumber: string; ticketId: string } | null => { // Match ticket numbers in format T20260109.0122 const match = title.match(/T(\d{8})\.(\d{4})/); if (match) { return { ticketNumber: match[0], ticketId: match[0].substring(1) // Remove the 'T' prefix for the ID }; } return null; }; const handleTicketClick = (ticketNumber: string) => { setSelectedTicketNumber(ticketNumber); setTicketModalOpen(true); }; const renderTitleWithTicketLink = (title: string) => { const ticketInfo = extractTicketNumber(title); if (!ticketInfo) { return
{title}
; } // Split the title into parts before and after the ticket number const parts = title.split(ticketInfo.ticketNumber); return (
{parts[0]} { e.stopPropagation(); handleTicketClick(ticketInfo.ticketNumber); }} title={`View ticket ${ticketInfo.ticketNumber} in Autotask`} > {ticketInfo.ticketNumber} {parts[1]}
); }; return (
{/* Header */}

Open Quotes

View and manage quotes from SalesBldr

{/* Stats */}
Total Open Quotes
{quotes.length}
Sent Quotes
{quotes.filter(q => q.status === 'sent').length}
Draft Quotes
{quotes.filter(q => q.status === 'draft').length}
{/* Quotes Table */} Quotes List All open quotes from SalesBldr {loading ? (
) : error ? (

Error: {error}

) : quotes.length === 0 ? (

No open quotes found

) : ( Quote # Title Company Owner Status Date Total {quotes.map((quote) => ( {quote.number} {renderTitleWithTicketLink(quote.title)}
{quote.company?.name || 'N/A'}
{quote.owner?.name || 'N/A'}
{getStatusBadge(quote.status)} {formatDate(quote.sentAt || quote.createdAt)} {formatCurrency(calculateTotal(quote))} {quote.link && ( )}
))}
)}
{/* Ticket Detail Modal */} {selectedTicketNumber && ( )}
); }