311 lines
12 KiB
TypeScript
311 lines
12 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog';
|
|
import { Button } from '../ui/button';
|
|
import { Label } from '../ui/label';
|
|
import { Textarea } from '../ui/textarea';
|
|
import { Badge } from '../ui/badge';
|
|
import { Upload, FileText, X, CheckCircle, AlertCircle } from 'lucide-react';
|
|
import { toast } from 'sonner@2.0.3';
|
|
|
|
interface DealerDocumentModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onSubmit: (data: {
|
|
proposalDocument: File | null;
|
|
costBreakup: File | null;
|
|
timeline: File | null;
|
|
otherDocuments: File[];
|
|
dealerComments: string;
|
|
}) => Promise<void>;
|
|
dealerName: string;
|
|
activityName: string;
|
|
}
|
|
|
|
export function DealerDocumentModal({
|
|
isOpen,
|
|
onClose,
|
|
onSubmit,
|
|
dealerName,
|
|
activityName
|
|
}: DealerDocumentModalProps) {
|
|
const [proposalDocument, setProposalDocument] = useState<File | null>(null);
|
|
const [costBreakup, setCostBreakup] = useState<File | null>(null);
|
|
const [timeline, setTimeline] = useState<File | null>(null);
|
|
const [otherDocuments, setOtherDocuments] = useState<File[]>([]);
|
|
const [dealerComments, setDealerComments] = useState('');
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
const handleFileUpload = (field: 'proposal' | 'cost' | 'timeline', file: File | null) => {
|
|
if (field === 'proposal') setProposalDocument(file);
|
|
if (field === 'cost') setCostBreakup(file);
|
|
if (field === 'timeline') setTimeline(file);
|
|
};
|
|
|
|
const handleMultipleFileUpload = (files: FileList | null) => {
|
|
if (files) {
|
|
const fileArray = Array.from(files);
|
|
setOtherDocuments(prev => [...prev, ...fileArray]);
|
|
}
|
|
};
|
|
|
|
const removeOtherDocument = (index: number) => {
|
|
setOtherDocuments(prev => prev.filter((_, i) => i !== index));
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
// Validation
|
|
if (!proposalDocument) {
|
|
toast.error('Proposal document is required');
|
|
return;
|
|
}
|
|
if (!costBreakup) {
|
|
toast.error('Cost breakup document is required');
|
|
return;
|
|
}
|
|
if (!timeline) {
|
|
toast.error('Timeline document is required');
|
|
return;
|
|
}
|
|
if (!dealerComments.trim()) {
|
|
toast.error('Please add dealer comments');
|
|
return;
|
|
}
|
|
|
|
setIsSubmitting(true);
|
|
try {
|
|
await onSubmit({
|
|
proposalDocument,
|
|
costBreakup,
|
|
timeline,
|
|
otherDocuments,
|
|
dealerComments
|
|
});
|
|
|
|
// Reset form
|
|
setProposalDocument(null);
|
|
setCostBreakup(null);
|
|
setTimeline(null);
|
|
setOtherDocuments([]);
|
|
setDealerComments('');
|
|
|
|
onClose();
|
|
} catch (error) {
|
|
console.error('Error submitting dealer documents:', error);
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2 text-2xl">
|
|
<Upload className="w-6 h-6 text-[--re-green]" />
|
|
Dealer Document Upload
|
|
</DialogTitle>
|
|
<DialogDescription className="text-base">
|
|
<div className="space-y-1 mt-2">
|
|
<p><strong>Dealer:</strong> {dealerName}</p>
|
|
<p><strong>Activity:</strong> {activityName}</p>
|
|
<p className="text-sm text-gray-600 mt-2">
|
|
Please upload all required documents and provide detailed comments about this claim request.
|
|
</p>
|
|
</div>
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-6 py-4">
|
|
{/* Required Documents Section */}
|
|
<div className="space-y-4">
|
|
<div className="flex items-center gap-2">
|
|
<h3 className="font-semibold text-lg">Required Documents</h3>
|
|
<Badge variant="destructive" className="text-xs">All Required</Badge>
|
|
</div>
|
|
|
|
{/* Proposal Document */}
|
|
<div>
|
|
<Label className="text-base font-semibold flex items-center gap-2">
|
|
Proposal Document *
|
|
{proposalDocument && <CheckCircle className="w-4 h-4 text-green-600" />}
|
|
</Label>
|
|
<p className="text-sm text-gray-600 mb-2">
|
|
Detailed proposal with activity details and requested information
|
|
</p>
|
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4 hover:border-blue-500 transition-colors">
|
|
<input
|
|
type="file"
|
|
accept=".pdf,.doc,.docx"
|
|
onChange={(e) => handleFileUpload('proposal', e.target.files?.[0] || null)}
|
|
className="hidden"
|
|
id="proposalDoc"
|
|
/>
|
|
<label htmlFor="proposalDoc" className="cursor-pointer flex flex-col items-center gap-2">
|
|
<Upload className="w-8 h-8 text-gray-400" />
|
|
<span className="text-sm text-gray-600">Click to upload proposal (PDF, DOC, DOCX)</span>
|
|
{proposalDocument && (
|
|
<Badge variant="secondary" className="mt-2 flex items-center gap-1">
|
|
<FileText className="w-3 h-3" />
|
|
{proposalDocument.name}
|
|
</Badge>
|
|
)}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Cost Breakup */}
|
|
<div>
|
|
<Label className="text-base font-semibold flex items-center gap-2">
|
|
Cost Breakup *
|
|
{costBreakup && <CheckCircle className="w-4 h-4 text-green-600" />}
|
|
</Label>
|
|
<p className="text-sm text-gray-600 mb-2">
|
|
Detailed cost analysis and breakdown
|
|
</p>
|
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4 hover:border-blue-500 transition-colors">
|
|
<input
|
|
type="file"
|
|
accept=".pdf,.xlsx,.xls,.csv"
|
|
onChange={(e) => handleFileUpload('cost', e.target.files?.[0] || null)}
|
|
className="hidden"
|
|
id="costDoc"
|
|
/>
|
|
<label htmlFor="costDoc" className="cursor-pointer flex flex-col items-center gap-2">
|
|
<Upload className="w-8 h-8 text-gray-400" />
|
|
<span className="text-sm text-gray-600">Click to upload cost breakup (Excel, PDF, CSV)</span>
|
|
{costBreakup && (
|
|
<Badge variant="secondary" className="mt-2 flex items-center gap-1">
|
|
<FileText className="w-3 h-3" />
|
|
{costBreakup.name}
|
|
</Badge>
|
|
)}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Timeline for Closure */}
|
|
<div>
|
|
<Label className="text-base font-semibold flex items-center gap-2">
|
|
Timeline for Closure *
|
|
{timeline && <CheckCircle className="w-4 h-4 text-green-600" />}
|
|
</Label>
|
|
<p className="text-sm text-gray-600 mb-2">
|
|
Project timeline and milestone details
|
|
</p>
|
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4 hover:border-blue-500 transition-colors">
|
|
<input
|
|
type="file"
|
|
accept=".pdf,.doc,.docx,.xlsx,.xls"
|
|
onChange={(e) => handleFileUpload('timeline', e.target.files?.[0] || null)}
|
|
className="hidden"
|
|
id="timelineDoc"
|
|
/>
|
|
<label htmlFor="timelineDoc" className="cursor-pointer flex flex-col items-center gap-2">
|
|
<Upload className="w-8 h-8 text-gray-400" />
|
|
<span className="text-sm text-gray-600">Click to upload timeline (PDF, DOC, Excel)</span>
|
|
{timeline && (
|
|
<Badge variant="secondary" className="mt-2 flex items-center gap-1">
|
|
<FileText className="w-3 h-3" />
|
|
{timeline.name}
|
|
</Badge>
|
|
)}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Optional Documents */}
|
|
<div className="space-y-4">
|
|
<div className="flex items-center gap-2">
|
|
<h3 className="font-semibold text-lg">Other Supporting Documents</h3>
|
|
<Badge variant="secondary" className="text-xs">Optional</Badge>
|
|
</div>
|
|
|
|
<div>
|
|
<Label className="text-base font-semibold">Additional Documents</Label>
|
|
<p className="text-sm text-gray-600 mb-2">
|
|
Any other supporting documents (invoices, receipts, photos, etc.)
|
|
</p>
|
|
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4 hover:border-blue-500 transition-colors">
|
|
<input
|
|
type="file"
|
|
multiple
|
|
onChange={(e) => handleMultipleFileUpload(e.target.files)}
|
|
className="hidden"
|
|
id="otherDocs"
|
|
/>
|
|
<label htmlFor="otherDocs" className="cursor-pointer flex flex-col items-center gap-2">
|
|
<Upload className="w-8 h-8 text-gray-400" />
|
|
<span className="text-sm text-gray-600">Click to upload additional documents (multiple files allowed)</span>
|
|
</label>
|
|
</div>
|
|
{otherDocuments.length > 0 && (
|
|
<div className="mt-3 space-y-2">
|
|
{otherDocuments.map((file, index) => (
|
|
<div key={index} className="flex items-center justify-between p-2 bg-gray-50 rounded border">
|
|
<div className="flex items-center gap-2">
|
|
<FileText className="w-4 h-4 text-gray-600" />
|
|
<span className="text-sm">{file.name}</span>
|
|
<span className="text-xs text-gray-500">({(file.size / 1024).toFixed(1)} KB)</span>
|
|
</div>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => removeOtherDocument(index)}
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Dealer Comments */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="dealerComments" className="text-base font-semibold flex items-center gap-2">
|
|
Dealer Comments / Details *
|
|
{dealerComments.trim() && <CheckCircle className="w-4 h-4 text-green-600" />}
|
|
</Label>
|
|
<Textarea
|
|
id="dealerComments"
|
|
placeholder="Provide detailed comments about this claim request, including any special considerations, execution details, or additional information..."
|
|
value={dealerComments}
|
|
onChange={(e) => setDealerComments(e.target.value)}
|
|
className="min-h-[120px]"
|
|
/>
|
|
<p className="text-xs text-gray-500">
|
|
{dealerComments.length} characters
|
|
</p>
|
|
</div>
|
|
|
|
{/* Validation Alert */}
|
|
{(!proposalDocument || !costBreakup || !timeline || !dealerComments.trim()) && (
|
|
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 flex items-start gap-3">
|
|
<AlertCircle className="w-5 h-5 text-amber-600 flex-shrink-0 mt-0.5" />
|
|
<div className="text-sm text-amber-800">
|
|
<p className="font-semibold mb-1">Missing Required Information</p>
|
|
<p>Please ensure all required documents are uploaded and dealer comments are provided before submitting.</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={onClose} disabled={isSubmitting}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={handleSubmit}
|
|
disabled={isSubmitting || !proposalDocument || !costBreakup || !timeline || !dealerComments.trim()}
|
|
className="bg-[--re-green] hover:bg-[--re-green-dark]"
|
|
>
|
|
{isSubmitting ? 'Submitting...' : 'Submit Documents'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|