export function serviceRecoverySurvey(form: FormTs) {
// Service Recovery Survey - Before/After satisfaction tracking
// Demonstrates: StarRating x4, EmojiRating x2, MatrixQuestion, RatingScale (CES),
// Slider, RadioButton, CheckboxList, computed values, dynamic styling
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Service Recovery Feedback',
computedValue: () => 'We recently resolved an issue for you. Help us understand how well we did.',
customStyles: {
background: 'linear-gradient(135deg, #059669 0%, #10b981 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: The Issue (Before)
// ============================================
const beforeSection = form.addSubform('beforeSection', {
title: 'Before: When You Contacted Us',
customStyles: {
backgroundColor: '#fef2f2',
padding: '16px',
borderRadius: '8px',
borderLeft: '4px solid #ef4444'
}
});
beforeSection.addRow(row => {
row.addTextPanel('beforeInfo', {
computedValue: () => 'Think back to when you first experienced the issue',
customStyles: {
color: '#991b1b',
fontSize: '14px',
fontStyle: 'italic',
marginBottom: '8px'
}
});
});
beforeSection.addRow(row => {
row.addEmojiRating('moodBefore', {
label: 'How were you feeling when you contacted us?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
beforeSection.addRow(row => {
row.addStarRating('satisfactionBefore', {
label: 'Your satisfaction level at that point',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
beforeSection.addRow(row => {
row.addRadioButton('issueType', {
label: 'What type of issue did you experience?',
options: [
{ id: 'product-defect', name: 'Product defect or quality issue' },
{ id: 'delivery', name: 'Delivery or shipping problem' },
{ id: 'billing', name: 'Billing or payment error' },
{ id: 'service', name: 'Service or support issue' },
{ id: 'communication', name: 'Miscommunication or wrong information' },
{ id: 'other', name: 'Other' }
],
orientation: 'vertical'
});
});
// ============================================
// SECTION 2: The Resolution Process
// ============================================
const processSection = form.addSubform('processSection', {
title: 'The Resolution Process'
});
processSection.addRow(row => {
row.addMatrixQuestion('resolutionProcess', {
label: 'Rate the following aspects of how we handled your issue:',
rows: [
{ id: 'speed', label: 'Speed of initial response', isRequired: true },
{ id: 'empathy', label: 'Empathy and understanding shown', isRequired: true },
{ id: 'communication', label: 'Communication throughout' },
{ id: 'solution', label: 'Quality of the solution offered' },
{ id: 'follow-through', label: 'Follow-through on promises' },
{ id: 'ease', label: 'Ease of getting help' }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
processSection.addRow(row => {
row.addRatingScale('effortScore', {
preset: 'ces',
label: 'How easy was it to get your issue resolved?',
lowLabel: 'Very Difficult',
highLabel: 'Very Easy'
});
});
processSection.addRow(row => {
row.addCheckboxList('recoveryActions', {
label: 'Which recovery actions did we take? (Select all that apply)',
options: [
{ id: 'apology', name: 'Sincere apology' },
{ id: 'explanation', name: 'Clear explanation of what happened' },
{ id: 'refund', name: 'Refund or credit' },
{ id: 'replacement', name: 'Replacement product/service' },
{ id: 'discount', name: 'Discount on future purchase' },
{ id: 'escalation', name: 'Escalation to senior staff' },
{ id: 'follow-up', name: 'Follow-up to ensure satisfaction' },
{ id: 'process-fix', name: 'Promise to fix underlying process' }
],
orientation: 'vertical'
});
});
// ============================================
// SECTION 3: After Resolution
// ============================================
const afterSection = form.addSubform('afterSection', {
title: 'After: Now That It\'s Resolved',
customStyles: {
backgroundColor: '#ecfdf5',
padding: '16px',
borderRadius: '8px',
borderLeft: '4px solid #22c55e'
}
});
afterSection.addRow(row => {
row.addTextPanel('afterInfo', {
computedValue: () => 'Rate how you feel now, after the resolution',
customStyles: {
color: '#166534',
fontSize: '14px',
fontStyle: 'italic',
marginBottom: '8px'
}
});
});
afterSection.addRow(row => {
row.addEmojiRating('moodAfter', {
label: 'How are you feeling now?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
afterSection.addRow(row => {
row.addStarRating('satisfactionAfter', {
label: 'Your satisfaction level now',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
// ============================================
// SECTION 4: Recovery Score
// ============================================
const recoveryScoreSection = form.addSubform('recoveryScoreSection', {
title: 'Recovery Analysis'
});
recoveryScoreSection.addRow(row => {
row.addTextPanel('recoveryCalculation', {
computedValue: () => {
const before = beforeSection.starRating('satisfactionBefore')?.value();
const after = afterSection.starRating('satisfactionAfter')?.value();
const moodBefore = beforeSection.emojiRating('moodBefore')?.value();
const moodAfter = afterSection.emojiRating('moodAfter')?.value();
if (!before || !after) return 'Complete the before/after ratings to see your recovery analysis.';
const change = after - before;
const recoveryRate = before > 0 ? Math.round(((after - before) / (5 - before)) * 100) : 0;
let emoji, status;
if (after >= 4 && change >= 0) {
emoji = '🎉';
status = 'Excellent Recovery';
} else if (change > 0) {
emoji = '📈';
status = 'Positive Recovery';
} else if (change === 0) {
emoji = '➡️';
status = 'Maintained';
} else {
emoji = '⚠️';
status = 'Recovery Needed';
}
const moodLabels: Record<string, string> = {
'very-bad': '😡 Very Unhappy',
'bad': '😕 Unhappy',
'neutral': '😐 Neutral',
'good': '🙂 Satisfied',
'excellent': '😊 Very Satisfied'
};
let summary = `${emoji} ${status}\n`;
summary += `${'─'.repeat(30)}\n\n`;
summary += `⭐ Satisfaction: ${before}/5 → ${after}/5 (${change >= 0 ? '+' : ''}${change})\n`;
if (recoveryRate > 0) {
summary += `📊 Recovery Rate: ${recoveryRate}%\n`;
}
if (moodBefore && moodAfter) {
summary += `\n😊 Mood:\n`;
summary += ` Before: ${moodLabels[moodBefore] || moodBefore}\n`;
summary += ` After: ${moodLabels[moodAfter] || moodAfter}`;
}
return summary;
},
customStyles: () => {
const before = beforeSection.starRating('satisfactionBefore')?.value();
const after = afterSection.starRating('satisfactionAfter')?.value();
const base = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px',
textAlign: 'center'
};
if (!before || !after) {
return { ...base, backgroundColor: '#f8fafc', borderLeft: '4px solid #94a3b8' };
}
const change = after - before;
if (after >= 4 && change >= 0) {
return { ...base, backgroundColor: '#dcfce7', borderLeft: '4px solid #22c55e' };
} else if (change > 0) {
return { ...base, backgroundColor: '#e0f2fe', borderLeft: '4px solid #0ea5e9' };
} else if (change === 0) {
return { ...base, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
return { ...base, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
});
});
// ============================================
// SECTION 5: Trust & Loyalty
// ============================================
const loyaltySection = form.addSubform('loyaltySection', {
title: 'Trust & Future Relationship'
});
loyaltySection.addRow(row => {
row.addStarRating('trustRestored', {
label: 'How much has your trust in us been restored?',
maxStars: 5,
size: 'lg',
alignment: 'center'
}, '1fr');
row.addStarRating('recommendLikelihood', {
label: 'How likely are you to recommend us now?',
maxStars: 5,
size: 'lg',
alignment: 'center'
}, '1fr');
});
loyaltySection.addRow(row => {
row.addRadioButton('futureIntention', {
label: 'After this experience, what are your intentions?',
options: [
{ id: 'stronger', name: 'I\'m MORE likely to continue doing business with you' },
{ id: 'same', name: 'I\'ll continue as before - no change' },
{ id: 'cautious', name: 'I\'ll continue but will be more cautious' },
{ id: 'consider-leaving', name: 'I\'m considering alternatives' },
{ id: 'leaving', name: 'I plan to switch to a competitor' }
],
orientation: 'vertical'
});
});
// Conditional follow-up for negative responses
loyaltySection.addRow(row => {
row.addTextarea('whatWouldHelp', {
label: 'What would help restore your confidence in us?',
placeholder: 'Please let us know what we can do to make this right...',
rows: 2,
isVisible: () => {
const intention = loyaltySection.radioButton('futureIntention')?.value();
return intention === 'consider-leaving' || intention === 'leaving' || intention === 'cautious';
}
});
});
// ============================================
// SECTION 6: Additional Comments
// ============================================
const commentsSection = form.addSubform('commentsSection', {
title: 'Final Thoughts'
});
commentsSection.addSpacer();
commentsSection.addRow(row => {
row.addTextarea('recoveryHighlight', {
label: () => {
const after = afterSection.starRating('satisfactionAfter')?.value();
if (after && after >= 4) {
return 'What did we do particularly well in resolving your issue?';
}
return 'What could we have done better to resolve your issue?';
},
placeholder: 'Your feedback helps us improve our recovery process...',
rows: 2
});
});
commentsSection.addRow(row => {
row.addTextarea('additionalComments', {
label: 'Anything else you\'d like us to know?',
placeholder: 'Additional thoughts, concerns, or suggestions...',
rows: 2
});
});
// ============================================
// SUMMARY SECTION
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Recovery Summary'
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const before = beforeSection.starRating('satisfactionBefore')?.value();
const after = afterSection.starRating('satisfactionAfter')?.value();
const effortScore = processSection.ratingScale('effortScore')?.value();
const trustRestored = loyaltySection.starRating('trustRestored')?.value();
const futureIntention = loyaltySection.radioButton('futureIntention')?.value();
const actions = processSection.checkboxList('recoveryActions')?.value() || [];
if (!before || !after) return 'Complete the survey to see your recovery summary.';
const change = after - before;
let overallStatus = '';
let emoji = '';
if (after >= 4 && change >= 0) {
emoji = '🎉';
overallStatus = 'Successful Recovery';
} else if (change > 0) {
emoji = '📈';
overallStatus = 'Partial Recovery';
} else if (change === 0) {
emoji = '➡️';
overallStatus = 'No Change';
} else {
emoji = '⚠️';
overallStatus = 'Recovery Failed';
}
let summary = `${emoji} Service Recovery Report\n`;
summary += `${'═'.repeat(35)}\n\n`;
summary += `📊 Status: ${overallStatus}\n`;
summary += `⭐ Satisfaction: ${before}/5 → ${after}/5\n`;
if (effortScore) {
const effortLabel = effortScore >= 6 ? 'Easy' : effortScore >= 4 ? 'Moderate' : 'Difficult';
summary += `💪 Resolution Effort: ${effortLabel} (${effortScore}/7)\n`;
}
if (trustRestored) {
summary += `🤝 Trust Restored: ${trustRestored}/5\n`;
}
if (actions.length > 0) {
summary += `\n✅ Actions Taken: ${actions.length} recovery actions`;
}
if (futureIntention) {
const intentionLabels: Record<string, string> = {
'stronger': '💚 More loyal',
'same': '➡️ No change',
'cautious': '⚠️ Cautious',
'consider-leaving': '🔴 At risk',
'leaving': '❌ Churning'
};
summary += `\n\n${intentionLabels[futureIntention] || futureIntention}`;
}
return summary;
},
customStyles: () => {
const before = beforeSection.starRating('satisfactionBefore')?.value();
const after = afterSection.starRating('satisfactionAfter')?.value();
const base = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (!before || !after) {
return { ...base, backgroundColor: '#f8fafc' };
}
const change = after - before;
if (after >= 4 && change >= 0) {
return { ...base, backgroundColor: '#dcfce7', borderLeft: '4px solid #22c55e' };
} else if (change > 0) {
return { ...base, backgroundColor: '#e0f2fe', borderLeft: '4px solid #0ea5e9' };
} else if (change === 0) {
return { ...base, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
return { ...base, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Recovery Feedback'
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank you for your feedback on our service recovery!',
message: 'We take service failures seriously and your feedback helps us improve how we handle these situations. Every response teaches us how to serve you better. We appreciate you giving us the opportunity to make things right.'
});
}