export function printShopCalculator(form: FormTs) {
// Product base prices (per unit at base quantity)
const productPrices: Record<string, Record<string, number>> = {
'business-cards': { '100': 0.15, '250': 0.10, '500': 0.07, '1000': 0.05, '2500': 0.04 },
'flyers': { '100': 0.35, '250': 0.25, '500': 0.18, '1000': 0.12, '2500': 0.09 },
'brochures': { '100': 0.85, '250': 0.65, '500': 0.50, '1000': 0.38, '2500': 0.30 },
'postcards': { '100': 0.25, '250': 0.18, '500': 0.12, '1000': 0.08, '2500': 0.06 },
'posters': { '10': 8, '25': 6, '50': 4.5, '100': 3.5, '250': 2.5 },
'banners': { '1': 75, '2': 65, '5': 55, '10': 45, '25': 35 },
'booklets': { '25': 4.5, '50': 3.5, '100': 2.8, '250': 2.2, '500': 1.8 },
'stickers': { '100': 0.20, '250': 0.15, '500': 0.10, '1000': 0.07, '2500': 0.05 }
};
// Paper stock multipliers
const paperMultipliers: Record<string, number> = {
'standard': 1.0,
'premium': 1.3,
'recycled': 1.15,
'glossy': 1.2,
'matte': 1.1,
'uncoated': 1.0,
'linen': 1.4,
'kraft': 1.25
};
// Finish options costs
const finishCosts: Record<string, number> = {
'none': 0,
'gloss-uv': 0.02,
'matte-uv': 0.02,
'soft-touch': 0.05,
'spot-uv': 0.08,
'foil': 0.15,
'emboss': 0.12
};
// Turnaround multipliers
const turnaroundMultipliers: Record<string, number> = {
'standard': 1.0,
'express': 1.25,
'rush': 1.5,
'same-day': 2.0
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Get Your Print Quote',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Product Selection Section
const productSection = form.addSubform('productDetails', { title: '๐ Product Selection' });
productSection.addRow(row => {
row.addDropdown('productType', {
label: 'Product Type',
options: [
{ id: 'business-cards', name: 'Business Cards' },
{ id: 'flyers', name: 'Flyers / Leaflets' },
{ id: 'brochures', name: 'Brochures (Tri-fold)' },
{ id: 'postcards', name: 'Postcards' },
{ id: 'posters', name: 'Posters' },
{ id: 'banners', name: 'Vinyl Banners' },
{ id: 'booklets', name: 'Booklets / Catalogs' },
{ id: 'stickers', name: 'Stickers / Labels' }
],
defaultValue: 'business-cards',
isRequired: true
}, '1fr');
row.addDropdown('quantity', {
label: 'Quantity',
options: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
if (product === 'posters') {
return [
{ id: '10', name: '10' },
{ id: '25', name: '25' },
{ id: '50', name: '50' },
{ id: '100', name: '100' },
{ id: '250', name: '250' }
];
} else if (product === 'banners') {
return [
{ id: '1', name: '1' },
{ id: '2', name: '2' },
{ id: '5', name: '5' },
{ id: '10', name: '10' },
{ id: '25', name: '25' }
];
} else if (product === 'booklets') {
return [
{ id: '25', name: '25' },
{ id: '50', name: '50' },
{ id: '100', name: '100' },
{ id: '250', name: '250' },
{ id: '500', name: '500' }
];
} else {
return [
{ id: '100', name: '100' },
{ id: '250', name: '250' },
{ id: '500', name: '500' },
{ id: '1000', name: '1,000' },
{ id: '2500', name: '2,500' }
];
}
},
defaultValue: '250',
isRequired: true
}, '1fr');
});
productSection.addRow(row => {
row.addDropdown('size', {
label: 'Size',
options: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
if (product === 'business-cards') {
return [
{ id: 'standard', name: 'Standard (3.5" x 2")' },
{ id: 'square', name: 'Square (2.5" x 2.5")' },
{ id: 'euro', name: 'Euro (3.346" x 2.165")' }
];
} else if (product === 'flyers' || product === 'postcards') {
return [
{ id: '4x6', name: '4" x 6"' },
{ id: '5x7', name: '5" x 7"' },
{ id: '8.5x11', name: '8.5" x 11"' },
{ id: '11x17', name: '11" x 17"' }
];
} else if (product === 'posters') {
return [
{ id: '11x17', name: '11" x 17"' },
{ id: '18x24', name: '18" x 24"' },
{ id: '24x36', name: '24" x 36"' }
];
} else if (product === 'banners') {
return [
{ id: '2x4', name: '2\' x 4\'' },
{ id: '3x6', name: '3\' x 6\'' },
{ id: '4x8', name: '4\' x 8\'' }
];
} else {
return [
{ id: 'standard', name: 'Standard' },
{ id: 'large', name: 'Large (+20%)' }
];
}
},
defaultValue: 'standard',
isRequired: true
}, '1fr');
row.addRadioButton('sides', {
label: 'Print Sides',
options: [
{ id: 'single', name: 'Single-Sided' },
{ id: 'double', name: 'Double-Sided (+40%)' }
],
defaultValue: 'double',
orientation: 'horizontal',
isRequired: true,
isVisible: () => {
const product = productSection.dropdown('productType')?.value();
return product !== 'banners' && product !== 'posters';
}
}, '1fr');
});
// Paper & Finish Section
const paperSection = form.addSubform('paperOptions', { title: '๐ Paper & Finish' });
paperSection.addRow(row => {
row.addDropdown('paperStock', {
label: 'Paper Stock',
options: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
if (product === 'banners') {
return [
{ id: 'standard', name: '13oz Vinyl' },
{ id: 'premium', name: '18oz Vinyl (+30%)' }
];
} else if (product === 'stickers') {
return [
{ id: 'standard', name: 'White Vinyl' },
{ id: 'glossy', name: 'Glossy Paper (+20%)' },
{ id: 'kraft', name: 'Kraft Paper (+25%)' }
];
} else {
return [
{ id: 'standard', name: 'Standard 14pt' },
{ id: 'premium', name: 'Premium 16pt (+30%)' },
{ id: 'glossy', name: 'Glossy 100lb (+20%)' },
{ id: 'matte', name: 'Matte 100lb (+10%)' },
{ id: 'uncoated', name: 'Uncoated 80lb' },
{ id: 'linen', name: 'Linen Texture (+40%)' },
{ id: 'recycled', name: 'Recycled (+15%)' }
];
}
},
defaultValue: 'standard',
isRequired: true
}, '1fr');
row.addDropdown('finish', {
label: 'Finish / Coating',
options: [
{ id: 'none', name: 'No Coating' },
{ id: 'gloss-uv', name: 'Gloss UV (+$0.02/pc)' },
{ id: 'matte-uv', name: 'Matte UV (+$0.02/pc)' },
{ id: 'soft-touch', name: 'Soft Touch Laminate (+$0.05/pc)' },
{ id: 'spot-uv', name: 'Spot UV (+$0.08/pc)' },
{ id: 'foil', name: 'Foil Stamping (+$0.15/pc)' },
{ id: 'emboss', name: 'Embossing (+$0.12/pc)' }
],
defaultValue: 'none',
isRequired: true,
isVisible: () => {
const product = productSection.dropdown('productType')?.value();
return product !== 'banners';
}
}, '1fr');
});
paperSection.addRow(row => {
row.addRadioButton('color', {
label: 'Print Color',
options: [
{ id: 'full', name: 'Full Color (CMYK)' },
{ id: 'bw', name: 'Black & White (-30%)' }
],
defaultValue: 'full',
orientation: 'horizontal',
isRequired: true
});
});
// Design & Turnaround Section
const designSection = form.addSubform('designOptions', { title: '๐จ Design & Delivery' });
designSection.addRow(row => {
row.addDropdown('designService', {
label: 'Design Service',
options: [
{ id: 'none', name: 'No Design Needed (Print-Ready File)' },
{ id: 'tweak', name: 'Minor Tweaks ($15)' },
{ id: 'basic', name: 'Basic Design ($35)' },
{ id: 'custom', name: 'Custom Design ($75)' },
{ id: 'premium', name: 'Premium Design ($150)' }
],
defaultValue: 'none',
isRequired: true
}, '1fr');
row.addDropdown('turnaround', {
label: 'Turnaround Time',
options: [
{ id: 'standard', name: 'Standard (5-7 business days)' },
{ id: 'express', name: 'Express (3-4 days) (+25%)' },
{ id: 'rush', name: 'Rush (1-2 days) (+50%)' },
{ id: 'same-day', name: 'Same Day (+100%)' }
],
defaultValue: 'standard',
isRequired: true
}, '1fr');
});
designSection.addRow(row => {
row.addCheckbox('proofApproval', {
label: 'Physical Proof ($10)',
defaultValue: false
}, '1fr');
row.addCheckbox('packagingIndividual', {
label: 'Individual Packaging (+$0.05/pc)',
defaultValue: false
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Price Summary Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Quote Summary', isCollapsible: false });
summarySection.addRow(row => {
row.addPriceDisplay('baseCost', {
label: 'Base Printing Cost',
computedValue: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
const quantity = productSection.dropdown('quantity')?.value() || '250';
const sides = productSection.radioButton('sides')?.value() || 'double';
const color = paperSection.radioButton('color')?.value() || 'full';
const size = productSection.dropdown('size')?.value() || 'standard';
const prices = productPrices[product] || productPrices['business-cards'];
const basePrice = prices?.[quantity] ?? 0.10;
const qty = parseInt(quantity) || 250;
let total = basePrice * qty;
// Double-sided surcharge
if (sides === 'double' && product !== 'banners' && product !== 'posters') {
total *= 1.4;
}
// B&W discount
if (color === 'bw') {
total *= 0.7;
}
// Size adjustments
if (size === 'large' || size === '11x17' || size === '24x36' || size === '4x8') {
total *= 1.2;
}
return total;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('paperCost', {
label: 'Paper Upgrade',
computedValue: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
const quantity = productSection.dropdown('quantity')?.value() || '250';
const paperStock = paperSection.dropdown('paperStock')?.value() || 'standard';
const sides = productSection.radioButton('sides')?.value() || 'double';
const color = paperSection.radioButton('color')?.value() || 'full';
const prices = productPrices[product] || productPrices['business-cards'];
const basePrice = prices?.[quantity] ?? 0.10;
const qty = parseInt(quantity) || 250;
let base = basePrice * qty;
if (sides === 'double' && product !== 'banners' && product !== 'posters') base *= 1.4;
if (color === 'bw') base *= 0.7;
const multiplier = paperMultipliers?.[paperStock] ?? 1.0;
return base * (multiplier - 1);
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('finishCost', {
label: 'Finish / Coating',
computedValue: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
const quantity = productSection.dropdown('quantity')?.value() || '250';
const finish = paperSection.dropdown('finish')?.value() || 'none';
if (product === 'banners') return 0;
const qty = parseInt(quantity) || 250;
const cost = finishCosts?.[finish] ?? 0;
return cost * qty;
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('designCost', {
label: 'Design Service',
computedValue: () => {
const design = designSection.dropdown('designService')?.value() || 'none';
const costs: Record<string, number> = {
'none': 0,
'tweak': 15,
'basic': 35,
'custom': 75,
'premium': 150
};
return costs?.[design] ?? 0;
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('addonsCost', {
label: 'Add-ons',
computedValue: () => {
const quantity = productSection.dropdown('quantity')?.value() || '250';
const qty = parseInt(quantity) || 250;
let total = 0;
if (designSection.checkbox('proofApproval')?.value()) total += 10;
if (designSection.checkbox('packagingIndividual')?.value()) total += qty * 0.05;
return total;
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('rushCost', {
label: 'Rush Fee',
computedValue: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
const quantity = productSection.dropdown('quantity')?.value() || '250';
const paperStock = paperSection.dropdown('paperStock')?.value() || 'standard';
const finish = paperSection.dropdown('finish')?.value() || 'none';
const sides = productSection.radioButton('sides')?.value() || 'double';
const color = paperSection.radioButton('color')?.value() || 'full';
const turnaround = designSection.dropdown('turnaround')?.value() || 'standard';
const prices = productPrices[product] || productPrices['business-cards'];
const basePrice = prices?.[quantity] ?? 0.10;
const qty = parseInt(quantity) || 250;
let base = basePrice * qty;
if (sides === 'double' && product !== 'banners' && product !== 'posters') base *= 1.4;
if (color === 'bw') base *= 0.7;
base *= paperMultipliers?.[paperStock] ?? 1.0;
if (product !== 'banners') base += (finishCosts?.[finish] ?? 0) * qty;
const multiplier = turnaroundMultipliers?.[turnaround] ?? 1.0;
return base * (multiplier - 1);
},
variant: 'default',
prefix: '+'
}, '1fr');
});
const finalSection = form.addSubform('final', {
title: '๐งพ Total',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalPrice', {
label: 'Total Price',
computedValue: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
const quantity = productSection.dropdown('quantity')?.value() || '250';
const paperStock = paperSection.dropdown('paperStock')?.value() || 'standard';
const finish = paperSection.dropdown('finish')?.value() || 'none';
const design = designSection.dropdown('designService')?.value() || 'none';
const turnaround = designSection.dropdown('turnaround')?.value() || 'standard';
const sides = productSection.radioButton('sides')?.value() || 'double';
const color = paperSection.radioButton('color')?.value() || 'full';
const size = productSection.dropdown('size')?.value() || 'standard';
const prices = productPrices[product] || productPrices['business-cards'];
const basePrice = prices?.[quantity] ?? 0.10;
const qty = parseInt(quantity) || 250;
// Base printing cost
let total = basePrice * qty;
// Double-sided
if (sides === 'double' && product !== 'banners' && product !== 'posters') {
total *= 1.4;
}
// B&W discount
if (color === 'bw') {
total *= 0.7;
}
// Size adjustment
if (size === 'large' || size === '11x17' || size === '24x36' || size === '4x8') {
total *= 1.2;
}
// Paper stock
total *= paperMultipliers?.[paperStock] ?? 1.0;
// Finish
if (product !== 'banners') {
total += (finishCosts?.[finish] ?? 0) * qty;
}
// Turnaround
total *= turnaroundMultipliers?.[turnaround] ?? 1.0;
// Design
const designCosts: Record<string, number> = {
'none': 0, 'tweak': 15, 'basic': 35, 'custom': 75, 'premium': 150
};
total += designCosts?.[design] ?? 0;
// Add-ons
if (designSection.checkbox('proofApproval')?.value()) total += 10;
if (designSection.checkbox('packagingIndividual')?.value()) total += qty * 0.05;
return Math.round(total * 100) / 100;
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('perUnit', {
computedValue: () => {
const product = productSection.dropdown('productType')?.value() || 'business-cards';
const quantity = productSection.dropdown('quantity')?.value() || '250';
const paperStock = paperSection.dropdown('paperStock')?.value() || 'standard';
const finish = paperSection.dropdown('finish')?.value() || 'none';
const turnaround = designSection.dropdown('turnaround')?.value() || 'standard';
const sides = productSection.radioButton('sides')?.value() || 'double';
const color = paperSection.radioButton('color')?.value() || 'full';
const size = productSection.dropdown('size')?.value() || 'standard';
const prices = productPrices[product] || productPrices['business-cards'];
const basePrice = prices?.[quantity] ?? 0.10;
const qty = parseInt(quantity) || 250;
let total = basePrice * qty;
if (sides === 'double' && product !== 'banners' && product !== 'posters') total *= 1.4;
if (color === 'bw') total *= 0.7;
if (size === 'large' || size === '11x17' || size === '24x36' || size === '4x8') total *= 1.2;
total *= paperMultipliers?.[paperStock] ?? 1.0;
if (product !== 'banners') total += (finishCosts?.[finish] ?? 0) * qty;
total *= turnaroundMultipliers?.[turnaround] ?? 1.0;
const perUnit = total / qty;
return `$${(Number(perUnit) || 0).toFixed(3)} per piece`;
},
customStyles: { 'font-size': '0.9rem', 'color': '#64748b', 'text-align': 'center' }
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices are estimates. Final price confirmed upon file review.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Submit Order'
});
}