export function appUpdateFeedback(form: FormTs) {
// App Update Reaction Survey - Post-update feedback collection
// Demonstrates: EmojiRating, SuggestionChips, StarRating, ThumbRating, RadioButton, conditional flow
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: "What's New - Version 2.5.0",
computedValue: () => "We've just released exciting updates! Tell us what you think.",
customStyles: {
background: 'linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)',
color: 'white',
padding: '24px',
borderRadius: '16px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: First Reaction
// ============================================
const reactionSection = form.addSubform('firstReaction', {
title: 'Your First Reaction'
});
reactionSection.addRow(row => {
row.addEmojiRating('overallReaction', {
label: 'How do you feel about this update?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center',
isRequired: true
});
});
// Dynamic response message
reactionSection.addRow(row => {
row.addTextPanel('reactionResponse', {
computedValue: () => {
const reaction = reactionSection.emojiRating('overallReaction')?.value();
if (!reaction) return '';
const responses: Record<string, string> = {
'very-bad': "We're sorry to hear that. Please tell us more so we can improve.",
'bad': "Thanks for the honest feedback. We'd love to understand what went wrong.",
'neutral': "Thanks for sharing! What would make this update better?",
'good': "Glad you like it! Tell us what you enjoyed most.",
'excellent': "Awesome! We're thrilled you love it! 🎉"
};
return responses[reaction] || '';
},
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null,
customStyles: () => {
const reaction = reactionSection.emojiRating('overallReaction')?.value();
const base = { padding: '12px', borderRadius: '8px', textAlign: 'center' };
if (reaction === 'very-bad' || reaction === 'bad') {
return { ...base, backgroundColor: '#fee2e2', color: '#991b1b' };
}
if (reaction === 'excellent' || reaction === 'good') {
return { ...base, backgroundColor: '#d1fae5', color: '#065f46' };
}
return { ...base, backgroundColor: '#f1f5f9', color: '#475569' };
}
});
});
// ============================================
// SECTION 2: Features Tried
// ============================================
const featuresSection = form.addSubform('features', {
title: 'New Features',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
featuresSection.addRow(row => {
row.addSuggestionChips('featuresTried', {
label: 'Which new features have you tried? (Select all that apply)',
suggestions: [
{ id: 'dark-mode', name: 'Dark Mode' },
{ id: 'new-dashboard', name: 'New Dashboard' },
{ id: 'quick-actions', name: 'Quick Actions' },
{ id: 'notifications', name: 'Smart Notifications' },
{ id: 'sharing', name: 'Easy Sharing' },
{ id: 'search', name: 'Improved Search' },
{ id: 'performance', name: 'Faster Performance' },
{ id: 'accessibility', name: 'Accessibility Options' }
],
alignment: 'center'
});
});
// Favorite feature (if any tried)
featuresSection.addRow(row => {
row.addDropdown('favoriteFeature', {
label: 'Which feature do you like the most?',
options: () => {
const tried = featuresSection.suggestionChips('featuresTried')?.value() || [];
const featureNames: Record<string, string> = {
'dark-mode': 'Dark Mode',
'new-dashboard': 'New Dashboard',
'quick-actions': 'Quick Actions',
'notifications': 'Smart Notifications',
'sharing': 'Easy Sharing',
'search': 'Improved Search',
'performance': 'Faster Performance',
'accessibility': 'Accessibility Options'
};
return tried.map(id => ({ id, name: featureNames[id] || id }));
},
isVisible: () => (featuresSection.suggestionChips('featuresTried')?.value()?.length || 0) > 1,
placeholder: 'Select your favorite...'
});
});
// Feature ratings
const featureRatings = featuresSection.addSubform('featureRatings', {
title: 'Rate the Features You Tried',
isVisible: () => (featuresSection.suggestionChips('featuresTried')?.value()?.length || 0) > 0
});
featureRatings.addRow(row => {
row.addMatrixQuestion('ratings', {
label: 'How useful are these features to you?',
rows: () => {
const tried = featuresSection.suggestionChips('featuresTried')?.value() || [];
const featureLabels: Record<string, string> = {
'dark-mode': 'Dark Mode',
'new-dashboard': 'New Dashboard',
'quick-actions': 'Quick Actions',
'notifications': 'Smart Notifications',
'sharing': 'Easy Sharing',
'search': 'Improved Search',
'performance': 'Faster Performance',
'accessibility': 'Accessibility Options'
};
return tried.slice(0, 5).map(id => ({
id,
label: featureLabels[id] || id,
isRequired: false
}));
},
columns: [
{ id: 'not-useful', label: 'Not Useful' },
{ id: 'somewhat', label: 'Somewhat' },
{ id: 'useful', label: 'Useful' },
{ id: 'very-useful', label: 'Very Useful' },
{ id: 'essential', label: 'Essential' }
],
fullWidth: true
});
});
// ============================================
// SECTION 3: Performance & Stability
// ============================================
const performanceSection = form.addSubform('performance', {
title: 'Performance & Stability',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
performanceSection.addRow(row => {
row.addStarRating('appSpeed', {
label: 'App Speed',
maxStars: 5,
size: 'md'
}, '1fr');
row.addStarRating('appStability', {
label: 'Stability (no crashes)',
maxStars: 5,
size: 'md'
}, '1fr');
});
performanceSection.addRow(row => {
row.addThumbRating('encounteredBugs', {
label: 'Did you encounter any bugs or issues?',
showLabels: true,
upLabel: 'Yes, found issues',
downLabel: 'No, all good',
alignment: 'center'
});
});
// Bug report section (conditional)
const bugSection = performanceSection.addSubform('bugReport', {
title: 'Report the Issue',
isVisible: () => performanceSection.thumbRating('encounteredBugs')?.value() === 'up',
customStyles: { backgroundColor: '#fef2f2', padding: '16px', borderRadius: '8px' }
});
bugSection.addRow(row => {
row.addRadioButton('bugSeverity', {
label: 'How severe is the issue?',
options: [
{ id: 'critical', name: 'Critical - App crashes/unusable' },
{ id: 'major', name: 'Major - Feature not working' },
{ id: 'minor', name: 'Minor - Annoying but workable' },
{ id: 'cosmetic', name: 'Cosmetic - Visual glitch' }
],
orientation: 'vertical'
});
});
bugSection.addRow(row => {
row.addTextarea('bugDescription', {
label: 'Describe what happened',
placeholder: 'What were you doing when the issue occurred?',
rows: 3,
isRequired: true
});
});
bugSection.addRow(row => {
row.addTextbox('deviceInfo', {
label: 'Device/Browser (optional)',
placeholder: 'e.g., iPhone 15, Chrome on Windows'
});
});
// ============================================
// SECTION 4: Design & Usability
// ============================================
const designSection = form.addSubform('design', {
title: 'Design & Usability',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
designSection.addRow(row => {
row.addStarRating('visualDesign', {
label: 'Visual Design',
maxStars: 5,
size: 'md'
}, '1fr');
row.addStarRating('easeOfUse', {
label: 'Ease of Use',
maxStars: 5,
size: 'md'
}, '1fr');
});
designSection.addRow(row => {
row.addRadioButton('navigationChange', {
label: 'How do you feel about the updated navigation?',
options: [
{ id: 'love', name: 'Love it - much better' },
{ id: 'like', name: 'Like it - slight improvement' },
{ id: 'neutral', name: 'Neutral - no strong opinion' },
{ id: 'dislike', name: 'Dislike - harder to use' },
{ id: 'hate', name: 'Really dislike - please revert' }
],
orientation: 'vertical'
});
});
// Navigation concerns (for negative feedback)
designSection.addRow(row => {
row.addTextarea('navigationIssues', {
label: 'What specifically is harder to use?',
placeholder: 'Tell us what you struggle with...',
rows: 2,
isVisible: () => {
const nav = designSection.radioButton('navigationChange')?.value();
return nav === 'dislike' || nav === 'hate';
}
});
});
// ============================================
// SECTION 5: Missing Features
// ============================================
const missingSection = form.addSubform('missing', {
title: 'What Else Would You Like?',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
missingSection.addRow(row => {
row.addThumbRating('missingFeatures', {
label: 'Are there features you expected in this update?',
showLabels: true,
upLabel: 'Yes, I wanted more',
downLabel: 'No, this is complete',
alignment: 'center'
});
});
missingSection.addRow(row => {
row.addTextarea('featureRequests', {
label: 'What features would you like to see next?',
placeholder: 'Share your ideas...',
rows: 3,
isVisible: () => missingSection.thumbRating('missingFeatures')?.value() === 'up'
});
});
// ============================================
// SECTION 6: Overall & Recommendation
// ============================================
const overallSection = form.addSubform('overall', {
title: 'Overall Assessment',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
overallSection.addRow(row => {
row.addRatingScale('updateRating', {
label: 'Overall, how would you rate this update?',
preset: 'satisfaction',
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRadioButton('updateComparison', {
label: 'Compared to the previous version, this update is:',
options: [
{ id: 'much-better', name: 'Much better' },
{ id: 'better', name: 'Somewhat better' },
{ id: 'same', name: 'About the same' },
{ id: 'worse', name: 'Somewhat worse' },
{ id: 'much-worse', name: 'Much worse' }
],
orientation: 'horizontal'
});
});
// Negative comparison follow-up
overallSection.addRow(row => {
row.addTextarea('worseReason', {
label: 'What made the previous version better?',
placeholder: 'Help us understand what we should bring back...',
rows: 2,
isVisible: () => {
const comp = overallSection.radioButton('updateComparison')?.value();
return comp === 'worse' || comp === 'much-worse';
}
});
});
// Additional comments
overallSection.addSpacer({ height: '16px' });
overallSection.addRow(row => {
row.addTextarea('additionalFeedback', {
label: () => {
const reaction = reactionSection.emojiRating('overallReaction')?.value();
if (reaction === 'excellent' || reaction === 'good') {
return 'Anything else you love about this update?';
}
if (reaction === 'very-bad' || reaction === 'bad') {
return 'How can we make things right?';
}
return 'Any other feedback or suggestions?';
},
placeholder: 'Share your thoughts...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 7: Contact Permission
// ============================================
const contactSection = form.addSubform('contact', {
title: 'Stay Connected',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
contactSection.addRow(row => {
row.addCheckbox('joinBeta', {
label: 'I want to test new features before release (join beta program)'
});
});
contactSection.addRow(row => {
row.addCheckbox('allowContact', {
label: 'You may contact me about my feedback'
});
});
contactSection.addRow(row => {
row.addEmail('email', {
label: 'Email address',
placeholder: 'your@email.com',
isVisible: () =>
contactSection.checkbox('joinBeta')?.value() === true ||
contactSection.checkbox('allowContact')?.value() === true,
isRequired: () =>
contactSection.checkbox('joinBeta')?.value() === true ||
contactSection.checkbox('allowContact')?.value() === true
});
});
// ============================================
// SECTION 8: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const reaction = reactionSection.emojiRating('overallReaction')?.value();
const features = featuresSection.suggestionChips('featuresTried')?.value() || [];
const bugs = performanceSection.thumbRating('encounteredBugs')?.value();
const updateRating = overallSection.ratingScale('updateRating')?.value();
const comparison = overallSection.radioButton('updateComparison')?.value();
if (!reaction) return '';
const reactionLabels: Record<string, string> = {
'very-bad': '😞 Not happy',
'bad': '😕 Disappointed',
'neutral': '😐 Neutral',
'good': '😊 Positive',
'excellent': '😍 Love it!'
};
let summary = '📱 Update Feedback Summary\n';
summary += `${'═'.repeat(26)}\n\n`;
summary += `First Reaction: ${reactionLabels[reaction] || reaction}\n`;
summary += `Features Tried: ${features.length}\n`;
summary += `Bugs Found: ${bugs === 'up' ? 'Yes' : 'No'}\n`;
if (updateRating) {
summary += `\nOverall Rating: ${updateRating}/5`;
}
if (comparison) {
const compLabels: Record<string, string> = {
'much-better': 'Much better than before',
'better': 'Better than before',
'same': 'Same as before',
'worse': 'Worse than before',
'much-worse': 'Much worse than before'
};
summary += `\n${compLabels[comparison] || comparison}`;
}
return summary;
},
customStyles: () => {
const reaction = reactionSection.emojiRating('overallReaction')?.value();
const base = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (reaction === 'excellent' || reaction === 'good') {
return { ...base, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
}
if (reaction === 'very-bad' || reaction === 'bad') {
return { ...base, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...base, backgroundColor: '#f1f5f9', borderLeft: '4px solid #64748b' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => reactionSection.emojiRating('overallReaction')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thanks for Your Feedback!',
message: 'Your input helps us build a better app. We read every piece of feedback and use it to guide our development priorities.'
});
}