Last-Mile Delivery Cost Estimator

Last-Mile Delivery Cost Estimator

Estimate and compare last-mile delivery costs based on various factors.

Enter delivery details to compare options.

`; document.getElementById('estimator-form').addEventListener('submit', handleCalculation); }; const handleCalculation = (e) => { e.preventDefault(); const distance = parseFloat(document.getElementById('distance').value); const locationType = document.getElementById('location-type').value; const deliverySpeed = document.getElementById('delivery-speed').value; const packageSize = document.getElementById('package-size').value; if (isNaN(distance) || distance <= 0) { alert('Please enter a valid, positive distance.'); return; } calculationInputs = { distance, locationType, deliverySpeed, packageSize }; let results = []; appData.methods.forEach(method => { const locationMultiplier = method.locationMultipliers[locationType]; const speedSurcharge = method.speedSurcharges[deliverySpeed]; const sizeSurcharge = method.sizeSurcharges[packageSize]; const totalCost = (method.baseCost + (distance * method.costPerMile)) * locationMultiplier + speedSurcharge + sizeSurcharge; if (totalCost > 9000) return; // Exclude bike for large packages results.push({ method: method.name, totalCost, deliveryTime: method.deliveryTime[deliverySpeed], breakdown: { base: method.baseCost, distance: (distance * method.costPerMile) * locationMultiplier, speed: speedSurcharge, size: sizeSurcharge } }); }); const bestOption = results.reduce((prev, curr) => prev.totalCost < curr.totalCost ? prev : curr); calculationResult = { results, bestOption }; renderResults(); }; const renderResults = () => { const resultsContainer = document.getElementById('results-container'); if (!calculationResult) return; const { results, bestOption } = calculationResult; const resultsHtml = results.map(r => { const isBest = r.method === bestOption.method; return `

${r.method}

Est. ${r.deliveryTime}

${isBest ? `Best Value` : ''}

${formatCurrency(r.totalCost)}

`; }).join(''); resultsContainer.innerHTML = `
${resultsHtml}
`; const ctx = document.getElementById('costChart').getContext('2d'); if (costChartInstance) costChartInstance.destroy(); costChartInstance = new Chart(ctx, { type: 'bar', data: { labels: results.map(r => r.method), datasets: [{ label: 'Total Estimated Cost', data: results.map(r => r.totalCost), backgroundColor: 'rgba(37, 99, 235, 0.6)' }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } }, plugins: { legend: { display: false } } } }); document.getElementById('download-pdf-btn').addEventListener('click', generatePdf); }; const renderConfiguration = () => { const configContent = document.getElementById('content-config'); if (!configContent) return; const methodsHtml = appData.methods.map((method, mIndex) => `

${method.name}

Location Multipliers

`).join(''); configContent.innerHTML = `
${methodsHtml}
`; document.getElementById('save-config-btn').addEventListener('click', handleConfigSave); }; const handleConfigSave = () => { document.querySelectorAll('#config-form input').forEach(input => { const mIndex = input.dataset.method; const fieldPath = input.dataset.field.split('.'); let target = appData.methods[mIndex]; for (let i = 0; i < fieldPath.length - 1; i++) { target = target[fieldPath[i]]; } target[fieldPath[fieldPath.length - 1]] = parseFloat(input.value); }); alert('Rate configuration saved!'); }; const generatePdf = () => { if (!calculationResult) return; loadingOverlay.style.display = 'flex'; const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); let y = 40; pdf.setFontSize(18).setFont('helvetica', 'bold').text('Last-Mile Delivery Cost Report', pdf.internal.pageSize.getWidth() / 2, y, { align: 'center' }); y += 30; pdf.autoTable({ startY: y, theme: 'plain', body: [ ['Distance:', `${calculationInputs.distance} miles`, 'Speed:', calculationInputs.deliverySpeed], ['Zone:', calculationInputs.locationType, 'Size:', calculationInputs.packageSize], ] }); y = pdf.autoTable.previous.finalY + 30; pdf.autoTable({ startY: y, head: [['Delivery Method', 'Est. Time', 'Total Cost']], body: calculationResult.results.map(r => [r.method, r.deliveryTime, formatCurrency(r.totalCost)]), theme: 'grid', headStyles: { fillColor: [37, 99, 235] } // Blue header }); pdf.save(`Last-Mile-Cost-Report.pdf`); loadingOverlay.style.display = 'none'; }; const switchTab = (tabIndex) => { activeTabIndex = tabIndex; document.querySelectorAll('.tab-btn').forEach((btn, i) => btn.classList.toggle('active', i === tabIndex)); document.querySelectorAll('.tab-content').forEach((content, i) => content.classList.toggle('hidden', i !== tabIndex)); updateNavButtons(); }; const updateNavButtons = () => { prevTabBtn.disabled = activeTabIndex === 0; nextTabBtn.disabled = activeTabIndex === tabIdentifiers.length - 1; }; const initializeUI = () => { const tabs = [ { name: 'Cost Estimator', id: 'estimator' }, { name: 'Rate Configuration', id: 'config' } ]; tabIdentifiers = tabs.map(t => t.id); tabsContainer.innerHTML = tabs.map(tab => ``).join(''); mainContent.innerHTML = tabs.map(tab => `
`).join(''); tabs.forEach((tab, index) => { document.getElementById(`tab-${tab.id}`).addEventListener('click', () => switchTab(index)); }); renderEstimator(); renderConfiguration(); switchTab(0); lucide.createIcons(); }; initializeUI(); prevTabBtn.addEventListener('click', () => { if (activeTabIndex > 0) switchTab(activeTabIndex - 1); }); nextTabBtn.addEventListener('click', () => { if (activeTabIndex < tabIdentifiers.length - 1) switchTab(activeTabIndex + 1); }); });
Scroll to Top