export function videoProductionCalculator(form: FormTs) {
// Pricing data
const videoTypePrices: Record<string, number> = {
'social-media': 500,
'promotional': 1500,
'corporate': 2500,
'commercial': 4000,
'documentary': 5000,
'music-video': 3500,
'event-highlight': 1000,
'training': 2000
};
const durationMultipliers: Record<string, number> = {
'30sec': 0.5,
'1min': 0.75,
'2-3min': 1,
'5min': 1.5,
'10min': 2.5,
'30min': 5,
'60min': 8
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Video Production Cost Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Project Overview Section
const projectSection = form.addSubform('project', { title: '🎬 Project Overview' });
projectSection.addRow(row => {
row.addDropdown('videoType', {
label: 'Video Type',
options: [
{ id: 'social-media', name: 'Social Media Content ($500 base)' },
{ id: 'promotional', name: 'Promotional Video ($1,500 base)' },
{ id: 'corporate', name: 'Corporate Video ($2,500 base)' },
{ id: 'commercial', name: 'TV/Web Commercial ($4,000 base)' },
{ id: 'documentary', name: 'Documentary ($5,000 base)' },
{ id: 'music-video', name: 'Music Video ($3,500 base)' },
{ id: 'event-highlight', name: 'Event Highlight ($1,000 base)' },
{ id: 'training', name: 'Training/Educational ($2,000 base)' }
],
defaultValue: 'promotional',
isRequired: true
}, '1fr');
row.addDropdown('duration', {
label: 'Final Video Duration',
options: [
{ id: '30sec', name: '30 seconds' },
{ id: '1min', name: '1 minute' },
{ id: '2-3min', name: '2-3 minutes' },
{ id: '5min', name: '5 minutes' },
{ id: '10min', name: '10 minutes' },
{ id: '30min', name: '30 minutes' },
{ id: '60min', name: '60+ minutes' }
],
defaultValue: '2-3min',
isRequired: true
}, '1fr');
});
projectSection.addRow(row => {
row.addDropdown('quality', {
label: 'Production Quality',
options: [
{ id: 'basic', name: 'Basic (1 camera, minimal crew)' },
{ id: 'standard', name: 'Standard (2 cameras, small crew)' },
{ id: 'professional', name: 'Professional (Multi-camera, full crew)' },
{ id: 'cinematic', name: 'Cinematic (Film-quality production)' }
],
defaultValue: 'standard',
isRequired: true
}, '1fr');
row.addInteger('shootDays', {
label: 'Shoot Days',
min: 1,
max: 30,
defaultValue: 1,
isRequired: true,
tooltip: 'Number of days required for filming'
}, '1fr');
});
// Pre-Production Section
const preSection = form.addSubform('preProduction', { title: '📝 Pre-Production' });
preSection.addRow(row => {
row.addDropdown('scriptwriting', {
label: 'Scriptwriting',
options: [
{ id: 'none', name: 'Not needed (client provides)' },
{ id: 'outline', name: 'Outline/talking points ($200)' },
{ id: 'basic', name: 'Basic script ($500)' },
{ id: 'detailed', name: 'Detailed script with revisions ($1,000)' },
{ id: 'premium', name: 'Premium creative script ($2,000)' }
],
defaultValue: 'basic'
}, '1fr');
row.addDropdown('storyboard', {
label: 'Storyboarding',
options: [
{ id: 'none', name: 'Not needed' },
{ id: 'basic', name: 'Basic shot list ($150)' },
{ id: 'detailed', name: 'Detailed storyboard ($400)' },
{ id: 'animated', name: 'Animated storyboard ($800)' }
],
defaultValue: 'none'
}, '1fr');
});
preSection.addRow(row => {
row.addCheckbox('locationScouting', {
label: 'Location Scouting',
defaultValue: false,
tooltip: '+$300 for location research and permits'
}, '1fr');
row.addCheckbox('castingServices', {
label: 'Casting Services',
defaultValue: false,
tooltip: '+$500 for talent auditions and selection'
}, '1fr');
});
// Production Section
const productionSection = form.addSubform('production', { title: '🎥 Production' });
productionSection.addRow(row => {
row.addDropdown('crewSize', {
label: 'Crew Size',
options: [
{ id: 'solo', name: 'Solo Videographer ($400/day)' },
{ id: 'small', name: 'Small Crew (2-3 people, $800/day)' },
{ id: 'medium', name: 'Medium Crew (4-6 people, $1,500/day)' },
{ id: 'large', name: 'Large Crew (7+ people, $3,000/day)' }
],
defaultValue: 'small',
isRequired: true
}, '1fr');
row.addDropdown('equipment', {
label: 'Equipment Package',
options: [
{ id: 'basic', name: 'Basic (DSLR, basic lighting)' },
{ id: 'standard', name: 'Standard (Cinema camera, pro lighting)' },
{ id: 'professional', name: 'Professional (RED/ARRI, grip truck)' },
{ id: 'premium', name: 'Premium (Full cinema package)' }
],
defaultValue: 'standard'
}, '1fr');
});
productionSection.addRow(row => {
row.addCheckbox('drone', {
label: 'Drone/Aerial Footage',
defaultValue: false,
tooltip: '+$500/day including licensed pilot'
}, '1fr');
row.addCheckbox('teleprompter', {
label: 'Teleprompter',
defaultValue: false,
tooltip: '+$200/day with operator'
}, '1fr');
});
productionSection.addRow(row => {
row.addCheckbox('liveStreaming', {
label: 'Live Streaming Setup',
defaultValue: false,
tooltip: '+$800 for multi-camera live streaming'
}, '1fr');
row.addCheckbox('greenScreen', {
label: 'Green Screen Studio',
defaultValue: false,
tooltip: '+$400/day for studio with green screen'
}, '1fr');
});
// Talent Section
const talentSection = form.addSubform('talent', { title: '👤 Talent & Actors' });
talentSection.addRow(row => {
row.addDropdown('onCameraTalent', {
label: 'On-Camera Talent',
options: [
{ id: 'none', name: 'None (client appears)' },
{ id: 'amateur', name: 'Non-professional ($100/person)' },
{ id: 'professional', name: 'Professional actors ($500/person)' },
{ id: 'celebrity', name: 'Celebrity/Influencer ($2,000+/person)' }
],
defaultValue: 'none'
}, '1fr');
row.addInteger('talentCount', {
label: 'Number of Talent',
min: 0,
max: 20,
defaultValue: 0,
isVisible: () => {
const talent = talentSection.dropdown('onCameraTalent')?.value();
return talent !== 'none';
}
}, '1fr');
});
talentSection.addRow(row => {
row.addCheckbox('voiceOver', {
label: 'Professional Voice Over',
defaultValue: false,
tooltip: '+$300 for professional VO artist'
}, '1fr');
row.addCheckbox('hairMakeup', {
label: 'Hair & Makeup Artist',
defaultValue: false,
tooltip: '+$400/day for professional styling'
}, '1fr');
});
// Post-Production Section
const postSection = form.addSubform('postProduction', { title: '✂️ Post-Production' });
postSection.addRow(row => {
row.addDropdown('editingLevel', {
label: 'Editing Level',
options: [
{ id: 'basic', name: 'Basic editing (cuts, basic transitions)' },
{ id: 'standard', name: 'Standard (color correction, graphics)' },
{ id: 'advanced', name: 'Advanced (motion graphics, effects)' },
{ id: 'premium', name: 'Premium (VFX, animation)' }
],
defaultValue: 'standard',
isRequired: true
}, '1fr');
row.addInteger('revisionRounds', {
label: 'Revision Rounds',
min: 1,
max: 10,
defaultValue: 2,
tooltip: 'First 2 rounds included, +$150 per additional round'
}, '1fr');
});
postSection.addRow(row => {
row.addDropdown('music', {
label: 'Music & Sound',
options: [
{ id: 'stock', name: 'Stock music (royalty-free)' },
{ id: 'licensed', name: 'Licensed music (+$200-500)' },
{ id: 'custom', name: 'Custom composition (+$1,500)' }
],
defaultValue: 'stock'
}, '1fr');
row.addCheckbox('soundDesign', {
label: 'Professional Sound Design',
defaultValue: false,
tooltip: '+$400 for custom sound effects and mixing'
}, '1fr');
});
postSection.addRow(row => {
row.addCheckbox('subtitles', {
label: 'Subtitles/Captions',
defaultValue: true,
tooltip: '+$100 for professional captioning'
}, '1fr');
row.addCheckbox('translations', {
label: 'Multi-language Versions',
defaultValue: false,
tooltip: '+$300 per additional language'
}, '1fr');
});
// Deliverables Section
const deliverablesSection = form.addSubform('deliverables', { title: '📦 Deliverables' });
deliverablesSection.addRow(row => {
row.addCheckbox('socialMediaCuts', {
label: 'Social Media Cuts (15s, 30s, 60s)',
defaultValue: false,
tooltip: '+$300 for multiple format versions'
}, '1fr');
row.addCheckbox('rawFootage', {
label: 'Raw Footage Delivery',
defaultValue: false,
tooltip: '+$200 for all raw footage files'
}, '1fr');
});
deliverablesSection.addRow(row => {
row.addCheckbox('thumbnails', {
label: 'Custom Thumbnails (5 options)',
defaultValue: true,
tooltip: '+$100 for designed thumbnails'
}, '1fr');
row.addCheckbox('behindScenes', {
label: 'Behind-the-Scenes Content',
defaultValue: false,
tooltip: '+$400 for BTS video/photos'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Cost Breakdown Section
const breakdownSection = form.addSubform('breakdown', { title: '📊 Cost Breakdown', isCollapsible: false });
breakdownSection.addRow(row => {
row.addPriceDisplay('baseCost', {
label: 'Base Production Cost',
computedValue: () => {
const videoType = projectSection.dropdown('videoType')?.value() || 'promotional';
const duration = projectSection.dropdown('duration')?.value() || '2-3min';
const quality = projectSection.dropdown('quality')?.value() || 'standard';
const base = videoTypePrices[videoType] || 1500;
const durationMult = durationMultipliers[duration] || 1;
const qualityMults: Record<string, number> = {
'basic': 0.7,
'standard': 1,
'professional': 1.5,
'cinematic': 2.2
};
return Math.round(base * durationMult * (qualityMults[quality] || 1));
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('preProductionCost', {
label: 'Pre-Production',
computedValue: () => {
let cost = 0;
const script = preSection.dropdown('scriptwriting')?.value() || 'basic';
const storyboard = preSection.dropdown('storyboard')?.value() || 'none';
const scriptPrices: Record<string, number> = {
'none': 0, 'outline': 200, 'basic': 500, 'detailed': 1000, 'premium': 2000
};
const storyboardPrices: Record<string, number> = {
'none': 0, 'basic': 150, 'detailed': 400, 'animated': 800
};
cost += scriptPrices[script] || 0;
cost += storyboardPrices[storyboard] || 0;
if (preSection.checkbox('locationScouting')?.value()) cost += 300;
if (preSection.checkbox('castingServices')?.value()) cost += 500;
return cost;
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('productionCost', {
label: 'Production (Crew & Equipment)',
computedValue: () => {
const shootDays = projectSection.integer('shootDays')?.value() || 1;
const crewSize = productionSection.dropdown('crewSize')?.value() || 'small';
const equipment = productionSection.dropdown('equipment')?.value() || 'standard';
const crewPrices: Record<string, number> = {
'solo': 400, 'small': 800, 'medium': 1500, 'large': 3000
};
const equipmentPrices: Record<string, number> = {
'basic': 200, 'standard': 500, 'professional': 1200, 'premium': 2500
};
let cost = (crewPrices[crewSize] + equipmentPrices[equipment]) * shootDays;
if (productionSection.checkbox('drone')?.value()) cost += 500 * shootDays;
if (productionSection.checkbox('teleprompter')?.value()) cost += 200 * shootDays;
if (productionSection.checkbox('liveStreaming')?.value()) cost += 800;
if (productionSection.checkbox('greenScreen')?.value()) cost += 400 * shootDays;
return cost;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('talentCost', {
label: 'Talent & Styling',
computedValue: () => {
const shootDays = projectSection.integer('shootDays')?.value() || 1;
const talentType = talentSection.dropdown('onCameraTalent')?.value() || 'none';
const talentCount = talentSection.integer('talentCount')?.value() || 0;
const talentPrices: Record<string, number> = {
'none': 0, 'amateur': 100, 'professional': 500, 'celebrity': 2000
};
let cost = (talentPrices[talentType] || 0) * talentCount * shootDays;
if (talentSection.checkbox('voiceOver')?.value()) cost += 300;
if (talentSection.checkbox('hairMakeup')?.value()) cost += 400 * shootDays;
return cost;
},
variant: 'default'
}, '1fr');
});
breakdownSection.addRow(row => {
row.addPriceDisplay('postProductionCost', {
label: 'Post-Production',
computedValue: () => {
const duration = projectSection.dropdown('duration')?.value() || '2-3min';
const editingLevel = postSection.dropdown('editingLevel')?.value() || 'standard';
const revisions = postSection.integer('revisionRounds')?.value() || 2;
const music = postSection.dropdown('music')?.value() || 'stock';
const editingPrices: Record<string, number> = {
'basic': 300, 'standard': 600, 'advanced': 1200, 'premium': 2500
};
const musicPrices: Record<string, number> = {
'stock': 50, 'licensed': 350, 'custom': 1500
};
const durationMult = durationMultipliers[duration] || 1;
let cost = Math.round(editingPrices[editingLevel] * durationMult);
cost += musicPrices[music] || 50;
if (revisions > 2) cost += (revisions - 2) * 150;
if (postSection.checkbox('soundDesign')?.value()) cost += 400;
if (postSection.checkbox('subtitles')?.value()) cost += 100;
if (postSection.checkbox('translations')?.value()) cost += 300;
return cost;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('deliverablesCost', {
label: 'Additional Deliverables',
computedValue: () => {
let cost = 0;
if (deliverablesSection.checkbox('socialMediaCuts')?.value()) cost += 300;
if (deliverablesSection.checkbox('rawFootage')?.value()) cost += 200;
if (deliverablesSection.checkbox('thumbnails')?.value()) cost += 100;
if (deliverablesSection.checkbox('behindScenes')?.value()) cost += 400;
return cost;
},
variant: 'default'
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '🎬 Total Production Cost',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addPriceDisplay('totalCost', {
label: 'Estimated Total',
computedValue: () => {
const videoType = projectSection.dropdown('videoType')?.value() || 'promotional';
const duration = projectSection.dropdown('duration')?.value() || '2-3min';
const quality = projectSection.dropdown('quality')?.value() || 'standard';
const shootDays = projectSection.integer('shootDays')?.value() || 1;
// Base cost
const base = videoTypePrices[videoType] || 1500;
const durationMult = durationMultipliers[duration] || 1;
const qualityMults: Record<string, number> = {
'basic': 0.7, 'standard': 1, 'professional': 1.5, 'cinematic': 2.2
};
let total = Math.round(base * durationMult * (qualityMults[quality] || 1));
// Pre-production
const script = preSection.dropdown('scriptwriting')?.value() || 'basic';
const storyboard = preSection.dropdown('storyboard')?.value() || 'none';
const scriptPrices: Record<string, number> = {
'none': 0, 'outline': 200, 'basic': 500, 'detailed': 1000, 'premium': 2000
};
const storyboardPrices: Record<string, number> = {
'none': 0, 'basic': 150, 'detailed': 400, 'animated': 800
};
total += scriptPrices[script] || 0;
total += storyboardPrices[storyboard] || 0;
if (preSection.checkbox('locationScouting')?.value()) total += 300;
if (preSection.checkbox('castingServices')?.value()) total += 500;
// Production
const crewSize = productionSection.dropdown('crewSize')?.value() || 'small';
const equipment = productionSection.dropdown('equipment')?.value() || 'standard';
const crewPrices: Record<string, number> = {
'solo': 400, 'small': 800, 'medium': 1500, 'large': 3000
};
const equipmentPrices: Record<string, number> = {
'basic': 200, 'standard': 500, 'professional': 1200, 'premium': 2500
};
total += (crewPrices[crewSize] + equipmentPrices[equipment]) * shootDays;
if (productionSection.checkbox('drone')?.value()) total += 500 * shootDays;
if (productionSection.checkbox('teleprompter')?.value()) total += 200 * shootDays;
if (productionSection.checkbox('liveStreaming')?.value()) total += 800;
if (productionSection.checkbox('greenScreen')?.value()) total += 400 * shootDays;
// Talent
const talentType = talentSection.dropdown('onCameraTalent')?.value() || 'none';
const talentCount = talentSection.integer('talentCount')?.value() || 0;
const talentPrices: Record<string, number> = {
'none': 0, 'amateur': 100, 'professional': 500, 'celebrity': 2000
};
total += (talentPrices[talentType] || 0) * talentCount * shootDays;
if (talentSection.checkbox('voiceOver')?.value()) total += 300;
if (talentSection.checkbox('hairMakeup')?.value()) total += 400 * shootDays;
// Post-production
const editingLevel = postSection.dropdown('editingLevel')?.value() || 'standard';
const revisions = postSection.integer('revisionRounds')?.value() || 2;
const music = postSection.dropdown('music')?.value() || 'stock';
const editingPrices: Record<string, number> = {
'basic': 300, 'standard': 600, 'advanced': 1200, 'premium': 2500
};
const musicPrices: Record<string, number> = {
'stock': 50, 'licensed': 350, 'custom': 1500
};
total += Math.round(editingPrices[editingLevel] * durationMult);
total += musicPrices[music] || 50;
if (revisions > 2) total += (revisions - 2) * 150;
if (postSection.checkbox('soundDesign')?.value()) total += 400;
if (postSection.checkbox('subtitles')?.value()) total += 100;
if (postSection.checkbox('translations')?.value()) total += 300;
// Deliverables
if (deliverablesSection.checkbox('socialMediaCuts')?.value()) total += 300;
if (deliverablesSection.checkbox('rawFootage')?.value()) total += 200;
if (deliverablesSection.checkbox('thumbnails')?.value()) total += 100;
if (deliverablesSection.checkbox('behindScenes')?.value()) total += 400;
return total;
},
variant: 'large'
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Prices are estimates and may vary based on project complexity, location, and specific requirements. Contact us for a detailed quote.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Request Video Quote'
});
}