export function detractorRecoverySurvey(form: FormTs) {
// Detractor Recovery Survey - Turn unhappy customers into promoters
// Demonstrates: NPS Scale, MatrixQuestion, Conditional Flows, Dynamic Styling, Multi-step Recovery
// ============================================
// STATE
// ============================================
const recoveryPath = form.state<string | null>(null);
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'We Want to Make This Right',
computedValue: () => 'Your feedback matters. Help us understand what went wrong so we can improve.',
customStyles: {
background: 'linear-gradient(135deg, #dc2626 0%, #991b1b 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center',
fontSize: '15px'
}
});
});
// ============================================
// SECTION 1: Current Satisfaction Level
// ============================================
const satisfactionSection = form.addSubform('satisfaction', {
title: 'How Are You Feeling?',
customStyles: { backgroundColor: '#fef2f2', padding: '16px', borderRadius: '8px' }
});
satisfactionSection.addRow(row => {
row.addEmojiRating('currentMood', {
label: 'How would you describe your current feeling about our service?',
preset: 'satisfaction',
size: 'lg',
alignment: 'center',
isRequired: true
});
});
satisfactionSection.addRow(row => {
row.addRatingScale('dissatisfactionLevel', {
label: 'How disappointed are you with your recent experience?',
preset: 'custom',
min: 1,
max: 10,
lowLabel: 'Slightly disappointed',
highLabel: 'Extremely disappointed',
size: 'md',
alignment: 'center',
isVisible: () => satisfactionSection.emojiRating('currentMood')?.value() !== null
});
});
// ============================================
// SECTION 2: Problem Identification
// ============================================
const problemSection = form.addSubform('problems', {
title: 'What Went Wrong?',
isVisible: () => satisfactionSection.ratingScale('dissatisfactionLevel')?.value() !== null,
customStyles: { backgroundColor: '#fff7ed', padding: '16px', borderRadius: '8px' }
});
problemSection.addRow(row => {
row.addCheckboxList('problemCategories', {
label: 'Which areas were problematic? (Select all that apply)',
options: [
{ id: 'product-quality', name: 'Product Quality' },
{ id: 'customer-service', name: 'Customer Service' },
{ id: 'pricing', name: 'Pricing / Value for Money' },
{ id: 'delivery', name: 'Delivery / Shipping' },
{ id: 'communication', name: 'Communication' },
{ id: 'website-app', name: 'Website / App Experience' },
{ id: 'billing', name: 'Billing Issues' },
{ id: 'expectations', name: 'Did Not Meet Expectations' }
],
orientation: 'vertical',
isRequired: true
});
});
// ============================================
// SECTION 3: Detailed Problem Analysis (Matrix)
// ============================================
const detailsSection = form.addSubform('details', {
title: 'Help Us Understand Better',
isVisible: () => {
const problems = problemSection.checkboxList('problemCategories')?.value() || [];
return problems.length > 0;
},
customStyles: { backgroundColor: '#fefce8', padding: '16px', borderRadius: '8px' }
});
detailsSection.addRow(row => {
row.addMatrixQuestion('issueImpact', {
label: 'Rate the severity of each issue you experienced:',
rows: () => {
const problems = problemSection.checkboxList('problemCategories')?.value() || [];
const rowMap: Record<string, { id: string; label: string }> = {
'product-quality': { id: 'product-quality', label: 'Product Quality' },
'customer-service': { id: 'customer-service', label: 'Customer Service' },
'pricing': { id: 'pricing', label: 'Pricing / Value' },
'delivery': { id: 'delivery', label: 'Delivery / Shipping' },
'communication': { id: 'communication', label: 'Communication' },
'website-app': { id: 'website-app', label: 'Website / App' },
'billing': { id: 'billing', label: 'Billing' },
'expectations': { id: 'expectations', label: 'Unmet Expectations' }
};
return problems.map(p => rowMap[p]).filter((row): row is { id: string; label: string } => row !== undefined);
},
columns: [
{ id: 'minor', label: 'Minor' },
{ id: 'moderate', label: 'Moderate' },
{ id: 'serious', label: 'Serious' },
{ id: 'critical', label: 'Critical' }
],
fullWidth: true,
striped: true
});
});
detailsSection.addSpacer({ height: '16px' });
detailsSection.addRow(row => {
row.addTextarea('whatHappened', {
label: 'Please describe what happened in your own words:',
placeholder: 'Tell us the details of your experience...',
rows: 4,
autoExpand: true,
isRequired: true
});
});
// ============================================
// SECTION 4: Impact Assessment
// ============================================
const impactSection = form.addSubform('impact', {
title: 'Impact on Your Decision',
isVisible: () => detailsSection.textarea('whatHappened')?.value()?.trim() !== '',
customStyles: { backgroundColor: '#f0fdf4', padding: '16px', borderRadius: '8px' }
});
impactSection.addRow(row => {
row.addRadioButton('futureIntent', {
label: 'Based on this experience, what is your current intention?',
options: [
{ id: 'stay-hopeful', name: 'I am willing to give you another chance' },
{ id: 'considering-leaving', name: 'I am considering alternatives' },
{ id: 'decided-to-leave', name: 'I have decided to stop using your service' },
{ id: 'already-switched', name: 'I have already switched to a competitor' }
],
orientation: 'vertical',
isRequired: true
});
});
impactSection.addRow(row => {
row.addThumbRating('wouldRecommend', {
label: 'Would you recommend us to others right now?',
size: 'lg',
showLabels: true,
upLabel: 'Maybe, if issues are resolved',
downLabel: 'No, not at this time',
alignment: 'center',
isVisible: () => impactSection.radioButton('futureIntent')?.value() !== null
});
});
// ============================================
// SECTION 5: Recovery Options
// ============================================
const recoverySection = form.addSubform('recovery', {
title: () => {
const intent = impactSection.radioButton('futureIntent')?.value();
if (intent === 'stay-hopeful') return 'How Can We Make It Up to You?';
if (intent === 'considering-leaving') return 'Give Us a Chance to Fix This';
if (intent === 'decided-to-leave') return 'We Would Love to Keep You';
return 'Recovery Options';
},
isVisible: () => impactSection.radioButton('futureIntent')?.value() !== null,
customStyles: () => {
const intent = impactSection.radioButton('futureIntent')?.value();
if (intent === 'stay-hopeful') return { backgroundColor: '#dcfce7', padding: '16px', borderRadius: '8px' };
if (intent === 'considering-leaving') return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
}
});
recoverySection.addRow(row => {
row.addRadioButton('recoveryOption', {
label: 'What would help resolve this situation?',
options: () => {
const intent = impactSection.radioButton('futureIntent')?.value();
const baseOptions = [
{ id: 'callback', name: 'A call from a manager to discuss' },
{ id: 'credit', name: 'A discount or credit on my account' },
{ id: 'replacement', name: 'Product replacement or re-service' },
{ id: 'expedite', name: 'Expedited resolution of my issue' },
{ id: 'apology', name: 'A sincere apology is enough' }
];
if (intent === 'decided-to-leave' || intent === 'already-switched') {
baseOptions.push({ id: 'nothing', name: 'Nothing, my decision is final' });
}
return baseOptions;
},
orientation: 'vertical',
isRequired: true,
onValueChange: (val) => recoveryPath.set(val ?? null)
});
});
// ============================================
// SECTION 6: Additional Information (Conditional)
// ============================================
const additionalSection = form.addSubform('additional', {
title: 'Additional Details',
isVisible: () => {
const option = recoverySection.radioButton('recoveryOption')?.value();
return option !== null && option !== 'nothing' && option !== 'apology';
},
customStyles: { backgroundColor: '#f8fafc', padding: '16px', borderRadius: '8px' }
});
additionalSection.addRow(row => {
row.addTextbox('preferredTime', {
label: 'Best time to contact you (if callback selected)',
placeholder: 'e.g., Weekdays after 3 PM',
isVisible: () => recoveryPath() === 'callback'
});
});
additionalSection.addRow(row => {
row.addTextbox('orderNumber', {
label: 'Order/Reference number (if applicable)',
placeholder: 'e.g., ORD-12345',
isVisible: () => recoveryPath() === 'replacement' || recoveryPath() === 'expedite'
});
});
additionalSection.addRow(row => {
row.addTextarea('additionalComments', {
label: 'Any additional information that would help us:',
placeholder: 'Share anything else we should know...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 7: Contact Preference
// ============================================
const contactSection = form.addSubform('contact', {
title: 'Stay Connected',
isVisible: () => {
const option = recoverySection.radioButton('recoveryOption')?.value();
return option !== null && option !== 'nothing';
}
});
contactSection.addRow(row => {
row.addCheckbox('wantFollowUp', {
label: 'I would like to be contacted about my feedback'
});
});
contactSection.addRow(row => {
row.addEmail('email', {
label: 'Email address',
placeholder: 'your@email.com',
isVisible: () => contactSection.checkbox('wantFollowUp')?.value() === true,
isRequired: () => contactSection.checkbox('wantFollowUp')?.value() === true
});
});
contactSection.addRow(row => {
row.addTextbox('phone', {
label: 'Phone number (optional)',
placeholder: '+1 (555) 123-4567',
isVisible: () => contactSection.checkbox('wantFollowUp')?.value() === true && recoveryPath() === 'callback'
});
});
// ============================================
// SECTION 8: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => recoverySection.radioButton('recoveryOption')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const mood = satisfactionSection.emojiRating('currentMood')?.value();
const disappointment = satisfactionSection.ratingScale('dissatisfactionLevel')?.value();
const problems = problemSection.checkboxList('problemCategories')?.value() || [];
const intent = impactSection.radioButton('futureIntent')?.value();
const recovery = recoverySection.radioButton('recoveryOption')?.value();
if (!recovery) return '';
const moodEmojis: Record<string, string> = {
'very-bad': 'Very Unhappy',
'bad': 'Unhappy',
'neutral': 'Neutral',
'good': 'Okay',
'excellent': 'Good'
};
const intentLabels: Record<string, string> = {
'stay-hopeful': 'Willing to give another chance',
'considering-leaving': 'Considering alternatives',
'decided-to-leave': 'Decided to leave',
'already-switched': 'Already switched'
};
const recoveryLabels: Record<string, string> = {
'callback': 'Manager callback',
'credit': 'Account credit/discount',
'replacement': 'Product replacement',
'expedite': 'Expedited resolution',
'apology': 'Apology accepted',
'nothing': 'No recovery needed'
};
let summary = 'Recovery Case Summary\n';
summary += '═'.repeat(25) + '\n\n';
summary += `Current Mood: ${moodEmojis[mood || ''] || mood || 'Not specified'}\n`;
summary += `Disappointment Level: ${disappointment || 'N/A'}/10\n`;
summary += `Problem Areas: ${problems.length} identified\n`;
summary += `Customer Intent: ${intentLabels[intent || ''] || intent || 'N/A'}\n`;
summary += `Recovery Request: ${recoveryLabels[recovery] || recovery}\n`;
const wantsFollowUp = contactSection.checkbox('wantFollowUp')?.value();
if (wantsFollowUp) {
summary += '\nFollow-up requested';
}
return summary;
},
customStyles: () => {
const intent = impactSection.radioButton('futureIntent')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px'
};
if (intent === 'stay-hopeful') {
return { ...baseStyles, backgroundColor: '#dcfce7', borderLeft: '4px solid #22c55e' };
} else if (intent === 'considering-leaving') {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: () => {
const recovery = recoverySection.radioButton('recoveryOption')?.value();
if (recovery === 'nothing') return 'Submit Feedback';
return 'Submit & Request Recovery';
},
isVisible: () => recoverySection.radioButton('recoveryOption')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: () => {
const recovery = recoverySection.radioButton('recoveryOption')?.value();
if (recovery === 'nothing') return 'Thank You for Your Feedback';
return 'We Will Make This Right';
},
message: () => {
const recovery = recoverySection.radioButton('recoveryOption')?.value();
const wantsFollowUp = contactSection.checkbox('wantFollowUp')?.value();
if (recovery === 'nothing') {
return 'We appreciate you taking the time to share your experience. Your feedback will help us improve for future customers.';
}
if (wantsFollowUp) {
return 'Your recovery request has been submitted. A member of our team will reach out to you within 24-48 hours. We are committed to making this right.';
}
return 'Your recovery request has been submitted. We are taking immediate action to address your concerns. Thank you for giving us the opportunity to make things right.';
}
});
}