export function proposalFeedback(form: FormTs) {
// Proposal/Pitch Feedback Form - Win-Loss Analysis for Sales
// Demonstrates: RadioButton, MatrixQuestion, StarRating, Slider, conditional visibility, dynamic labels
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Proposal Feedback',
computedValue: () => 'Your honest feedback helps us improve our proposals and better serve future clients.',
customStyles: {
background: 'linear-gradient(135deg, #1e3a5f 0%, #2563eb 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Deal Outcome
// ============================================
const outcomeSection = form.addSubform('outcome', {
title: 'Deal Outcome'
});
outcomeSection.addRow(row => {
row.addRadioButton('dealOutcome', {
label: 'What was the outcome of this proposal?',
options: [
{ id: 'won', name: 'We won the deal (chose our solution)' },
{ id: 'lost-competitor', name: 'Lost to a competitor' },
{ id: 'lost-no-decision', name: 'No decision made / project postponed' },
{ id: 'lost-internal', name: 'Went with internal solution' },
{ id: 'lost-budget', name: 'Project cancelled due to budget' }
],
isRequired: true
});
});
outcomeSection.addRow(row => {
row.addTextbox('competitorName', {
label: 'Which competitor did you choose?',
placeholder: 'Competitor name',
isVisible: () => outcomeSection.radioButton('dealOutcome')?.value() === 'lost-competitor',
isRequired: () => outcomeSection.radioButton('dealOutcome')?.value() === 'lost-competitor'
});
});
// ============================================
// SECTION 2: Decision Factors
// ============================================
const decisionSection = form.addSubform('decision', {
title: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') return 'Why You Chose Us';
return 'Decision Factors';
},
isVisible: () => !!outcomeSection.radioButton('dealOutcome')?.value(),
customStyles: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
}
return { backgroundColor: '#f1f5f9', padding: '16px', borderRadius: '8px' };
}
});
decisionSection.addRow(row => {
row.addMatrixQuestion('decisionFactors', {
label: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return 'How important were these factors in choosing us?';
}
return 'How did we compare on these factors?';
},
rows: [
{ id: 'price', label: 'Price/Value', isRequired: true },
{ id: 'features', label: 'Product Features', isRequired: true },
{ id: 'reputation', label: 'Company Reputation', isRequired: true },
{ id: 'support', label: 'Support & Service', isRequired: true },
{ id: 'innovation', label: 'Innovation/Technology', isRequired: true },
{ id: 'relationship', label: 'Sales Relationship', isRequired: true },
{ id: 'implementation', label: 'Implementation Plan', isRequired: true }
],
columns: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return [
{ id: 'not-important', label: 'Not Important' },
{ id: 'somewhat', label: 'Somewhat' },
{ id: 'important', label: 'Important' },
{ id: 'very', label: 'Very Important' },
{ id: 'critical', label: 'Critical' }
];
}
return [
{ id: 'much-worse', label: 'Much Worse' },
{ id: 'worse', label: 'Worse' },
{ id: 'similar', label: 'Similar' },
{ id: 'better', label: 'Better' },
{ id: 'much-better', label: 'Much Better' }
];
},
fullWidth: true
});
});
decisionSection.addSpacer();
decisionSection.addRow(row => {
row.addRadioButton('primaryFactor', {
label: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return 'What was the single most important factor in choosing us?';
}
return 'What was the primary reason for not selecting us?';
},
options: [
{ id: 'price', name: 'Price/Budget' },
{ id: 'features', name: 'Product capabilities' },
{ id: 'experience', name: 'Industry experience' },
{ id: 'support', name: 'Support/Service quality' },
{ id: 'relationship', name: 'Sales relationship/trust' },
{ id: 'timeline', name: 'Implementation timeline' },
{ id: 'references', name: 'References/reputation' },
{ id: 'other', name: 'Other factor' }
],
isRequired: true
});
});
decisionSection.addRow(row => {
row.addTextarea('primaryFactorDetails', {
label: 'Please elaborate on this factor:',
placeholder: 'Provide more details about this decision factor...',
rows: 3,
isVisible: () => !!decisionSection.radioButton('primaryFactor')?.value()
});
});
// ============================================
// SECTION 3: Pricing Perception
// ============================================
const pricingSection = form.addSubform('pricing', {
title: 'Pricing Perception',
isVisible: () => !!decisionSection.radioButton('primaryFactor')?.value()
});
pricingSection.addRow(row => {
row.addRatingScale('pricePerception', {
label: 'How would you rate our pricing relative to the value offered?',
preset: 'custom',
min: 1,
max: 5,
lowLabel: 'Very Overpriced',
highLabel: 'Great Value',
variant: 'segmented',
alignment: 'center'
});
});
pricingSection.addRow(row => {
row.addSlider('priceVsCompetitor', {
label: 'How did our pricing compare to competitors?',
min: -50,
max: 50,
step: 5,
unit: '%',
defaultValue: 0,
isVisible: () => outcomeSection.radioButton('dealOutcome')?.value() === 'lost-competitor'
});
});
pricingSection.addRow(row => {
row.addTextPanel('priceCompareLabel', {
computedValue: () => {
const diff = pricingSection.slider('priceVsCompetitor')?.value();
if (diff === null || diff === undefined) return '';
if (diff < -20) return 'Much cheaper than competitor';
if (diff < 0) return 'Somewhat cheaper than competitor';
if (diff === 0) return 'Similar pricing to competitor';
if (diff > 20) return 'Much more expensive than competitor';
return 'Somewhat more expensive than competitor';
},
customStyles: {
textAlign: 'center',
padding: '8px',
backgroundColor: '#f8fafc',
borderRadius: '6px',
fontStyle: 'italic'
},
isVisible: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
return outcome === 'lost-competitor' && pricingSection.slider('priceVsCompetitor')?.value() !== null;
}
});
});
// ============================================
// SECTION 4: Proposal Quality
// ============================================
const proposalSection = form.addSubform('proposal', {
title: 'Proposal Quality',
isVisible: () => !!pricingSection.ratingScale('pricePerception')?.value()
});
proposalSection.addRow(row => {
row.addMatrixQuestion('proposalRatings', {
label: 'Rate the quality of our proposal:',
rows: [
{ id: 'clarity', label: 'Clarity & Organization' },
{ id: 'understanding', label: 'Understanding of Your Needs' },
{ id: 'solution', label: 'Solution Fit' },
{ id: 'timeline', label: 'Realistic Timeline' },
{ id: 'professionalism', label: 'Overall Professionalism' }
],
columns: [
{ id: '1', label: 'Poor' },
{ id: '2', label: 'Fair' },
{ id: '3', label: 'Good' },
{ id: '4', label: 'Very Good' },
{ id: '5', label: 'Excellent' }
],
fullWidth: true
});
});
proposalSection.addRow(row => {
row.addCheckboxList('proposalStrengths', {
label: 'What did you like most about our proposal?',
options: [
{ id: 'thoroughness', name: 'Thoroughness' },
{ id: 'creativity', name: 'Creative approach' },
{ id: 'customization', name: 'Customized to our needs' },
{ id: 'examples', name: 'Relevant examples/case studies' },
{ id: 'presentation', name: 'Presentation quality' },
{ id: 'team', name: 'Proposed team' }
]
}, '1fr');
row.addCheckboxList('proposalWeaknesses', {
label: 'What could be improved?',
options: [
{ id: 'detail', name: 'More detail needed' },
{ id: 'pricing', name: 'Pricing breakdown' },
{ id: 'timeline', name: 'Clearer timeline' },
{ id: 'references', name: 'More references' },
{ id: 'technical', name: 'Technical depth' },
{ id: 'visuals', name: 'Visual presentation' }
]
}, '1fr');
});
// ============================================
// SECTION 5: Sales Process
// ============================================
const salesSection = form.addSubform('sales', {
title: 'Sales Experience',
isVisible: () => !!proposalSection.matrixQuestion('proposalRatings')?.value()
});
salesSection.addRow(row => {
row.addStarRating('salesRepRating', {
label: 'How would you rate your sales representative?',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
salesSection.addRow(row => {
row.addMatrixQuestion('salesProcess', {
label: 'Rate the sales process:',
rows: [
{ id: 'responsiveness', label: 'Responsiveness' },
{ id: 'knowledge', label: 'Product Knowledge' },
{ id: 'listening', label: 'Listening to Needs' },
{ id: 'followup', label: 'Follow-up Quality' },
{ id: 'pressure', label: 'Non-Pressuring Approach' }
],
columns: [
{ id: '1', label: 'Poor' },
{ id: '2', label: 'Fair' },
{ id: '3', label: 'Good' },
{ id: '4', label: 'Very Good' },
{ id: '5', label: 'Excellent' }
],
fullWidth: true
});
});
// ============================================
// SECTION 6: Future Relationship
// ============================================
const futureSection = form.addSubform('future', {
title: 'Future Relationship',
isVisible: () => !!salesSection.starRating('salesRepRating')?.value()
});
futureSection.addRow(row => {
row.addRatingScale('futureConsideration', {
label: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return 'How likely are you to work with us again?';
}
return 'How likely are you to consider us for future opportunities?';
},
preset: 'nps',
showSegmentColors: true,
showCategoryLabel: true,
alignment: 'center'
});
});
futureSection.addRow(row => {
row.addCheckboxList('futureOpportunities', {
label: 'Would you be interested in hearing from us about:',
options: [
{ id: 'similar', name: 'Similar projects in the future' },
{ id: 'other-products', name: 'Other products/services' },
{ id: 'updates', name: 'New features/capabilities' },
{ id: 'thought-leadership', name: 'Industry insights' },
{ id: 'none', name: 'Prefer not to be contacted' }
],
isVisible: () => {
const score = futureSection.ratingScale('futureConsideration')?.value();
return score !== null && score !== undefined && score >= 5;
}
});
});
futureSection.addSpacer();
futureSection.addRow(row => {
row.addTextarea('additionalFeedback', {
label: 'Any additional feedback or advice for us?',
placeholder: 'What could we do differently next time? Any other thoughts?',
rows: 4
});
});
// ============================================
// SECTION 7: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => futureSection.ratingScale('futureConsideration')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
const primaryFactor = decisionSection.radioButton('primaryFactor')?.value();
const pricePerception = pricingSection.ratingScale('pricePerception')?.value();
const salesRep = salesSection.starRating('salesRepRating')?.value();
const futureScore = futureSection.ratingScale('futureConsideration')?.value();
if (!outcome) return '';
const outcomeLabels: Record<string, string> = {
'won': 'π Won',
'lost-competitor': 'β Lost to Competitor',
'lost-no-decision': 'βΈοΈ No Decision',
'lost-internal': 'π Internal Solution',
'lost-budget': 'π° Budget Cancelled'
};
const factorLabels: Record<string, string> = {
'price': 'Price/Budget',
'features': 'Product Capabilities',
'experience': 'Industry Experience',
'support': 'Support Quality',
'relationship': 'Sales Relationship',
'timeline': 'Timeline',
'references': 'References',
'other': 'Other'
};
let summary = 'π Win/Loss Analysis Summary\n';
summary += 'β'.repeat(30) + '\n\n';
summary += `Outcome: ${outcomeLabels[outcome] || outcome}\n`;
if (primaryFactor) {
summary += `Primary Factor: ${factorLabels[primaryFactor] || primaryFactor}\n`;
}
if (pricePerception) {
const priceLabel = pricePerception >= 4 ? 'Good Value' :
pricePerception === 3 ? 'Fair' : 'Overpriced';
summary += `Price Perception: ${priceLabel}\n`;
}
if (salesRep) {
summary += `\nSales Rep: ${'β'.repeat(salesRep)}`;
}
if (futureScore !== null && futureScore !== undefined) {
const futureLabel = futureScore >= 9 ? 'Very Likely' :
futureScore >= 7 ? 'Likely' :
futureScore >= 5 ? 'Maybe' : 'Unlikely';
summary += `\nFuture Consideration: ${futureScore}/10 (${futureLabel})`;
}
return summary;
},
customStyles: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (outcome === 'won') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => futureSection.ratingScale('futureConsideration')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return 'Thank You for Choosing Us!';
}
return 'Thank You for Your Candid Feedback!';
},
message: () => {
const outcome = outcomeSection.radioButton('dealOutcome')?.value();
if (outcome === 'won') {
return 'We\'re excited to work with you! Your feedback helps us understand what we\'re doing right and continue to improve.';
}
return 'Your honest feedback is invaluable in helping us improve our proposals and better serve future clients. We hope to have the opportunity to work together in the future.';
}
});
}