export function productMarkupCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Product Markup Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Product Cost Section
const costSection = form.addSubform('cost', { title: '💰 Product Costs' });
costSection.addRow(row => {
row.addDecimal('productCost', {
label: 'Product/Unit Cost',
min: 0.01,
max: 100000,
defaultValue: 25,
prefix: '$',
isRequired: true,
tooltip: 'Cost to purchase or manufacture one unit'
}, '1fr');
row.addDropdown('costType', {
label: 'Cost Type',
options: [
{ id: 'wholesale', name: 'Wholesale Cost' },
{ id: 'manufacturing', name: 'Manufacturing Cost' },
{ id: 'landed', name: 'Landed Cost (incl. shipping)' },
{ id: 'full', name: 'Full Cost (incl. overhead)' }
],
defaultValue: 'wholesale'
}, '1fr');
});
costSection.addRow(row => {
row.addDecimal('shippingCost', {
label: 'Shipping Cost (per unit)',
min: 0,
max: 1000,
defaultValue: 0,
prefix: '$',
tooltip: 'Inbound shipping/freight cost per unit',
isVisible: () => costSection.dropdown('costType')?.value() !== 'landed'
}, '1fr');
row.addDecimal('packagingCost', {
label: 'Packaging Cost',
min: 0,
max: 100,
defaultValue: 0,
prefix: '$'
}, '1fr');
});
// Pricing Strategy Section
const strategySection = form.addSubform('strategy', { title: '🎯 Pricing Strategy' });
strategySection.addRow(row => {
row.addRadioButton('pricingMethod', {
label: 'Pricing Method',
options: [
{ id: 'markup', name: 'Markup Percentage (% on cost)' },
{ id: 'margin', name: 'Profit Margin (% of selling price)' },
{ id: 'target', name: 'Target Selling Price' }
],
defaultValue: 'markup',
isRequired: true
});
});
strategySection.addRow(row => {
row.addDecimal('markupPercent', {
label: 'Markup Percentage',
min: 0,
max: 1000,
defaultValue: 100,
suffix: '%',
tooltip: '100% markup = doubles your cost',
isVisible: () => strategySection.radioButton('pricingMethod')?.value() === 'markup'
}, '1fr');
row.addDecimal('marginPercent', {
label: 'Desired Profit Margin',
min: 0,
max: 99,
defaultValue: 50,
suffix: '%',
tooltip: '50% margin = you keep half of selling price',
isVisible: () => strategySection.radioButton('pricingMethod')?.value() === 'margin'
}, '1fr');
row.addDecimal('targetPrice', {
label: 'Target Selling Price',
min: 0,
max: 100000,
defaultValue: 50,
prefix: '$',
isVisible: () => strategySection.radioButton('pricingMethod')?.value() === 'target'
}, '1fr');
});
// Industry Benchmarks Section
const benchmarkSection = form.addSubform('benchmark', { title: '📊 Industry Benchmarks', isCollapsible: true });
benchmarkSection.addRow(row => {
row.addDropdown('industry', {
label: 'Select Industry',
options: [
{ id: 'retail', name: 'General Retail' },
{ id: 'fashion', name: 'Fashion/Apparel' },
{ id: 'electronics', name: 'Electronics' },
{ id: 'food', name: 'Food/Grocery' },
{ id: 'jewelry', name: 'Jewelry' },
{ id: 'furniture', name: 'Furniture' },
{ id: 'beauty', name: 'Beauty/Cosmetics' },
{ id: 'automotive', name: 'Auto Parts' },
{ id: 'pharma', name: 'Pharmacy' },
{ id: 'restaurant', name: 'Restaurant' },
{ id: 'ecommerce', name: 'E-commerce' }
],
defaultValue: 'retail'
});
});
benchmarkSection.addRow(row => {
row.addTextPanel('industryBenchmark', {
computedValue: () => {
const industry = benchmarkSection.dropdown('industry')?.value() || 'retail';
const benchmarks: Record<string, string> = {
'retail': 'Typical markup: 50-100% | Margin: 33-50%',
'fashion': 'Typical markup: 100-300% | Margin: 50-75%',
'electronics': 'Typical markup: 25-50% | Margin: 20-33%',
'food': 'Typical markup: 25-50% | Margin: 20-33%',
'jewelry': 'Typical markup: 100-300% | Margin: 50-75%',
'furniture': 'Typical markup: 80-150% | Margin: 44-60%',
'beauty': 'Typical markup: 100-400% | Margin: 50-80%',
'automotive': 'Typical markup: 30-50% | Margin: 23-33%',
'pharma': 'Typical markup: 30-100% | Margin: 23-50%',
'restaurant': 'Food: 200-400% | Beverages: 300-500%',
'ecommerce': 'Typical markup: 50-100% | Margin: 33-50%'
};
return benchmarks[industry] || 'Typical markup: 50-100%';
},
customStyles: { 'font-size': '0.9rem', 'color': '#059669', 'font-weight': '500' }
});
});
// Additional Costs Section
const additionalSection = form.addSubform('additional', { title: '📦 Additional Costs' });
additionalSection.addRow(row => {
row.addDecimal('platformFees', {
label: 'Platform/Marketplace Fees',
min: 0,
max: 50,
defaultValue: 0,
suffix: '%',
tooltip: 'Amazon: 15%, eBay: 12-15%, Etsy: 6.5%'
}, '1fr');
row.addDecimal('paymentFees', {
label: 'Payment Processing Fees',
min: 0,
max: 10,
defaultValue: 2.9,
suffix: '%',
tooltip: 'Credit card processing (typically 2.5-3.5%)'
}, '1fr');
});
additionalSection.addRow(row => {
row.addDecimal('shippingOutbound', {
label: 'Outbound Shipping Cost',
min: 0,
max: 500,
defaultValue: 0,
prefix: '$',
tooltip: 'Cost to ship to customer (if absorbed)'
}, '1fr');
row.addDecimal('returnsPercent', {
label: 'Expected Return Rate',
min: 0,
max: 50,
defaultValue: 5,
suffix: '%',
tooltip: 'E-commerce average: 15-20%'
}, '1fr');
});
// Volume Pricing Section
const volumeSection = form.addSubform('volume', { title: '📈 Volume Analysis' });
volumeSection.addRow(row => {
row.addInteger('monthlySales', {
label: 'Expected Monthly Sales (units)',
min: 1,
max: 100000,
defaultValue: 100
}, '1fr');
row.addDecimal('fixedCosts', {
label: 'Monthly Fixed Costs',
min: 0,
max: 100000,
defaultValue: 0,
prefix: '$',
tooltip: 'Rent, salaries, software, etc.'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Results Section
const resultsSection = form.addSubform('results', { title: '📊 Pricing Results', isCollapsible: false });
resultsSection.addRow(row => {
row.addPriceDisplay('totalCost', {
label: 'Total Unit Cost',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
return Math.round((productCost + shippingCost + packagingCost) * 100) / 100;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('sellingPrice', {
label: 'Selling Price',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
return Math.round(sellingPrice * 100) / 100;
},
variant: 'large'
}, '1fr');
});
resultsSection.addRow(row => {
row.addTextPanel('markupDisplay', {
label: 'Markup %',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
const markup = ((sellingPrice - totalCost) / totalCost) * 100;
return `${(Number(markup) || 0).toFixed(1)}%`;
},
customStyles: { 'font-size': '1.2rem', 'font-weight': '600', 'text-align': 'center', 'color': '#0369a1' }
}, '1fr');
row.addTextPanel('marginDisplay', {
label: 'Profit Margin',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
const margin = ((sellingPrice - totalCost) / sellingPrice) * 100;
return `${(Number(margin) || 0).toFixed(1)}%`;
},
customStyles: { 'font-size': '1.2rem', 'font-weight': '600', 'text-align': 'center', 'color': '#059669' }
}, '1fr');
});
resultsSection.addRow(row => {
row.addPriceDisplay('grossProfit', {
label: 'Gross Profit per Unit',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
return Math.round((sellingPrice - totalCost) * 100) / 100;
},
variant: 'success'
}, '1fr');
row.addPriceDisplay('netProfit', {
label: 'Net Profit (after fees)',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
const platformFees = (additionalSection.decimal('platformFees')?.value() || 0) / 100 * sellingPrice;
const paymentFees = (additionalSection.decimal('paymentFees')?.value() || 2.9) / 100 * sellingPrice;
const shippingOutbound = additionalSection.decimal('shippingOutbound')?.value() || 0;
const returnsPercent = (additionalSection.decimal('returnsPercent')?.value() || 5) / 100;
const returnCost = sellingPrice * returnsPercent;
const netProfit = sellingPrice - totalCost - platformFees - paymentFees - shippingOutbound - returnCost;
return Math.round(netProfit * 100) / 100;
},
variant: 'success'
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '💵 Monthly Projections',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('monthlyRevenue', {
label: 'Monthly Revenue',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
const monthlySales = volumeSection.integer('monthlySales')?.value() || 100;
return Math.round(sellingPrice * monthlySales);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('monthlyProfit', {
label: 'Monthly Net Profit',
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
const platformFees = (additionalSection.decimal('platformFees')?.value() || 0) / 100 * sellingPrice;
const paymentFees = (additionalSection.decimal('paymentFees')?.value() || 2.9) / 100 * sellingPrice;
const shippingOutbound = additionalSection.decimal('shippingOutbound')?.value() || 0;
const returnsPercent = (additionalSection.decimal('returnsPercent')?.value() || 5) / 100;
const returnCost = sellingPrice * returnsPercent;
const netProfitPerUnit = sellingPrice - totalCost - platformFees - paymentFees - shippingOutbound - returnCost;
const monthlySales = volumeSection.integer('monthlySales')?.value() || 100;
const fixedCosts = volumeSection.decimal('fixedCosts')?.value() || 0;
return Math.round(netProfitPerUnit * monthlySales - fixedCosts);
},
variant: 'large'
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('breakeven', {
computedValue: () => {
const productCost = costSection.decimal('productCost')?.value() || 25;
const costType = costSection.dropdown('costType')?.value() || 'wholesale';
const shippingCost = costType !== 'landed' ? (costSection.decimal('shippingCost')?.value() || 0) : 0;
const packagingCost = costSection.decimal('packagingCost')?.value() || 0;
const totalCost = productCost + shippingCost + packagingCost;
const method = strategySection.radioButton('pricingMethod')?.value() || 'markup';
const markupPercent = strategySection.decimal('markupPercent')?.value() || 100;
const marginPercent = strategySection.decimal('marginPercent')?.value() || 50;
const targetPrice = strategySection.decimal('targetPrice')?.value() || 50;
let sellingPrice = 0;
if (method === 'markup') {
sellingPrice = totalCost * (1 + markupPercent / 100);
} else if (method === 'margin') {
sellingPrice = totalCost / (1 - marginPercent / 100);
} else {
sellingPrice = targetPrice;
}
const platformFees = (additionalSection.decimal('platformFees')?.value() || 0) / 100 * sellingPrice;
const paymentFees = (additionalSection.decimal('paymentFees')?.value() || 2.9) / 100 * sellingPrice;
const shippingOutbound = additionalSection.decimal('shippingOutbound')?.value() || 0;
const returnsPercent = (additionalSection.decimal('returnsPercent')?.value() || 5) / 100;
const returnCost = sellingPrice * returnsPercent;
const netProfitPerUnit = sellingPrice - totalCost - platformFees - paymentFees - shippingOutbound - returnCost;
const fixedCosts = volumeSection.decimal('fixedCosts')?.value() || 0;
if (netProfitPerUnit <= 0) return 'Warning: Negative profit per unit!';
if (fixedCosts === 0) return 'Add fixed costs to calculate break-even';
const breakeven = Math.ceil(fixedCosts / netProfitPerUnit);
return `Break-even: ${breakeven} units/month`;
},
customStyles: { 'font-size': '0.95rem', 'text-align': 'center', 'color': '#475569' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Results are estimates. Actual profits depend on sales volume, competition, and market conditions.',
customStyles: { 'font-size': '0.8rem', 'color': '#94a3b8', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Save Pricing'
});
}