export function customProductBuilderCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Custom Product Builder',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Product Selection Section
const productSection = form.addSubform('product', { title: '👕 Product Selection' });
productSection.addRow(row => {
row.addDropdown('productType', {
label: 'Product Type',
options: [
{ id: 'tshirt', name: 'T-Shirt' },
{ id: 'polo', name: 'Polo Shirt' },
{ id: 'hoodie', name: 'Hoodie / Sweatshirt' },
{ id: 'tank', name: 'Tank Top' },
{ id: 'longsleeve', name: 'Long Sleeve Shirt' },
{ id: 'hat', name: 'Hat / Cap' },
{ id: 'mug', name: 'Mug' },
{ id: 'tote', name: 'Tote Bag' },
{ id: 'apron', name: 'Apron' },
{ id: 'jacket', name: 'Jacket' }
],
defaultValue: 'tshirt',
isRequired: true
}, '1fr');
row.addDropdown('quality', {
label: 'Product Quality',
options: [
{ id: 'economy', name: 'Economy' },
{ id: 'standard', name: 'Standard' },
{ id: 'premium', name: 'Premium' },
{ id: 'luxury', name: 'Luxury / Retail' }
],
defaultValue: 'standard',
tooltip: 'Higher quality = better fabric and fit'
}, '1fr');
});
productSection.addRow(row => {
row.addDropdown('color', {
label: 'Product Color',
options: [
{ id: 'white', name: 'White' },
{ id: 'black', name: 'Black' },
{ id: 'navy', name: 'Navy' },
{ id: 'gray', name: 'Gray' },
{ id: 'red', name: 'Red' },
{ id: 'royal', name: 'Royal Blue' },
{ id: 'other', name: 'Other / Custom' }
],
defaultValue: 'white',
tooltip: 'Dark colors may affect printing cost'
}, '1fr');
row.addCheckbox('mixedSizes', {
label: 'Mixed sizes (S-3XL)',
defaultValue: true,
tooltip: 'Order includes multiple sizes'
}, '1fr');
});
// Quantity Section
const quantitySection = form.addSubform('quantity', { title: '📦 Quantity' });
quantitySection.addRow(row => {
row.addInteger('totalQty', {
label: 'Total Quantity',
min: 1,
max: 10000,
defaultValue: 50,
isRequired: true
}, '1fr');
row.addCheckbox('plusSizes', {
label: 'Include Plus Sizes (2XL-5XL)',
defaultValue: false,
tooltip: 'Plus sizes have additional cost',
isVisible: () => ['tshirt', 'polo', 'hoodie', 'longsleeve', 'jacket'].includes(productSection.dropdown('productType')?.value() || '')
}, '1fr');
});
// Decoration Section
const decorationSection = form.addSubform('decoration', { title: '🎨 Decoration' });
decorationSection.addRow(row => {
row.addDropdown('method', {
label: 'Decoration Method',
options: [
{ id: 'screenprint', name: 'Screen Printing' },
{ id: 'dtg', name: 'DTG (Direct-to-Garment)' },
{ id: 'heattransfer', name: 'Heat Transfer / Vinyl' },
{ id: 'embroidery', name: 'Embroidery' },
{ id: 'sublimation', name: 'Sublimation' }
],
defaultValue: 'screenprint',
isRequired: true
}, '1fr');
row.addDropdown('locations', {
label: 'Print Locations',
options: [
{ id: '1', name: '1 Location (Front OR Back)' },
{ id: '2', name: '2 Locations (Front AND Back)' },
{ id: '3', name: '3 Locations (+ Sleeve)' },
{ id: '4', name: '4 Locations (Full Coverage)' }
],
defaultValue: '1'
}, '1fr');
});
decorationSection.addRow(row => {
row.addInteger('colors', {
label: 'Number of Ink Colors',
min: 1,
max: 12,
defaultValue: 2,
isVisible: () => ['screenprint', 'heattransfer'].includes(decorationSection.dropdown('method')?.value() || ''),
tooltip: 'Each color adds to setup cost'
}, '1fr');
row.addDropdown('designSize', {
label: 'Design Size',
options: [
{ id: 'small', name: 'Small (up to 4")' },
{ id: 'medium', name: 'Medium (up to 8")' },
{ id: 'large', name: 'Large (up to 12")' },
{ id: 'oversized', name: 'Oversized (12"+)' }
],
defaultValue: 'medium'
}, '1fr');
});
decorationSection.addRow(row => {
row.addInteger('stitchCount', {
label: 'Stitch Count (thousands)',
min: 1,
max: 50,
defaultValue: 8,
isVisible: () => decorationSection.dropdown('method')?.value() === 'embroidery',
tooltip: 'Embroidery priced per 1000 stitches'
});
});
// Add-ons Section
const addonsSection = form.addSubform('addons', { title: '✨ Add-ons' });
addonsSection.addRow(row => {
row.addCheckbox('names', {
label: 'Individual Names',
defaultValue: false,
tooltip: 'Add different name to each item'
}, '1fr');
row.addCheckbox('numbers', {
label: 'Individual Numbers',
defaultValue: false,
tooltip: 'Add different number to each item'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('tags', {
label: 'Custom Tags / Labels',
defaultValue: false,
tooltip: 'Replace manufacturer tags with yours'
}, '1fr');
row.addCheckbox('folding', {
label: 'Individual Polybag & Fold',
defaultValue: false,
tooltip: 'Professional packaging per item'
}, '1fr');
});
addonsSection.addRow(row => {
row.addCheckbox('rush', {
label: 'Rush Order (3-5 days)',
defaultValue: false,
tooltip: 'Standard is 10-14 business days'
}, '1fr');
row.addCheckbox('designService', {
label: 'Design Service',
defaultValue: false,
tooltip: 'Professional design assistance'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Breakdown Section
const breakdownSection = form.addSubform('breakdown', { title: '📊 Price Breakdown', isCollapsible: true });
breakdownSection.addRow(row => {
row.addPriceDisplay('productCost', {
label: 'Product Cost (per unit)',
computedValue: () => {
const productType = productSection.dropdown('productType')?.value() || 'tshirt';
const quality = productSection.dropdown('quality')?.value() || 'standard';
const basePrices: Record<string, Record<string, number>> = {
'tshirt': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 15 },
'polo': { 'economy': 8, 'standard': 12, 'premium': 18, 'luxury': 30 },
'hoodie': { 'economy': 15, 'standard': 22, 'premium': 32, 'luxury': 50 },
'tank': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 14 },
'longsleeve': { 'economy': 5, 'standard': 8, 'premium': 12, 'luxury': 22 },
'hat': { 'economy': 4, 'standard': 7, 'premium': 12, 'luxury': 20 },
'mug': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 12 },
'tote': { 'economy': 2, 'standard': 4, 'premium': 7, 'luxury': 12 },
'apron': { 'economy': 6, 'standard': 10, 'premium': 16, 'luxury': 25 },
'jacket': { 'economy': 25, 'standard': 40, 'premium': 60, 'luxury': 100 }
};
return basePrices[productType]?.[quality] || 5;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('decorationCost', {
label: 'Decoration (per unit)',
computedValue: () => {
const method = decorationSection.dropdown('method')?.value() || 'screenprint';
const locations = parseInt(decorationSection.dropdown('locations')?.value() || '1');
const colors = decorationSection.integer('colors')?.value() || 2;
const designSize = decorationSection.dropdown('designSize')?.value() || 'medium';
const stitchCount = decorationSection.integer('stitchCount')?.value() || 8;
const qty = quantitySection.integer('totalQty')?.value() || 50;
const productColor = productSection.dropdown('color')?.value() || 'white';
const isDark = ['black', 'navy'].includes(productColor);
let costPerUnit = 0;
const sizeMultipliers: Record<string, number> = {
'small': 0.8, 'medium': 1, 'large': 1.3, 'oversized': 1.6
};
if (method === 'screenprint') {
// Base cost decreases with quantity
const baseCost = qty < 24 ? 4 : qty < 72 ? 3 : qty < 144 ? 2.5 : qty < 500 ? 2 : 1.5;
costPerUnit = baseCost * colors * locations * (sizeMultipliers[designSize] || 1);
if (isDark) costPerUnit *= 1.2; // Under-base needed
} else if (method === 'dtg') {
costPerUnit = 8 * locations * (sizeMultipliers[designSize] || 1);
if (isDark) costPerUnit *= 1.5;
} else if (method === 'heattransfer') {
costPerUnit = 5 * colors * locations * (sizeMultipliers[designSize] || 1);
} else if (method === 'embroidery') {
costPerUnit = (stitchCount * 0.5) * locations;
} else if (method === 'sublimation') {
costPerUnit = 6 * locations * (sizeMultipliers[designSize] || 1);
}
return Math.round(costPerUnit * 100) / 100;
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('setupFees', {
label: 'Setup Fees (one-time)',
computedValue: () => {
const method = decorationSection.dropdown('method')?.value() || 'screenprint';
const locations = parseInt(decorationSection.dropdown('locations')?.value() || '1');
const colors = decorationSection.integer('colors')?.value() || 2;
if (method === 'screenprint') {
return colors * locations * 30; // $30 per screen
} else if (method === 'embroidery') {
return locations * 50; // $50 digitizing per location
} else if (method === 'dtg' || method === 'sublimation') {
return 0; // No setup
} else if (method === 'heattransfer') {
return 25; // Cutting setup
}
return 0;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('addonsCost', {
label: 'Add-ons (per unit)',
computedValue: () => {
let cost = 0;
if (addonsSection.checkbox('names')?.value()) cost += 3;
if (addonsSection.checkbox('numbers')?.value()) cost += 2;
if (addonsSection.checkbox('tags')?.value()) cost += 1.5;
if (addonsSection.checkbox('folding')?.value()) cost += 1;
return cost;
},
variant: 'default'
}, '1fr');
});
// Quote Section
const quoteSection = form.addSubform('quote', { title: '💰 Your Quote', isCollapsible: false });
quoteSection.addRow(row => {
row.addPriceDisplay('pricePerUnit', {
label: 'Price Per Unit',
computedValue: () => {
const productType = productSection.dropdown('productType')?.value() || 'tshirt';
const quality = productSection.dropdown('quality')?.value() || 'standard';
const method = decorationSection.dropdown('method')?.value() || 'screenprint';
const locations = parseInt(decorationSection.dropdown('locations')?.value() || '1');
const colors = decorationSection.integer('colors')?.value() || 2;
const designSize = decorationSection.dropdown('designSize')?.value() || 'medium';
const stitchCount = decorationSection.integer('stitchCount')?.value() || 8;
const qty = quantitySection.integer('totalQty')?.value() || 50;
const productColor = productSection.dropdown('color')?.value() || 'white';
const isDark = ['black', 'navy'].includes(productColor);
const plusSizes = quantitySection.checkbox('plusSizes')?.value();
const basePrices: Record<string, Record<string, number>> = {
'tshirt': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 15 },
'polo': { 'economy': 8, 'standard': 12, 'premium': 18, 'luxury': 30 },
'hoodie': { 'economy': 15, 'standard': 22, 'premium': 32, 'luxury': 50 },
'tank': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 14 },
'longsleeve': { 'economy': 5, 'standard': 8, 'premium': 12, 'luxury': 22 },
'hat': { 'economy': 4, 'standard': 7, 'premium': 12, 'luxury': 20 },
'mug': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 12 },
'tote': { 'economy': 2, 'standard': 4, 'premium': 7, 'luxury': 12 },
'apron': { 'economy': 6, 'standard': 10, 'premium': 16, 'luxury': 25 },
'jacket': { 'economy': 25, 'standard': 40, 'premium': 60, 'luxury': 100 }
};
let productCost = basePrices[productType]?.[quality] || 5;
if (plusSizes) productCost *= 1.15; // 15% premium for plus sizes average
const sizeMultipliers: Record<string, number> = {
'small': 0.8, 'medium': 1, 'large': 1.3, 'oversized': 1.6
};
let decorationCost = 0;
if (method === 'screenprint') {
const baseCost = qty < 24 ? 4 : qty < 72 ? 3 : qty < 144 ? 2.5 : qty < 500 ? 2 : 1.5;
decorationCost = baseCost * colors * locations * (sizeMultipliers[designSize] || 1);
if (isDark) decorationCost *= 1.2;
} else if (method === 'dtg') {
decorationCost = 8 * locations * (sizeMultipliers[designSize] || 1);
if (isDark) decorationCost *= 1.5;
} else if (method === 'heattransfer') {
decorationCost = 5 * colors * locations * (sizeMultipliers[designSize] || 1);
} else if (method === 'embroidery') {
decorationCost = (stitchCount * 0.5) * locations;
} else if (method === 'sublimation') {
decorationCost = 6 * locations * (sizeMultipliers[designSize] || 1);
}
let addonsCost = 0;
if (addonsSection.checkbox('names')?.value()) addonsCost += 3;
if (addonsSection.checkbox('numbers')?.value()) addonsCost += 2;
if (addonsSection.checkbox('tags')?.value()) addonsCost += 1.5;
if (addonsSection.checkbox('folding')?.value()) addonsCost += 1;
let totalPerUnit = productCost + decorationCost + addonsCost;
// Rush surcharge
if (addonsSection.checkbox('rush')?.value()) {
totalPerUnit *= 1.3;
}
return Math.round(totalPerUnit * 100) / 100;
},
variant: 'success'
});
});
quoteSection.addRow(row => {
row.addPriceDisplay('orderTotal', {
label: 'Order Total',
computedValue: () => {
const productType = productSection.dropdown('productType')?.value() || 'tshirt';
const quality = productSection.dropdown('quality')?.value() || 'standard';
const method = decorationSection.dropdown('method')?.value() || 'screenprint';
const locations = parseInt(decorationSection.dropdown('locations')?.value() || '1');
const colors = decorationSection.integer('colors')?.value() || 2;
const designSize = decorationSection.dropdown('designSize')?.value() || 'medium';
const stitchCount = decorationSection.integer('stitchCount')?.value() || 8;
const qty = quantitySection.integer('totalQty')?.value() || 50;
const productColor = productSection.dropdown('color')?.value() || 'white';
const isDark = ['black', 'navy'].includes(productColor);
const plusSizes = quantitySection.checkbox('plusSizes')?.value();
const basePrices: Record<string, Record<string, number>> = {
'tshirt': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 15 },
'polo': { 'economy': 8, 'standard': 12, 'premium': 18, 'luxury': 30 },
'hoodie': { 'economy': 15, 'standard': 22, 'premium': 32, 'luxury': 50 },
'tank': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 14 },
'longsleeve': { 'economy': 5, 'standard': 8, 'premium': 12, 'luxury': 22 },
'hat': { 'economy': 4, 'standard': 7, 'premium': 12, 'luxury': 20 },
'mug': { 'economy': 3, 'standard': 5, 'premium': 8, 'luxury': 12 },
'tote': { 'economy': 2, 'standard': 4, 'premium': 7, 'luxury': 12 },
'apron': { 'economy': 6, 'standard': 10, 'premium': 16, 'luxury': 25 },
'jacket': { 'economy': 25, 'standard': 40, 'premium': 60, 'luxury': 100 }
};
let productCost = basePrices[productType]?.[quality] || 5;
if (plusSizes) productCost *= 1.15;
const sizeMultipliers: Record<string, number> = {
'small': 0.8, 'medium': 1, 'large': 1.3, 'oversized': 1.6
};
let decorationCost = 0;
if (method === 'screenprint') {
const baseCost = qty < 24 ? 4 : qty < 72 ? 3 : qty < 144 ? 2.5 : qty < 500 ? 2 : 1.5;
decorationCost = baseCost * colors * locations * (sizeMultipliers[designSize] || 1);
if (isDark) decorationCost *= 1.2;
} else if (method === 'dtg') {
decorationCost = 8 * locations * (sizeMultipliers[designSize] || 1);
if (isDark) decorationCost *= 1.5;
} else if (method === 'heattransfer') {
decorationCost = 5 * colors * locations * (sizeMultipliers[designSize] || 1);
} else if (method === 'embroidery') {
decorationCost = (stitchCount * 0.5) * locations;
} else if (method === 'sublimation') {
decorationCost = 6 * locations * (sizeMultipliers[designSize] || 1);
}
let addonsCost = 0;
if (addonsSection.checkbox('names')?.value()) addonsCost += 3;
if (addonsSection.checkbox('numbers')?.value()) addonsCost += 2;
if (addonsSection.checkbox('tags')?.value()) addonsCost += 1.5;
if (addonsSection.checkbox('folding')?.value()) addonsCost += 1;
let totalPerUnit = productCost + decorationCost + addonsCost;
if (addonsSection.checkbox('rush')?.value()) totalPerUnit *= 1.3;
let orderTotal = totalPerUnit * qty;
// Add setup fees
if (method === 'screenprint') {
orderTotal += colors * locations * 30;
} else if (method === 'embroidery') {
orderTotal += locations * 50;
} else if (method === 'heattransfer') {
orderTotal += 25;
}
// Design service
if (addonsSection.checkbox('designService')?.value()) {
orderTotal += 75;
}
return Math.round(orderTotal * 100) / 100;
},
variant: 'large'
});
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '📋 Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addTextPanel('summaryText', {
computedValue: () => {
const productType = productSection.dropdown('productType')?.value() || 'tshirt';
const method = decorationSection.dropdown('method')?.value() || 'screenprint';
const qty = quantitySection.integer('totalQty')?.value() || 50;
const productLabels: Record<string, string> = {
'tshirt': 'T-Shirts', 'polo': 'Polos', 'hoodie': 'Hoodies',
'tank': 'Tanks', 'longsleeve': 'Long Sleeves', 'hat': 'Hats',
'mug': 'Mugs', 'tote': 'Totes', 'apron': 'Aprons', 'jacket': 'Jackets'
};
const methodLabels: Record<string, string> = {
'screenprint': 'Screen Print', 'dtg': 'DTG Print', 'heattransfer': 'Heat Transfer',
'embroidery': 'Embroidery', 'sublimation': 'Sublimation'
};
return `${qty} ${productLabels[productType]} | ${methodLabels[method]}`;
},
customStyles: { 'font-size': '0.95rem', 'font-weight': '500', 'text-align': 'center', 'color': '#1e293b' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices are estimates. Final quote may vary based on artwork review. Standard production: 10-14 business days.',
customStyles: { 'font-size': '0.8rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Request Quote'
});
}