export function homeownerConstructionFeedback(form: FormTs) {
// Homeowner Construction Feedback Form
// Demonstrates: MatrixQuestion, StarRating, EmojiRating, ThumbRating, RatingScale, Slider, conditional visibility, dynamic styling
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Project Completion Feedback',
computedValue: () => 'Thank you for choosing us! Your feedback helps us deliver even better results for future homeowners.',
customStyles: {
background: 'linear-gradient(135deg, #ca8a04 0%, #eab308 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Project Information
// ============================================
const projectSection = form.addSubform('projectSection', {
title: 'Project Details',
customStyles: { backgroundColor: '#fefce8', padding: '16px', borderRadius: '8px' }
});
projectSection.addRow(row => {
row.addDropdown('projectType', {
label: 'Type of project',
options: [
{ id: 'new-build', name: 'New Home Construction' },
{ id: 'addition', name: 'Home Addition' },
{ id: 'kitchen', name: 'Kitchen Remodel' },
{ id: 'bathroom', name: 'Bathroom Remodel' },
{ id: 'basement', name: 'Basement Finishing' },
{ id: 'exterior', name: 'Exterior/Siding' },
{ id: 'roofing', name: 'Roofing' },
{ id: 'deck-patio', name: 'Deck/Patio' },
{ id: 'flooring', name: 'Flooring' },
{ id: 'painting', name: 'Painting' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select project type',
isRequired: true
}, '1fr');
row.addDropdown('projectSize', {
label: 'Project scope',
options: [
{ id: 'minor', name: 'Minor (Under $10,000)' },
{ id: 'medium', name: 'Medium ($10,000-$50,000)' },
{ id: 'major', name: 'Major ($50,000-$150,000)' },
{ id: 'large', name: 'Large ($150,000+)' }
],
placeholder: 'Select scope'
}, '1fr');
});
projectSection.addRow(row => {
row.addTextbox('projectAddress', {
label: 'Project address (City/State only for privacy)',
placeholder: 'e.g., Austin, TX'
}, '1fr');
row.addDatepicker('completionDate', {
label: 'Approximate completion date'
}, '1fr');
});
// ============================================
// SECTION 2: Workmanship Quality
// ============================================
const qualitySection = form.addSubform('qualitySection', {
title: 'Workmanship Quality'
});
qualitySection.addRow(row => {
row.addStarRating('overallQuality', {
label: 'Overall quality of workmanship',
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true
});
});
qualitySection.addRow(row => {
row.addMatrixQuestion('qualityMatrix', {
label: 'Please rate specific aspects of the work:',
rows: [
{ id: 'craftsmanship', label: 'Attention to detail and craftsmanship', isRequired: true },
{ id: 'materials', label: 'Quality of materials used' },
{ id: 'finish', label: 'Finish work and aesthetics' },
{ id: 'code', label: 'Code compliance and inspections' },
{ id: 'cleanup', label: 'Final cleanup and presentation' }
],
columns: [
{ id: '1', label: 'Poor' },
{ id: '2', label: 'Fair' },
{ id: '3', label: 'Good' },
{ id: '4', label: 'Very Good' },
{ id: '5', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
// ============================================
// SECTION 3: Communication & Project Management
// ============================================
const communicationSection = form.addSubform('communicationSection', {
title: 'Communication & Project Management'
});
communicationSection.addRow(row => {
row.addMatrixQuestion('commMatrix', {
label: 'Rate our communication and management:',
rows: [
{ id: 'responsiveness', label: 'Responsiveness to calls/messages', isRequired: true },
{ id: 'updates', label: 'Regular project updates' },
{ id: 'clarity', label: 'Clear explanation of process/costs' },
{ id: 'concerns', label: 'Addressed your concerns promptly' },
{ id: 'professionalism', label: 'Professional and courteous manner' }
],
columns: [
{ id: '1', label: 'Poor' },
{ id: '2', label: 'Fair' },
{ id: '3', label: 'Good' },
{ id: '4', label: 'Very Good' },
{ id: '5', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
communicationSection.addRow(row => {
row.addRatingScale('easeOfWorking', {
preset: 'likert-5',
label: 'Overall, it was easy to work with our team',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
// ============================================
// SECTION 4: Timeline & Budget
// ============================================
const timelineBudgetSection = form.addSubform('timelineBudgetSection', {
title: 'Timeline & Budget'
});
timelineBudgetSection.addRow(row => {
row.addRadioButton('timelineAccuracy', {
label: 'How did the actual timeline compare to the estimate?',
options: [
{ id: 'early', name: 'Finished early' },
{ id: 'on-time', name: 'Finished on schedule' },
{ id: 'slight-delay', name: 'Slightly delayed (1-2 weeks)' },
{ id: 'delayed', name: 'Significantly delayed' }
],
orientation: 'vertical',
isRequired: true
}, '1fr');
row.addRadioButton('budgetAccuracy', {
label: 'How did the final cost compare to the estimate?',
options: [
{ id: 'under', name: 'Under budget' },
{ id: 'on-budget', name: 'On budget' },
{ id: 'slight-over', name: 'Slightly over (1-10%)' },
{ id: 'over', name: 'Significantly over budget' }
],
orientation: 'vertical',
isRequired: true
}, '1fr');
});
timelineBudgetSection.addRow(row => {
row.addTextarea('delayBudgetExplanation', {
label: 'Please explain any timeline or budget concerns',
placeholder: 'Were delays or cost increases communicated clearly? Were they justified?',
rows: 3,
autoExpand: true,
isVisible: () => {
const timeline = timelineBudgetSection.radioButton('timelineAccuracy')?.value();
const budget = timelineBudgetSection.radioButton('budgetAccuracy')?.value();
return timeline === 'delayed' || budget === 'over';
}
});
});
// ============================================
// SECTION 5: Crew & Site Management
// ============================================
const crewSection = form.addSubform('crewSection', {
title: 'Crew & Site Management'
});
crewSection.addRow(row => {
row.addStarRating('crewProfessionalism', {
label: 'Crew professionalism and courtesy',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
row.addStarRating('siteCleanliness', {
label: 'Job site cleanliness during work',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
});
crewSection.addRow(row => {
row.addCheckboxList('crewPositives', {
label: 'What did our crew do well?',
options: [
{ id: 'punctual', name: 'Arrived on time' },
{ id: 'respectful', name: 'Respectful of your home' },
{ id: 'clean', name: 'Kept site clean daily' },
{ id: 'communicative', name: 'Good communicators' },
{ id: 'skilled', name: 'Highly skilled workers' },
{ id: 'problem-solving', name: 'Good problem-solving' }
],
orientation: 'vertical'
}, '1fr');
row.addCheckboxList('crewImprovements', {
label: 'Areas for improvement?',
options: [
{ id: 'timeliness', name: 'Arrive on time' },
{ id: 'cleanliness', name: 'Better cleanup' },
{ id: 'communication', name: 'More communication' },
{ id: 'noise', name: 'Noise management' },
{ id: 'parking', name: 'Vehicle/parking courtesy' },
{ id: 'none', name: 'No improvements needed' }
],
orientation: 'vertical'
}, '1fr');
});
// ============================================
// SECTION 6: Overall Satisfaction
// ============================================
const satisfactionSection = form.addSubform('satisfactionSection', {
title: 'Overall Satisfaction',
customStyles: () => {
const nps = satisfactionSection.ratingScale('npsScore')?.npsCategory();
if (nps === 'promoter') return { backgroundColor: '#dcfce7', padding: '16px', borderRadius: '8px' };
if (nps === 'passive') return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
if (nps === 'detractor') return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #cbd5e1' };
}
});
satisfactionSection.addRow(row => {
row.addEmojiRating('overallExperience', {
label: 'How would you describe your overall experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
satisfactionSection.addSpacer({ height: '16px' });
satisfactionSection.addRow(row => {
row.addRatingScale('npsScore', {
preset: 'nps',
label: 'How likely are you to recommend us to friends and family?',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true,
isRequired: true
});
});
satisfactionSection.addSpacer({ height: '16px' });
satisfactionSection.addRow(row => {
row.addThumbRating('hireAgain', {
label: 'Would you hire us again for future projects?',
showLabels: true,
upLabel: 'Definitely!',
downLabel: 'Probably not',
alignment: 'center',
size: 'lg'
});
});
// ============================================
// SECTION 7: Testimonial (Promoters)
// ============================================
const testimonialSection = form.addSubform('testimonialSection', {
title: 'Share Your Experience',
isVisible: () => {
const score = satisfactionSection.ratingScale('npsScore')?.value();
return score !== null && score !== undefined && score >= 8;
},
customStyles: { backgroundColor: '#dcfce7', padding: '16px', borderRadius: '8px' }
});
testimonialSection.addRow(row => {
row.addTextPanel('testimonialIntro', {
computedValue: () => "We're thrilled you had a great experience! Would you mind sharing a few words we could use as a testimonial?",
customStyles: {
backgroundColor: '#d1fae5',
padding: '12px',
borderRadius: '6px',
fontSize: '14px'
}
});
});
testimonialSection.addSpacer({ height: '12px' });
testimonialSection.addRow(row => {
row.addTextarea('testimonial', {
label: 'Your testimonial',
placeholder: 'What would you tell others about working with us?',
rows: 4,
autoExpand: true
});
});
testimonialSection.addRow(row => {
row.addCheckboxList('testimonialPermissions', {
label: 'May we use your feedback in:',
options: [
{ id: 'website', name: 'Our website' },
{ id: 'social', name: 'Social media' },
{ id: 'marketing', name: 'Marketing materials' },
{ id: 'google', name: 'Google reviews (we\'ll send a link)' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 8: Concerns (Detractors/Passives)
// ============================================
const concernsSection = form.addSubform('concernsSection', {
title: 'We Want to Make It Right',
isVisible: () => {
const score = satisfactionSection.ratingScale('npsScore')?.value();
return score !== null && score !== undefined && score <= 6;
},
customStyles: { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' }
});
concernsSection.addRow(row => {
row.addTextPanel('concernsIntro', {
computedValue: () => "We're sorry your experience didn't meet expectations. Please share your concerns so we can address them.",
customStyles: {
backgroundColor: '#fffbeb',
padding: '12px',
borderRadius: '6px',
fontSize: '14px',
borderLeft: '4px solid #f59e0b'
}
});
});
concernsSection.addSpacer({ height: '12px' });
concernsSection.addRow(row => {
row.addCheckboxList('concernAreas', {
label: 'What areas were disappointing?',
options: [
{ id: 'quality', name: 'Workmanship quality' },
{ id: 'communication', name: 'Communication' },
{ id: 'timeline', name: 'Timeline delays' },
{ id: 'budget', name: 'Budget overruns' },
{ id: 'cleanup', name: 'Site cleanliness' },
{ id: 'crew', name: 'Crew behavior' },
{ id: 'warranty', name: 'Warranty/follow-up' },
{ id: 'other', name: 'Other issues' }
],
orientation: 'vertical'
});
});
concernsSection.addRow(row => {
row.addTextarea('concernDetails', {
label: 'Please describe the issues in detail',
placeholder: 'The more detail you provide, the better we can address your concerns...',
rows: 4,
autoExpand: true,
isRequired: true
});
});
concernsSection.addRow(row => {
row.addCheckbox('requestCallback', {
label: 'I would like a manager to contact me about these concerns'
});
});
// ============================================
// SECTION 9: Contact Information
// ============================================
const contactSection = form.addSubform('contactSection', {
title: 'Your Information'
});
contactSection.addRow(row => {
row.addTextbox('homeownerName', {
label: 'Your name',
placeholder: 'First and last name',
isRequired: () => {
const perms = testimonialSection.checkboxList('testimonialPermissions')?.value() || [];
const callback = concernsSection.checkbox('requestCallback')?.value();
return perms.length > 0 || callback === true;
}
}, '1fr');
row.addEmail('homeownerEmail', {
label: 'Email',
placeholder: 'your@email.com',
isRequired: () => {
const callback = concernsSection.checkbox('requestCallback')?.value();
return callback === true;
}
}, '1fr');
});
contactSection.addRow(row => {
row.addTextbox('homeownerPhone', {
label: 'Phone (optional)',
placeholder: '(555) 123-4567',
isVisible: () => concernsSection.checkbox('requestCallback')?.value() === true
});
});
// ============================================
// SECTION 10: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => satisfactionSection.ratingScale('npsScore')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const npsScore = satisfactionSection.ratingScale('npsScore')?.value();
const npsCategory = satisfactionSection.ratingScale('npsScore')?.npsCategory();
const overallQuality = qualitySection.starRating('overallQuality')?.value();
const crewRating = crewSection.starRating('crewProfessionalism')?.value();
const mood = satisfactionSection.emojiRating('overallExperience')?.value();
const hireAgain = satisfactionSection.thumbRating('hireAgain')?.value();
const timeline = timelineBudgetSection.radioButton('timelineAccuracy')?.value();
const budget = timelineBudgetSection.radioButton('budgetAccuracy')?.value();
if (npsScore === null || npsScore === undefined) return '';
let emoji = '';
if (npsCategory === 'promoter') emoji = '🏠';
else if (npsCategory === 'passive') emoji = '🔧';
else emoji = '⚠️';
let summary = `${emoji} Project Feedback Summary\n`;
summary += `${'═'.repeat(28)}\n\n`;
summary += `📊 NPS Score: ${npsScore}/10 (${npsCategory?.charAt(0).toUpperCase()}${npsCategory?.slice(1)})\n`;
if (overallQuality) {
summary += `⭐ Quality: ${overallQuality}/5 stars\n`;
}
if (crewRating) {
summary += `👷 Crew: ${crewRating}/5 stars\n`;
}
if (mood) {
const moodLabels: Record<string, string> = {
'very-bad': '😢 Poor experience',
'bad': '😕 Below expectations',
'neutral': '😐 Average',
'good': '😊 Good experience',
'excellent': '😍 Excellent!'
};
summary += `\n${moodLabels[mood] || mood}\n`;
}
if (timeline) {
const timelineLabels: Record<string, string> = {
'early': '⏰ Finished early',
'on-time': '⏰ On schedule',
'slight-delay': '⏰ Slight delay',
'delayed': '⏰ Significantly delayed'
};
summary += `${timelineLabels[timeline] || timeline}\n`;
}
if (budget) {
const budgetLabels: Record<string, string> = {
'under': '💰 Under budget',
'on-budget': '💰 On budget',
'slight-over': '💰 Slightly over',
'over': '💰 Over budget'
};
summary += `${budgetLabels[budget] || budget}\n`;
}
if (hireAgain) {
summary += `\n${hireAgain === 'up' ? '👍 Would hire again' : '👎 Would not hire again'}`;
}
return summary;
},
customStyles: () => {
const category = satisfactionSection.ratingScale('npsScore')?.npsCategory();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (category === 'promoter') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (category === 'passive') {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
} else if (category === 'detractor') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return baseStyles;
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => satisfactionSection.ratingScale('npsScore')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'We truly appreciate you taking the time to share your experience. Your feedback helps us improve and serve our clients better.'
});
}