import React, { useState, useEffect, useRef } from 'react';
import {
Ship,
Plane,
Package,
MapPin,
Calculator,
FileText,
Shield,
Truck,
Warehouse,
DollarSign,
Download,
Mail
} from 'lucide-react';
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
const App = () => {
const [activeTab, setActiveTab] = useState('air');
const [formData, setFormData] = useState({
transportType: 'air',
incoterm: 'FOB',
originCountry: '',
originCity: '',
destinationCountry: 'Chile',
destinationCity: '',
cargoType: 'general',
weight: '',
volume: '',
value: '',
pickup: false,
customsAgency: false,
taxPayment: false,
lastMile: false,
insurance: false,
warehousing: false,
consolidation: false,
wireTransfer: false,
specialRequirements: '',
expectedDate: ''
});
const [quoteResult, setQuoteResult] = useState(null);
const quoteRef = useRef(); // Para capturar el contenido del PDF
useEffect(() => {
setFormData(prev => ({ ...prev, transportType: activeTab }));
}, [activeTab]);
// ... (mismos datos: originCities, destinationCities, incoterms, etc.)
const originCities = {
'China': ['Shanghai', 'Beijing', 'Guangzhou', 'Shenzhen', 'Tianjin', 'Qingdao', 'Ningbo', 'Xiamen'],
'Estados Unidos': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Miami', 'Seattle', 'Atlanta', 'Dallas'],
'Colombia': ['Bogotá', 'Medellín', 'Cali', 'Barranquilla', 'Cartagena'],
'Perú': ['Lima', 'Arequipa', 'Trujillo', 'Chiclayo', 'Piura'],
'Unión Europea': ['Amsterdam', 'Antwerp', 'Barcelona', 'Berlin', 'Brussels', 'Copenhagen', 'Dublin', 'Frankfurt', 'Hamburg', 'Helsinki', 'Lisbon', 'Madrid', 'Milan', 'Paris', 'Rotterdam', 'Stockholm']
};
const destinationCities = [
'Arica', 'Iquique', 'Antofagasta', 'La Serena', 'Santiago', 'Valparaíso/Viña Del Mar', 'Rancagua', 'Curicó', 'Talca', 'Concepción', 'Chillán', 'Temuco', 'Puerto Montt', 'Coyhaique', 'Punta Arenas'
];
const incoterms = [
{ value: 'EXW', label: 'EXW - En fábrica' },
{ value: 'FCA', label: 'FCA - Libre transportista' },
{ value: 'FOB', label: 'FOB - Libre a bordo' },
{ value: 'CFR', label: 'CFR - Costo y flete' },
{ value: 'CIF', label: 'CIF - Costo, seguro y flete' },
{ value: 'DAP', label: 'DAP - Entregado en lugar' },
{ value: 'DDP', label: 'DDP - Entregado derechos pagados' }
];
const cargoTypes = [
{ value: 'general', label: 'Carga general' },
{ value: 'dangerous', label: 'Carga peligrosa' },
{ value: 'refrigerated', label: 'Refrigerada' },
{ value: 'oversized', label: 'Sobredimensionada' },
{ value: 'bulk', label: 'A granel' }
];
const originCountries = ['China', 'Estados Unidos', 'Colombia', 'Perú', 'Unión Europea'];
const handleInputChange = (field, value) => {
setFormData(prev => {
const newFormData = { ...prev, [field]: value };
if (field === 'originCountry') {
newFormData.originCity = '';
}
return newFormData;
});
};
const calculateQuote = () => {
const weight = parseFloat(formData.weight) || 0;
const volume = parseFloat(formData.volume) || 0;
const value = parseFloat(formData.value) || 0;
let transportCost = 0;
let chargeableWeight = null;
let volumeUsed = null;
let modeLabel = '';
if (activeTab === 'air') {
const volumetricWeight = volume * 167;
chargeableWeight = Math.max(weight, volumetricWeight);
transportCost = chargeableWeight * 4.5;
modeLabel = 'Aéreo';
} else {
volumeUsed = volume;
transportCost = volume * 80; // USD/m³
modeLabel = 'Marítimo';
}
const servicesCost = {
pickup: formData.pickup ? 150 : 0,
customsAgency: formData.customsAgency ? 200 : 0,
taxPayment: formData.taxPayment ? value * 0.15 : 0,
lastMile: formData.lastMile ? 250 : 0,
insurance: formData.insurance ? value * 0.02 : 0,
warehousing: formData.warehousing ? 50 : 0,
};
const totalServices = Object.values(servicesCost).reduce((sum, cost) => sum + cost, 0);
const totalCost = transportCost + totalServices;
setQuoteResult({
transportCost: Math.round(transportCost * 100) / 100,
servicesCost: Math.round(totalServices * 100) / 100,
totalCost: Math.round(totalCost * 100) / 100,
breakdown: servicesCost,
chargeableWeight,
volumeUsed,
modeLabel
});
};
const handleSubmit = (e) => {
e.preventDefault();
const { originCountry, originCity, destinationCity, weight, volume, value } = formData;
if (!originCountry || !originCity || !destinationCity) {
alert('Por favor complete todos los campos de ubicación');
return;
}
if (!weight && !volume) {
alert('Debe ingresar al menos el peso (kg) o el volumen (m³) de la carga');
return;
}
if (activeTab === 'sea' && !volume) {
alert('En transporte marítimo, el volumen (m³) es obligatorio');
return;
}
if (!value) {
alert('Por favor ingrese el valor de la mercadería (USD)');
return;
}
calculateQuote();
};
// 📧 Enviar por email
const handleEmail = () => {
if (!quoteResult) return;
const subject = encodeURIComponent(`Cotización de carga - CargoSud Pro`);
const bodyLines = [
`Cotización generada el ${new Date().toLocaleDateString('es-ES')}`,
``,
`Origen: ${formData.originCity}, ${formData.originCountry}`,
`Destino: ${formData.destinationCity}, Chile`,
`Modo: ${quoteResult.modeLabel}`,
`Incoterm: ${formData.incoterm}`,
`Tipo de carga: ${cargoTypes.find(c => c.value === formData.cargoType)?.label || formData.cargoType}`,
``,
`DETALLE DE COSTOS:`,
`Transporte ${quoteResult.modeLabel}: $${quoteResult.transportCost.toLocaleString()}`,
...(quoteResult.breakdown.pickup > 0 ? [`Recogida en origen: $${quoteResult.breakdown.pickup.toLocaleString()}`] : []),
...(quoteResult.breakdown.customsAgency > 0 ? [`Agencia de aduanas: $${quoteResult.breakdown.customsAgency.toLocaleString()}`] : []),
...(quoteResult.breakdown.taxPayment > 0 ? [`Impuestos: $${quoteResult.breakdown.taxPayment.toLocaleString()}`] : []),
...(quoteResult.breakdown.lastMile > 0 ? [`Última milla: $${quoteResult.breakdown.lastMile.toLocaleString()}`] : []),
...(quoteResult.breakdown.insurance > 0 ? [`Seguro: $${quoteResult.breakdown.insurance.toLocaleString()}`] : []),
...(quoteResult.breakdown.warehousing > 0 ? [`Almacenamiento: $${quoteResult.breakdown.warehousing.toLocaleString()}`] : []),
``,
`TOTAL: $${quoteResult.totalCost.toLocaleString()}`,
``,
`Requisitos especiales: ${formData.specialRequirements || 'Ninguno'}`,
`Fecha de embarque estimada: ${formData.expectedDate || 'No especificada'}`
];
const body = encodeURIComponent(bodyLines.join('\n'));
window.location.href = `mailto:?subject=${subject}&body=${body}`;
};
// 📄 Descargar PDF
const handleDownloadPDF = () => {
if (!quoteRef.current) return;
const input = quoteRef.current;
html2canvas(input, { scale: 2, useCORS: true }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF('p', 'mm', 'a4');
const width = pdf.internal.pageSize.getWidth();
const height = (canvas.height * width) / canvas.width;
pdf.addImage(imgData, 'PNG', 0, 0, width, height);
pdf.save(`cotizacion-cargosud-${new Date().toISOString().split('T')[0]}.pdf`);
});
};
return (
{/* Header */}
{activeTab === 'air' ?
:
}
CargoSud Pro
Cotización detallada de importaciones
{/* Form */}
{/* Resultado */}
{quoteResult ? (
Cotización Detallada
{/* Botones de acción */}
{/* Contenido de la cotización (igual que antes) */}
{quoteResult.chargeableWeight !== null ? (
<>
Peso facturable (Aéreo)
{quoteResult.chargeableWeight.toFixed(2)} kg
>
) : (
<>
Volumen (Marítimo)
{quoteResult.volumeUsed.toFixed(2)} m³
>
)}
{formData.originCity}, {formData.originCountry} → {formData.destinationCity}, Chile
Transporte {quoteResult.modeLabel}
${quoteResult.transportCost.toLocaleString()}
{quoteResult.breakdown.pickup > 0 && (
Recogida en origen
${quoteResult.breakdown.pickup.toLocaleString()}
)}
{quoteResult.breakdown.customsAgency > 0 && (
Agencia de aduanas
${quoteResult.breakdown.customsAgency.toLocaleString()}
)}
{quoteResult.breakdown.taxPayment > 0 && (
Impuestos
${quoteResult.breakdown.taxPayment.toLocaleString()}
)}
{quoteResult.breakdown.lastMile > 0 && (
Última milla
${quoteResult.breakdown.lastMile.toLocaleString()}
)}
{quoteResult.breakdown.insurance > 0 && (
Seguro
${quoteResult.breakdown.insurance.toLocaleString()}
)}
{quoteResult.breakdown.warehousing > 0 && (
Almacenamiento
${quoteResult.breakdown.warehousing.toLocaleString()}
)}
Total
${quoteResult.totalCost.toLocaleString()}
) : (
Cotización Pendiente
Complete el formulario y haga clic en "Calcular Cotización" para ver los costos detallados.
)}
{/* Info Card */}
¿Por qué elegirnos?
-
Cotizaciones transparentes y detalladas
-
Asesoramiento experto en Incoterms®
-
Red global de socios confiables
-
Seguimiento en tiempo real de su carga
);
};
export default App;