export function tattooStudioCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Tattoo Price Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Tattoo Type Section
const typeSection = form.addSubform('tattooType', { title: '๐จ Tattoo Type' });
typeSection.addRow(row => {
row.addDropdown('serviceType', {
label: 'Service Type',
options: [
{ id: 'custom', name: 'Custom Design' },
{ id: 'flash', name: 'Flash/Pre-designed' },
{ id: 'coverup', name: 'Cover-Up' },
{ id: 'touchup', name: 'Touch-Up/Refresh' },
{ id: 'rework', name: 'Rework Existing Tattoo' }
],
defaultValue: 'custom',
isRequired: true
}, '1fr');
});
// Size Section
const sizeSection = form.addSubform('size', { title: '๐ Size & Dimensions' });
sizeSection.addRow(row => {
row.addDropdown('tattooSize', {
label: 'Tattoo Size',
options: [
{ id: 'tiny', name: 'Tiny (under 1 inch)' },
{ id: 'small', name: 'Small (1-2 inches)' },
{ id: 'medium', name: 'Medium (3-4 inches)' },
{ id: 'large', name: 'Large (5-7 inches)' },
{ id: 'extra-large', name: 'Extra Large (8-12 inches)' },
{ id: 'half-sleeve', name: 'Half Sleeve' },
{ id: 'full-sleeve', name: 'Full Sleeve' },
{ id: 'back-piece', name: 'Back Piece' },
{ id: 'chest-piece', name: 'Chest Piece' }
],
defaultValue: 'medium',
isRequired: true
}, '1fr');
});
// Placement Section
const placementSection = form.addSubform('placement', { title: '๐ Body Placement' });
placementSection.addRow(row => {
row.addDropdown('bodyArea', {
label: 'Body Area',
options: [
{ id: 'forearm', name: 'Forearm (outer)' },
{ id: 'forearm-inner', name: 'Forearm (inner)' },
{ id: 'upper-arm', name: 'Upper Arm/Bicep' },
{ id: 'shoulder', name: 'Shoulder/Deltoid' },
{ id: 'back-upper', name: 'Upper Back' },
{ id: 'back-lower', name: 'Lower Back' },
{ id: 'chest', name: 'Chest' },
{ id: 'ribs', name: 'Ribs/Side' },
{ id: 'stomach', name: 'Stomach/Abdomen' },
{ id: 'thigh', name: 'Thigh' },
{ id: 'calf', name: 'Calf' },
{ id: 'ankle', name: 'Ankle/Foot' },
{ id: 'wrist', name: 'Wrist' },
{ id: 'hand', name: 'Hand/Fingers' },
{ id: 'neck', name: 'Neck' },
{ id: 'head', name: 'Head/Scalp' },
{ id: 'face', name: 'Face' }
],
defaultValue: 'forearm',
isRequired: true
}, '1fr');
});
// Style Section
const styleSection = form.addSubform('style', { title: 'โจ Tattoo Style' });
styleSection.addRow(row => {
row.addDropdown('tattooStyle', {
label: 'Style',
options: [
{ id: 'traditional', name: 'Traditional/Old School' },
{ id: 'neo-traditional', name: 'Neo-Traditional' },
{ id: 'realism', name: 'Realism/Photorealistic' },
{ id: 'black-grey', name: 'Black & Grey' },
{ id: 'watercolor', name: 'Watercolor' },
{ id: 'geometric', name: 'Geometric' },
{ id: 'dotwork', name: 'Dotwork/Stippling' },
{ id: 'tribal', name: 'Tribal' },
{ id: 'japanese', name: 'Japanese/Irezumi' },
{ id: 'fine-line', name: 'Fine Line/Single Needle' },
{ id: 'minimalist', name: 'Minimalist' },
{ id: 'lettering', name: 'Script/Lettering' },
{ id: 'blackwork', name: 'Blackwork/Ornamental' },
{ id: 'trash-polka', name: 'Trash Polka' },
{ id: 'illustrative', name: 'Illustrative' }
],
defaultValue: 'traditional',
isRequired: true
}, '1fr');
});
styleSection.addRow(row => {
row.addDropdown('colorType', {
label: 'Color',
options: [
{ id: 'black-grey', name: 'Black & Grey Only' },
{ id: 'limited-color', name: 'Limited Color (1-3 colors)' },
{ id: 'full-color', name: 'Full Color' },
{ id: 'vibrant', name: 'Vibrant/Saturated Colors' }
],
defaultValue: 'black-grey'
}, '1fr');
row.addDropdown('complexity', {
label: 'Design Complexity',
options: [
{ id: 'simple', name: 'Simple (basic shapes, minimal detail)' },
{ id: 'moderate', name: 'Moderate (standard detail)' },
{ id: 'detailed', name: 'Detailed (intricate work)' },
{ id: 'highly-detailed', name: 'Highly Detailed (extreme precision)' }
],
defaultValue: 'moderate'
}, '1fr');
});
// Special Requirements Section
const specialSection = form.addSubform('special', { title: 'โก Special Requirements' });
specialSection.addRow(row => {
row.addCheckbox('customDesign', {
label: 'Custom Design Consultation',
defaultValue: false
}, '1fr');
row.addCheckbox('multipleSession', {
label: 'Multiple Sessions Expected',
defaultValue: false
}, '1fr');
});
specialSection.addRow(row => {
row.addCheckbox('whiteInk', {
label: 'White Ink Highlights',
defaultValue: false
}, '1fr');
row.addCheckbox('uvInk', {
label: 'UV/Blacklight Reactive Ink',
defaultValue: false
}, '1fr');
});
specialSection.addRow(row => {
row.addCheckbox('scarCover', {
label: 'Scar/Stretch Mark Coverage',
defaultValue: false
}, '1fr');
row.addCheckbox('rushOrder', {
label: 'Rush Appointment (within 1 week)',
defaultValue: false
}, '1fr');
});
// Artist Level Section
const artistSection = form.addSubform('artist', { title: '๐จโ๐จ Artist Level' });
artistSection.addRow(row => {
row.addRadioButton('artistLevel', {
label: 'Select Artist',
options: [
{ id: 'apprentice', name: 'Apprentice Artist' },
{ id: 'resident', name: 'Resident Artist' },
{ id: 'senior', name: 'Senior Artist' },
{ id: 'featured', name: 'Featured/Guest Artist' }
],
defaultValue: 'resident',
orientation: 'horizontal'
});
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Pricing Section
const pricingSection = form.addSubform('pricing', { title: '๐ฐ Estimate', isCollapsible: false });
const calculatePrice = () => {
const serviceType = typeSection.dropdown('serviceType')?.value() || 'custom';
const tattooSize = sizeSection.dropdown('tattooSize')?.value() || 'medium';
const bodyArea = placementSection.dropdown('bodyArea')?.value() || 'forearm';
const tattooStyle = styleSection.dropdown('tattooStyle')?.value() || 'traditional';
const colorType = styleSection.dropdown('colorType')?.value() || 'black-grey';
const complexity = styleSection.dropdown('complexity')?.value() || 'moderate';
const artistLevel = artistSection.radioButton('artistLevel')?.value() || 'resident';
// Base prices by size
const sizePrices: Record<string, number> = {
'tiny': 80,
'small': 150,
'medium': 300,
'large': 500,
'extra-large': 800,
'half-sleeve': 1500,
'full-sleeve': 3000,
'back-piece': 4000,
'chest-piece': 2500
};
let basePrice = sizePrices[tattooSize] || 300;
// Placement difficulty multiplier
const placementMult: Record<string, number> = {
'forearm': 1.0,
'forearm-inner': 1.1,
'upper-arm': 1.0,
'shoulder': 1.0,
'back-upper': 1.0,
'back-lower': 1.05,
'chest': 1.1,
'ribs': 1.25,
'stomach': 1.15,
'thigh': 1.0,
'calf': 1.05,
'ankle': 1.15,
'wrist': 1.1,
'hand': 1.3,
'neck': 1.25,
'head': 1.35,
'face': 1.5
};
basePrice *= placementMult[bodyArea] || 1.0;
// Style multiplier
const styleMult: Record<string, number> = {
'traditional': 1.0,
'neo-traditional': 1.1,
'realism': 1.4,
'black-grey': 1.0,
'watercolor': 1.2,
'geometric': 1.15,
'dotwork': 1.25,
'tribal': 0.95,
'japanese': 1.2,
'fine-line': 1.15,
'minimalist': 0.9,
'lettering': 0.85,
'blackwork': 1.1,
'trash-polka': 1.25,
'illustrative': 1.15
};
basePrice *= styleMult[tattooStyle] || 1.0;
// Color multiplier
const colorMult: Record<string, number> = {
'black-grey': 1.0,
'limited-color': 1.15,
'full-color': 1.3,
'vibrant': 1.4
};
basePrice *= colorMult[colorType] || 1.0;
// Complexity multiplier
const complexMult: Record<string, number> = {
'simple': 0.85,
'moderate': 1.0,
'detailed': 1.25,
'highly-detailed': 1.5
};
basePrice *= complexMult[complexity] || 1.0;
// Service type multiplier
const serviceMult: Record<string, number> = {
'custom': 1.0,
'flash': 0.75,
'coverup': 1.35,
'touchup': 0.4,
'rework': 1.2
};
basePrice *= serviceMult[serviceType] || 1.0;
// Artist level multiplier
const artistMult: Record<string, number> = {
'apprentice': 0.6,
'resident': 1.0,
'senior': 1.35,
'featured': 1.75
};
basePrice *= artistMult[artistLevel] || 1.0;
// Special add-ons
let addonsPrice = 0;
if (specialSection.checkbox('customDesign')?.value()) addonsPrice += 75;
if (specialSection.checkbox('whiteInk')?.value()) addonsPrice += 50;
if (specialSection.checkbox('uvInk')?.value()) addonsPrice += 100;
if (specialSection.checkbox('scarCover')?.value()) addonsPrice += 100;
if (specialSection.checkbox('rushOrder')?.value()) addonsPrice += basePrice * 0.25;
const total = basePrice + addonsPrice;
// Estimate sessions for large pieces
let sessions = 1;
if (['half-sleeve', 'full-sleeve', 'back-piece', 'chest-piece'].includes(tattooSize)) {
const sessionHours: Record<string, number> = {
'half-sleeve': 8,
'full-sleeve': 20,
'back-piece': 30,
'chest-piece': 15
};
sessions = Math.ceil((sessionHours[tattooSize] || 8) / 4);
}
if (specialSection.checkbox('multipleSession')?.value()) {
sessions = Math.max(sessions, 2);
}
// Estimate time
let hours = 1;
const sizeHours: Record<string, number> = {
'tiny': 0.5,
'small': 1,
'medium': 2,
'large': 4,
'extra-large': 6,
'half-sleeve': 8,
'full-sleeve': 20,
'back-piece': 30,
'chest-piece': 15
};
hours = sizeHours[tattooSize] || 2;
hours *= complexMult[complexity] || 1.0;
return {
basePrice: Math.round(basePrice),
addonsPrice: Math.round(addonsPrice),
total: Math.round(total),
minPrice: Math.round(total * 0.85),
maxPrice: Math.round(total * 1.15),
sessions,
hours: Math.round(hours * 10) / 10
};
};
pricingSection.addRow(row => {
row.addPriceDisplay('estimate', {
label: 'Estimated Price',
computedValue: () => calculatePrice().total,
variant: 'large'
}, '1fr');
});
pricingSection.addRow(row => {
row.addTextPanel('range', {
computedValue: () => {
const price = calculatePrice();
return `Price Range: $${price.minPrice} - $${price.maxPrice}`;
},
customStyles: { 'font-size': '1rem', 'color': '#475569', 'text-align': 'center' }
});
});
pricingSection.addRow(row => {
row.addTextPanel('time', {
computedValue: () => {
const price = calculatePrice();
const sessionText = price.sessions > 1 ? `${price.sessions} sessions` : '1 session';
return `Estimated Time: ~${price.hours} hours (${sessionText})`;
},
customStyles: { 'font-size': '0.9rem', 'color': '#64748b', 'text-align': 'center' }
});
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('total', {
label: 'Starting From',
computedValue: () => calculatePrice().minPrice,
variant: 'large'
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('note', {
computedValue: () => 'Final price determined after consultation. A deposit (typically 20-50%) is required to book. Prices are estimates and may vary based on final design.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Request Consultation'
});
}