export function shippingCostCalculator(form: FormTs) {
// Base rates by zone (per lb)
const zoneRates: Record<string, number> = {
'local': 0.50,
'regional': 0.75,
'national': 1.00,
'canada': 2.50,
'international': 4.00
};
// Speed multipliers
const speedMultipliers: Record<string, number> = {
'economy': 1.0,
'standard': 1.3,
'express': 1.8,
'overnight': 3.0
};
// Base handling fees
const baseHandlingFees: Record<string, number> = {
'economy': 4.99,
'standard': 6.99,
'express': 12.99,
'overnight': 24.99
};
// DIM weight divisor (standard is 139 for domestic)
const dimDivisor = 139;
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Shipping Cost Estimator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Package Details Section
const packageSection = form.addSubform('packageDetails', { title: '๐ฆ Package Details' });
packageSection.addRow(row => {
row.addDecimal('weight', {
label: 'Package Weight (lbs)',
min: 0.1,
max: 150,
defaultValue: 2,
step: 0.1,
isRequired: true
}, '1fr');
row.addDropdown('packageType', {
label: 'Package Type',
options: [
{ id: 'envelope', name: 'Envelope/Flat' },
{ id: 'small-box', name: 'Small Box (up to 12")' },
{ id: 'medium-box', name: 'Medium Box (up to 18")' },
{ id: 'large-box', name: 'Large Box (up to 24")' },
{ id: 'custom', name: 'Custom Dimensions' }
],
defaultValue: 'small-box',
isRequired: true
}, '1fr');
});
packageSection.addRow(row => {
row.addDecimal('length', {
label: 'Length (inches)',
min: 1,
max: 108,
defaultValue: 10,
step: 0.5,
isVisible: () => packageSection.dropdown('packageType')?.value() === 'custom'
}, '1fr');
row.addDecimal('width', {
label: 'Width (inches)',
min: 1,
max: 108,
defaultValue: 8,
step: 0.5,
isVisible: () => packageSection.dropdown('packageType')?.value() === 'custom'
}, '1fr');
row.addDecimal('height', {
label: 'Height (inches)',
min: 1,
max: 108,
defaultValue: 6,
step: 0.5,
isVisible: () => packageSection.dropdown('packageType')?.value() === 'custom'
}, '1fr');
});
packageSection.addRow(row => {
row.addDropdown('contents', {
label: 'Package Contents',
options: [
{ id: 'general', name: 'General Merchandise' },
{ id: 'clothing', name: 'Clothing & Textiles' },
{ id: 'electronics', name: 'Electronics' },
{ id: 'fragile', name: 'Fragile Items (+$5)' },
{ id: 'documents', name: 'Documents' },
{ id: 'perishable', name: 'Perishable (+$10)' }
],
defaultValue: 'general'
}, '1fr');
row.addDecimal('declaredValue', {
label: 'Declared Value ($)',
min: 0,
max: 50000,
defaultValue: 50,
step: 1,
placeholder: 'For insurance'
}, '1fr');
});
// Destination Section
const destinationSection = form.addSubform('destination', { title: '๐ Origin & Destination' });
destinationSection.addRow(row => {
row.addAddress('originAddress', {
label: 'Ship From',
placeholder: 'Enter origin address...',
restrictToCountries: ['US', 'CA'],
distanceUnit: 'miles',
isRequired: true
});
});
destinationSection.addRow(row => {
row.addAddress('destAddress', {
label: 'Ship To',
placeholder: 'Enter destination address...',
showMap: true,
showDistance: true,
referenceAddress: () => destinationSection.address('originAddress')?.value() ?? null,
distanceUnit: 'miles',
isRequired: true
});
});
destinationSection.addRow(row => {
row.addTextPanel('zoneInfo', {
computedValue: () => {
const destField = destinationSection.address('destAddress');
const miles = destField?.distance();
if (miles == null) return 'Enter both addresses to see shipping zone';
if (miles < 50) return '๐ Zone 1 - Local (Same metro area)';
if (miles < 150) return '๐ Zone 2 - Regional (Same state)';
if (miles < 600) return '๐ Zone 3 - National (Short distance)';
if (miles < 1400) return '๐ Zone 4 - National (Medium distance)';
if (miles < 1800) return '๐ Zone 5 - National (Long distance)';
return '๐ Zone 6 - Cross-country';
},
customStyles: { 'font-size': '0.9rem', 'color': '#0369a1', 'background': '#e0f2fe', 'padding': '10px', 'border-radius': '6px' }
});
});
destinationSection.addRow(row => {
row.addDropdown('deliveryType', {
label: 'Delivery Type',
options: [
{ id: 'residential', name: 'Residential Address' },
{ id: 'commercial', name: 'Commercial Address (-$2)' },
{ id: 'pobox', name: 'PO Box' }
],
defaultValue: 'residential'
}, '1fr');
});
// Shipping Speed Section
const speedSection = form.addSubform('shippingSpeed', { title: '๐ Shipping Speed' });
speedSection.addRow(row => {
row.addRadioButton('speed', {
label: 'Delivery Speed',
options: [
{ id: 'economy', name: 'Economy (7-10 business days)' },
{ id: 'standard', name: 'Standard (5-7 business days)' },
{ id: 'express', name: 'Express (2-3 business days)' },
{ id: 'overnight', name: 'Overnight (Next business day)' }
],
defaultValue: 'standard',
orientation: 'vertical',
isRequired: true
});
});
// Options Section
const optionsSection = form.addSubform('options', { title: 'โจ Additional Options' });
optionsSection.addRow(row => {
row.addCheckbox('signature', {
label: 'Signature Required (+$3)',
defaultValue: false
}, '1fr');
row.addCheckbox('insurance', {
label: 'Additional Insurance (+1% of value)',
defaultValue: false
}, '1fr');
});
optionsSection.addRow(row => {
row.addCheckbox('tracking', {
label: 'Enhanced Tracking (+$2)',
defaultValue: true
}, '1fr');
row.addCheckbox('saturdayDelivery', {
label: 'Saturday Delivery (+$15)',
defaultValue: false
}, '1fr');
});
optionsSection.addRow(row => {
row.addCheckbox('packagingService', {
label: 'Professional Packaging (+$8)',
defaultValue: false
}, '1fr');
row.addCheckbox('pickupService', {
label: 'Scheduled Pickup (+$5)',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Quote Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Shipping Quote', isCollapsible: false });
const getPackageDimensions = () => {
const packageType = packageSection.dropdown('packageType')?.value() || 'small-box';
// Preset dimensions for standard package types
const presets: Record<string, { l: number; w: number; h: number }> = {
'envelope': { l: 12, w: 9, h: 0.5 },
'small-box': { l: 12, w: 10, h: 8 },
'medium-box': { l: 18, w: 14, h: 12 },
'large-box': { l: 24, w: 18, h: 18 },
'custom': {
l: packageSection.decimal('length')?.value() || 10,
w: packageSection.decimal('width')?.value() || 8,
h: packageSection.decimal('height')?.value() || 6
}
};
return presets[packageType] || presets['small-box'];
};
const getBillableWeight = () => {
const actualWeight = packageSection.decimal('weight')?.value() || 2;
const dims = getPackageDimensions() || { l: 10, w: 8, h: 6 };
// Calculate dimensional weight
const dimWeight = (dims.l * dims.w * dims.h) / dimDivisor;
// Use the greater of actual or dimensional weight
return Math.max(actualWeight, dimWeight);
};
// Helper to get zone from distance
const getZoneFromDistance = () => {
const destField = destinationSection.address('destAddress');
const miles = destField?.distance();
if (miles == null) return 'national'; // default
if (miles < 50) return 'local';
if (miles < 150) return 'regional';
if (miles < 1800) return 'national';
return 'national'; // cross-country still uses national rates
};
const getBaseShippingCost = () => {
const zone = getZoneFromDistance();
const speed = speedSection.radioButton('speed')?.value() || 'standard';
const billableWeight = getBillableWeight();
const ratePerLb = zoneRates[zone] || 1.00;
const speedMult = speedMultipliers[speed] || 1.3;
const handling = baseHandlingFees[speed] || 6.99;
// Add distance-based surcharge for long hauls
const destField = destinationSection.address('destAddress');
const miles = destField?.distance() ?? 500;
const distanceSurcharge = miles > 1000 ? (miles - 1000) * 0.005 * billableWeight : 0;
return handling + (billableWeight * ratePerLb * speedMult) + distanceSurcharge;
};
summarySection.addRow(row => {
row.addPriceDisplay('baseRate', {
label: 'Base Shipping Rate',
computedValue: () => {
return Math.round(getBaseShippingCost() * 100) / 100;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('weightInfo', {
label: () => {
const actualWeight = packageSection.decimal('weight')?.value() || 2;
const billableWeight = getBillableWeight();
if (billableWeight > actualWeight) {
return 'Billable Weight (DIM)';
}
return 'Billable Weight';
},
computedValue: () => getBillableWeight(),
variant: 'default',
suffix: ' lbs',
prefix: ''
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('surcharges', {
label: 'Surcharges & Fees',
computedValue: () => {
let surcharges = 0;
// Content-based surcharges
const contents = packageSection.dropdown('contents')?.value();
if (contents === 'fragile') surcharges += 5;
if (contents === 'perishable') surcharges += 10;
// Delivery type discount
const deliveryType = destinationSection.dropdown('deliveryType')?.value();
if (deliveryType === 'commercial') surcharges -= 2;
return surcharges;
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('optionsTotal', {
label: 'Options & Services',
computedValue: () => {
let options = 0;
if (optionsSection.checkbox('signature')?.value()) options += 3;
if (optionsSection.checkbox('insurance')?.value()) {
const value = packageSection.decimal('declaredValue')?.value() || 50;
options += Math.max(2, value * 0.01); // 1% of value, min $2
}
if (optionsSection.checkbox('tracking')?.value()) options += 2;
if (optionsSection.checkbox('saturdayDelivery')?.value()) options += 15;
if (optionsSection.checkbox('packagingService')?.value()) options += 8;
if (optionsSection.checkbox('pickupService')?.value()) options += 5;
return Math.round(options * 100) / 100;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalShipping', {
label: 'Estimated Shipping Cost',
computedValue: () => {
let total = getBaseShippingCost();
// Content surcharges
const contents = packageSection.dropdown('contents')?.value();
if (contents === 'fragile') total += 5;
if (contents === 'perishable') total += 10;
// Delivery type
const deliveryType = destinationSection.dropdown('deliveryType')?.value();
if (deliveryType === 'commercial') total -= 2;
// Options
if (optionsSection.checkbox('signature')?.value()) total += 3;
if (optionsSection.checkbox('insurance')?.value()) {
const value = packageSection.decimal('declaredValue')?.value() || 50;
total += Math.max(2, value * 0.01);
}
if (optionsSection.checkbox('tracking')?.value()) total += 2;
if (optionsSection.checkbox('saturdayDelivery')?.value()) total += 15;
if (optionsSection.checkbox('packagingService')?.value()) total += 8;
if (optionsSection.checkbox('pickupService')?.value()) total += 5;
return Math.round(total * 100) / 100;
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('deliveryEstimate', {
computedValue: () => {
const speed = speedSection.radioButton('speed')?.value() || 'standard';
const destField = destinationSection.address('destAddress');
const miles = destField?.distance();
// Determine zone from distance
let zone = 'national';
if (miles != null) {
if (miles < 50) zone = 'local';
else if (miles < 150) zone = 'regional';
else if (miles < 600) zone = 'short';
else if (miles < 1400) zone = 'medium';
else zone = 'long';
}
const estimates: Record<string, Record<string, string>> = {
'economy': {
'local': '5-7 days',
'regional': '6-8 days',
'short': '7-9 days',
'medium': '8-10 days',
'long': '9-12 days',
'national': '7-10 days'
},
'standard': {
'local': '3-5 days',
'regional': '4-5 days',
'short': '5-6 days',
'medium': '5-7 days',
'long': '6-8 days',
'national': '5-7 days'
},
'express': {
'local': '1-2 days',
'regional': '2-3 days',
'short': '2-3 days',
'medium': '2-3 days',
'long': '3-4 days',
'national': '2-3 days'
},
'overnight': {
'local': 'Next business day',
'regional': 'Next business day',
'short': 'Next business day',
'medium': 'Next business day',
'long': 'Next business day',
'national': 'Next business day'
}
};
const estimate = estimates[speed]?.[zone] || '5-7 business days';
const distanceText = miles != null ? ` (${Math.round(miles)} miles)` : '';
return `Estimated delivery: ${estimate}${distanceText}`;
},
customStyles: { 'font-size': '0.95rem', 'color': '#059669', 'font-weight': '500' }
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Rates are estimates. Final cost calculated at checkout.',
customStyles: { 'font-size': '0.85rem', 'color': '#94a3b8', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Calculate Shipping'
});
}