/** * DealerProposalSubmissionModal Component * Modal for Step 1: Dealer Proposal Submission * Allows dealers to upload proposal documents, provide cost breakdown, timeline, and comments */ import { useState, useRef, useMemo } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Badge } from '@/components/ui/badge'; import { Upload, Plus, X, Calendar, DollarSign, CircleAlert } from 'lucide-react'; import { toast } from 'sonner'; interface CostItem { id: string; description: string; amount: number; } interface DealerProposalSubmissionModalProps { isOpen: boolean; onClose: () => void; onSubmit: (data: { proposalDocument: File | null; costBreakup: CostItem[]; expectedCompletionDate: string; otherDocuments: File[]; dealerComments: string; }) => Promise; dealerName?: string; activityName?: string; requestId?: string; } export function DealerProposalSubmissionModal({ isOpen, onClose, onSubmit, dealerName = 'Jaipur Royal Enfield', activityName = 'Activity', requestId, }: DealerProposalSubmissionModalProps) { const [proposalDocument, setProposalDocument] = useState(null); const [costItems, setCostItems] = useState([ { id: '1', description: '', amount: 0 }, ]); const [timelineMode, setTimelineMode] = useState<'date' | 'days'>('date'); const [expectedCompletionDate, setExpectedCompletionDate] = useState(''); const [numberOfDays, setNumberOfDays] = useState(''); const [otherDocuments, setOtherDocuments] = useState([]); const [dealerComments, setDealerComments] = useState(''); const [submitting, setSubmitting] = useState(false); const proposalDocInputRef = useRef(null); const otherDocsInputRef = useRef(null); // Calculate total estimated budget const totalBudget = useMemo(() => { return costItems.reduce((sum, item) => sum + (item.amount || 0), 0); }, [costItems]); // Check if all required fields are filled const isFormValid = useMemo(() => { const hasProposalDoc = proposalDocument !== null; const hasValidCostItems = costItems.length > 0 && costItems.every(item => item.description.trim() !== '' && item.amount > 0); const hasTimeline = timelineMode === 'date' ? expectedCompletionDate !== '' : numberOfDays !== '' && parseInt(numberOfDays) > 0; const hasComments = dealerComments.trim().length > 0; return hasProposalDoc && hasValidCostItems && hasTimeline && hasComments; }, [proposalDocument, costItems, timelineMode, expectedCompletionDate, numberOfDays, dealerComments]); const handleProposalDocChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { // Validate file type const allowedTypes = ['.pdf', '.doc', '.docx']; const fileExtension = '.' + file.name.split('.').pop()?.toLowerCase(); if (!allowedTypes.includes(fileExtension)) { toast.error('Please upload a PDF, DOC, or DOCX file'); return; } setProposalDocument(file); } }; const handleOtherDocsChange = (e: React.ChangeEvent) => { const files = Array.from(e.target.files || []); setOtherDocuments(prev => [...prev, ...files]); }; const handleAddCostItem = () => { setCostItems(prev => [ ...prev, { id: Date.now().toString(), description: '', amount: 0 }, ]); }; const handleRemoveCostItem = (id: string) => { if (costItems.length > 1) { setCostItems(prev => prev.filter(item => item.id !== id)); } }; const handleCostItemChange = (id: string, field: 'description' | 'amount', value: string | number) => { setCostItems(prev => prev.map(item => item.id === id ? { ...item, [field]: field === 'amount' ? parseFloat(value.toString()) || 0 : value } : item ) ); }; const handleRemoveOtherDoc = (index: number) => { setOtherDocuments(prev => prev.filter((_, i) => i !== index)); }; const handleSubmit = async () => { if (!isFormValid) { toast.error('Please fill all required fields'); return; } // Calculate final completion date if using days mode let finalCompletionDate = expectedCompletionDate; if (timelineMode === 'days' && numberOfDays) { const days = parseInt(numberOfDays); const date = new Date(); date.setDate(date.getDate() + days); finalCompletionDate = date.toISOString().split('T')[0]; } try { setSubmitting(true); await onSubmit({ proposalDocument, costBreakup: costItems.filter(item => item.description.trim() !== '' && item.amount > 0), expectedCompletionDate: finalCompletionDate, otherDocuments, dealerComments, }); handleReset(); onClose(); } catch (error) { console.error('Failed to submit proposal:', error); toast.error('Failed to submit proposal. Please try again.'); } finally { setSubmitting(false); } }; const handleReset = () => { setProposalDocument(null); setCostItems([{ id: '1', description: '', amount: 0 }]); setTimelineMode('date'); setExpectedCompletionDate(''); setNumberOfDays(''); setOtherDocuments([]); setDealerComments(''); if (proposalDocInputRef.current) proposalDocInputRef.current.value = ''; if (otherDocsInputRef.current) otherDocsInputRef.current.value = ''; }; const handleClose = () => { if (!submitting) { handleReset(); onClose(); } }; // Get minimum date (today) const minDate = new Date().toISOString().split('T')[0]; return ( Dealer Proposal Submission Step 1: Upload proposal and planning details
Dealer: {dealerName}
Activity: {activityName}
Please upload proposal document, provide cost breakdown, timeline, and detailed comments.
{/* Proposal Document Section */}

Proposal Document

Required

Detailed proposal with activity details and requested information

{/* Cost Breakup Section */}

Cost Breakup

Required
{costItems.map((item) => (
handleCostItemChange(item.id, 'description', e.target.value) } />
handleCostItemChange(item.id, 'amount', e.target.value) } />
))}
Estimated Budget
₹{totalBudget.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{/* Timeline for Closure Section */}

Timeline for Closure

Required
{timelineMode === 'date' ? (
setExpectedCompletionDate(e.target.value)} />
) : (
setNumberOfDays(e.target.value)} />
)}
{/* Other Supporting Documents Section */}

Other Supporting Documents

Optional

Any other supporting documents (invoices, receipts, photos, etc.)

{otherDocuments.length > 0 && (
{otherDocuments.map((file, index) => (
{file.name}
))}
)}
{/* Dealer Comments Section */}