export function renovationSatisfactionSurvey(form: FormTs) {
// Renovation Satisfaction Survey
// Demonstrates: StarRating, RatingScale (NPS), MatrixQuestion, Slider (before/after), EmojiRating, RadioButton, CheckboxList, SuggestionChips
// Track budget state
const budgetState = form.state<{ original: number | null; final: number | null }>({ original: null, final: null });
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Renovation Project Feedback',
computedValue: () => 'Help us improve our services. Your feedback is valuable.',
customStyles: {
backgroundColor: '#78716c',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Project Details
// ============================================
const projectSection = form.addSubform('projectDetails', {
title: 'Project Information'
});
projectSection.addRow(row => {
row.addCheckboxList('projectType', {
label: 'What type of renovation was completed? (Select all that apply)',
options: [
{ id: 'kitchen', name: 'Kitchen remodel' },
{ id: 'bathroom', name: 'Bathroom renovation' },
{ id: 'basement', name: 'Basement finishing' },
{ id: 'addition', name: 'Room addition' },
{ id: 'exterior', name: 'Exterior/Siding' },
{ id: 'roofing', name: 'Roofing' },
{ id: 'flooring', name: 'Flooring' },
{ id: 'painting', name: 'Painting' },
{ id: 'electrical', name: 'Electrical' },
{ id: 'plumbing', name: 'Plumbing' },
{ id: 'hvac', name: 'HVAC' },
{ id: 'whole-home', name: 'Whole home renovation' }
],
orientation: 'vertical',
isRequired: true,
min: 1
}, '1fr');
row.addRadioButton('projectDuration', {
label: 'How long did the project take?',
options: [
{ id: 'under-week', name: 'Less than 1 week' },
{ id: '1-2-weeks', name: '1-2 weeks' },
{ id: '2-4-weeks', name: '2-4 weeks' },
{ id: '1-2-months', name: '1-2 months' },
{ id: '2-6-months', name: '2-6 months' },
{ id: 'over-6-months', name: 'Over 6 months' }
],
orientation: 'vertical',
isRequired: true
}, '1fr');
});
projectSection.addRow(row => {
row.addRadioButton('projectScale', {
label: 'How would you describe the project scale?',
options: [
{ id: 'minor', name: 'Minor update (cosmetic changes)' },
{ id: 'moderate', name: 'Moderate renovation (some structural)' },
{ id: 'major', name: 'Major renovation (significant changes)' },
{ id: 'complete', name: 'Complete gut renovation' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 2: Budget Analysis
// ============================================
const budgetSection = form.addSubform('budgetAnalysis', {
title: 'Budget & Value',
isVisible: () => projectSection.checkboxList('projectType')?.value()?.length! > 0
});
budgetSection.addRow(row => {
row.addDecimal('originalBudget', {
label: 'Original estimated budget ($)',
placeholder: 'e.g., 50000',
min: 0,
onValueChange: (val) => budgetState.update(s => ({ ...s, original: val ?? null }))
}, '1fr');
row.addDecimal('finalCost', {
label: 'Final project cost ($)',
placeholder: 'e.g., 55000',
min: 0,
onValueChange: (val) => budgetState.update(s => ({ ...s, final: val ?? null }))
}, '1fr');
});
budgetSection.addRow(row => {
row.addTextPanel('budgetComparison', {
label: 'Budget Analysis',
computedValue: () => {
const original = budgetState().original;
const final = budgetState().final;
if (!original || !final) return '';
const difference = final - original;
const percentChange = ((difference / original) * 100).toFixed(1);
if (difference === 0) {
return `Perfect! The project came in exactly on budget.`;
} else if (difference > 0) {
return `The project was $${difference.toLocaleString()} over budget (+${percentChange}%).`;
} else {
return `Great news! The project came in $${Math.abs(difference).toLocaleString()} under budget (${percentChange}%).`;
}
},
customStyles: () => {
const original = budgetState().original;
const final = budgetState().final;
const baseStyles = {
padding: '12px',
borderRadius: '6px',
textAlign: 'center' as const
};
if (!original || !final) return { ...baseStyles, display: 'none' };
const difference = final - original;
const percentOver = (difference / original) * 100;
if (percentOver > 20) {
return { ...baseStyles, backgroundColor: '#fee2e2', color: '#991b1b' };
} else if (percentOver > 10) {
return { ...baseStyles, backgroundColor: '#fef3c7', color: '#92400e' };
} else if (percentOver > 0) {
return { ...baseStyles, backgroundColor: '#fef9c3', color: '#713f12' };
} else {
return { ...baseStyles, backgroundColor: '#d1fae5', color: '#065f46' };
}
},
isVisible: () => budgetState().original !== null && budgetState().final !== null
});
});
budgetSection.addRow(row => {
row.addRatingScale('budgetSatisfaction', {
label: 'How satisfied are you with the value for money?',
preset: 'satisfaction',
size: 'md',
alignment: 'center'
});
});
// ============================================
// SECTION 3: Before/After Comparison
// ============================================
const comparisonSection = form.addSubform('beforeAfter', {
title: 'Before & After',
isVisible: () => projectSection.checkboxList('projectType')?.value()?.length! > 0,
customStyles: { backgroundColor: '#faf5ff', padding: '16px', borderRadius: '8px' }
});
comparisonSection.addRow(row => {
row.addSlider('satisfactionBefore', {
label: 'Satisfaction with your space BEFORE renovation',
min: 1,
max: 10,
step: 1,
showValue: true,
defaultValue: 3
}, '1fr');
row.addSlider('satisfactionAfter', {
label: 'Satisfaction with your space AFTER renovation',
min: 1,
max: 10,
step: 1,
showValue: true,
defaultValue: 8
}, '1fr');
});
comparisonSection.addRow(row => {
row.addTextPanel('improvementScore', {
label: 'Improvement Score',
computedValue: () => {
const before = comparisonSection.slider('satisfactionBefore')?.value() || 3;
const after = comparisonSection.slider('satisfactionAfter')?.value() || 8;
const improvement = after - before;
if (improvement >= 5) return `Transformation! +${improvement} point improvement in satisfaction.`;
if (improvement >= 3) return `Significant improvement: +${improvement} points.`;
if (improvement > 0) return `Improvement: +${improvement} points.`;
if (improvement === 0) return 'Satisfaction level unchanged.';
return `Satisfaction decreased by ${Math.abs(improvement)} points.`;
},
customStyles: () => {
const before = comparisonSection.slider('satisfactionBefore')?.value() || 3;
const after = comparisonSection.slider('satisfactionAfter')?.value() || 8;
const improvement = after - before;
return {
padding: '12px',
borderRadius: '6px',
textAlign: 'center',
backgroundColor: improvement >= 3 ? '#d1fae5' : improvement > 0 ? '#fef3c7' : '#fee2e2',
fontWeight: 'bold'
};
}
});
});
// ============================================
// SECTION 4: Workmanship Quality Matrix
// ============================================
const qualitySection = form.addSubform('workmanship', {
title: 'Workmanship Quality',
isVisible: () => projectSection.checkboxList('projectType')?.value()?.length! > 0
});
qualitySection.addRow(row => {
row.addMatrixQuestion('qualityMatrix', {
label: 'Please rate the following aspects of the work:',
rows: [
{ id: 'craftsmanship', label: 'Overall craftsmanship', isRequired: true },
{ id: 'attention', label: 'Attention to detail' },
{ id: 'materials', label: 'Quality of materials used' },
{ id: 'finishes', label: 'Finish work and touch-ups' },
{ id: 'cleanup', label: 'Site cleanup and organization' },
{ id: 'protection', label: 'Protection of existing property' }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
qualitySection.addRow(row => {
row.addStarRating('overallQuality', {
label: 'Overall quality of workmanship',
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true
});
});
// ============================================
// SECTION 5: Team & Communication
// ============================================
const teamSection = form.addSubform('teamCommunication', {
title: 'Team & Communication',
isVisible: () => projectSection.checkboxList('projectType')?.value()?.length! > 0
});
teamSection.addRow(row => {
row.addStarRating('projectManager', {
label: 'Project manager responsiveness',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
row.addStarRating('crewProfessionalism', {
label: 'Crew professionalism',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
});
teamSection.addRow(row => {
row.addMatrixQuestion('communicationMatrix', {
label: 'Rate communication throughout the project:',
rows: [
{ id: 'updates', label: 'Regular progress updates' },
{ id: 'changes', label: 'Handling of change orders' },
{ id: 'concerns', label: 'Response to concerns/questions' },
{ id: 'timeline', label: 'Timeline communication' },
{ id: 'final', label: 'Final walkthrough thoroughness' }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'okay', label: 'Okay' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
// ============================================
// SECTION 6: Timeline Performance
// ============================================
const timelineSection = form.addSubform('timeline', {
title: 'Timeline & Scheduling',
isVisible: () => projectSection.checkboxList('projectType')?.value()?.length! > 0
});
timelineSection.addRow(row => {
row.addRadioButton('timelineAdherence', {
label: 'How did the actual timeline compare to the estimate?',
options: [
{ id: 'early', name: 'Finished early' },
{ id: 'on-time', name: 'Finished on time' },
{ id: 'slightly-late', name: 'Slightly delayed (up to 2 weeks)' },
{ id: 'late', name: 'Significantly delayed (2+ weeks)' },
{ id: 'very-late', name: 'Major delays (1+ month)' }
],
orientation: 'vertical'
});
});
timelineSection.addRow(row => {
row.addRatingScale('scheduleRespect', {
label: 'Were scheduled work times respected?',
preset: 'satisfaction',
size: 'md',
alignment: 'center'
});
});
// ============================================
// SECTION 7: Overall Experience
// ============================================
const overallSection = form.addSubform('overallExperience', {
title: 'Overall Experience',
isVisible: () => qualitySection.starRating('overallQuality')?.value() !== null,
customStyles: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
if (npsCategory === 'promoter') return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (npsCategory === 'passive') return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
if (npsCategory === 'detractor') return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #d1d5db' };
}
});
overallSection.addRow(row => {
row.addEmojiRating('overallFeeling', {
label: 'How do you feel about your renovated space?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRatingScale('npsScore', {
label: 'How likely are you to recommend us to friends or family?',
preset: 'nps',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true,
size: 'md'
});
});
// Conditional feedback
overallSection.addRow(row => {
row.addSuggestionChips('positives', {
label: 'What did we do well?',
suggestions: [
{ id: 'quality', name: 'Quality work' },
{ id: 'communication', name: 'Great communication' },
{ id: 'timeline', name: 'On schedule' },
{ id: 'clean', name: 'Clean worksite' },
{ id: 'professional', name: 'Professional crew' },
{ id: 'budget', name: 'Within budget' },
{ id: 'flexible', name: 'Flexible/accommodating' },
{ id: 'creative', name: 'Creative solutions' }
],
alignment: 'center',
isVisible: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
return npsCategory === 'promoter' || npsCategory === 'passive';
}
});
});
overallSection.addRow(row => {
row.addSuggestionChips('improvements', {
label: 'What could we improve?',
suggestions: [
{ id: 'communication', name: 'Communication' },
{ id: 'timeline', name: 'Timeline accuracy' },
{ id: 'cleanup', name: 'Daily cleanup' },
{ id: 'budget', name: 'Budget management' },
{ id: 'quality', name: 'Work quality' },
{ id: 'scheduling', name: 'Scheduling' },
{ id: 'management', name: 'Project management' },
{ id: 'finishing', name: 'Finishing details' }
],
alignment: 'center',
isVisible: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
return npsCategory === 'detractor' || npsCategory === 'passive';
}
});
});
// ============================================
// SECTION 8: Additional Feedback
// ============================================
const feedbackSection = form.addSubform('additionalFeedback', {
title: 'Additional Comments',
isVisible: () => overallSection.ratingScale('npsScore')?.value() !== null
});
feedbackSection.addSpacer();
feedbackSection.addRow(row => {
row.addTextarea('comments', {
label: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
if (npsCategory === 'promoter') return 'We would love to hear more about your experience!';
if (npsCategory === 'detractor') return 'Please help us understand how we can improve.';
return 'Any additional feedback or suggestions?';
},
placeholder: 'Share your thoughts...',
rows: 4,
autoExpand: true
});
});
feedbackSection.addRow(row => {
row.addCheckbox('canUseTestimonial', {
label: 'You may use my feedback as a testimonial (with my permission)',
isVisible: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
return npsCategory === 'promoter';
}
});
});
feedbackSection.addRow(row => {
row.addCheckbox('wantCallback', {
label: 'I would like someone to contact me about my feedback',
isVisible: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
return npsCategory === 'detractor' || npsCategory === 'passive';
}
});
});
// ============================================
// SECTION 9: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => overallSection.ratingScale('npsScore')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const projects = projectSection.checkboxList('projectType')?.value() || [];
const duration = projectSection.radioButton('projectDuration')?.value();
const qualityRating = qualitySection.starRating('overallQuality')?.value();
const npsScore = overallSection.ratingScale('npsScore')?.value();
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
const feeling = overallSection.emojiRating('overallFeeling')?.value();
const beforeSat = comparisonSection.slider('satisfactionBefore')?.value();
const afterSat = comparisonSection.slider('satisfactionAfter')?.value();
if (npsScore === null || npsScore === undefined) return '';
const durationLabels: Record<string, string> = {
'under-week': '<1 week', '1-2-weeks': '1-2 weeks', '2-4-weeks': '2-4 weeks',
'1-2-months': '1-2 months', '2-6-months': '2-6 months', 'over-6-months': '6+ months'
};
const feelingEmojis: Record<string, string> = {
'very-bad': '😢', 'bad': '😕', 'neutral': '😐', 'good': '😊', 'excellent': '😍'
};
let summary = `Renovation Feedback Summary\n`;
summary += `${'═'.repeat(30)}\n\n`;
summary += `Project: ${projects.length} area(s) renovated\n`;
summary += `Duration: ${durationLabels[duration || ''] || 'N/A'}\n\n`;
if (qualityRating) {
summary += `Workmanship: ${'★'.repeat(qualityRating)}${'☆'.repeat(5 - qualityRating)}\n`;
}
if (beforeSat && afterSat) {
const improvement = afterSat - beforeSat;
summary += `Satisfaction: ${beforeSat}/10 → ${afterSat}/10 (${improvement > 0 ? '+' : ''}${improvement})\n`;
}
if (feeling) {
summary += `Overall feeling: ${feelingEmojis[feeling] || ''}\n`;
}
summary += `\nRecommendation: ${npsScore}/10 (${npsCategory?.charAt(0).toUpperCase()}${npsCategory?.slice(1)})`;
return summary;
},
customStyles: () => {
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (npsCategory === 'promoter') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #059669' };
} else if (npsCategory === 'passive') {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #d97706' };
} else if (npsCategory === 'detractor') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #dc2626' };
}
return { ...baseStyles, backgroundColor: '#f3f4f6', borderLeft: '4px solid #6b7280' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => overallSection.ratingScale('npsScore')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your detailed feedback helps us improve our services and deliver better renovation experiences. We appreciate you taking the time to share your thoughts.'
});
}