export function constructionCompletionSurvey(form: FormTs) {
// Construction Job Completion Review - Construction & Trades Feedback
// Demonstrates: Multi-page, StarRating x6+, MatrixQuestion, Slider, ThumbRating, CheckboxList, NPS, computed values
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Project Completion Review',
computedValue: () => 'Your feedback helps us deliver better results on every project. Thank you for choosing us!',
customStyles: {
background: 'linear-gradient(135deg, #b45309 0%, #d97706 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// MULTI-PAGE WIZARD
// ============================================
const pages = form.addPages('reviewPages', { heightMode: 'current-page' });
// ----------------------------------------
// PAGE 1: Project Information
// ----------------------------------------
const page1 = pages.addPage('projectInfo');
page1.addRow(row => {
row.addTextPanel('page1Title', {
label: 'Step 1 of 4: Project Details',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#b45309',
marginBottom: '16px'
}
});
});
page1.addRow(row => {
row.addDropdown('projectType', {
label: 'What type of project was completed?',
placeholder: 'Select project type...',
options: [
{ id: 'new-construction', name: 'New Construction / Build' },
{ id: 'renovation', name: 'Renovation / Remodel' },
{ id: 'addition', name: 'Home Addition' },
{ id: 'kitchen', name: 'Kitchen Remodel' },
{ id: 'bathroom', name: 'Bathroom Remodel' },
{ id: 'roofing', name: 'Roofing' },
{ id: 'siding', name: 'Siding / Exterior' },
{ id: 'deck-patio', name: 'Deck / Patio / Outdoor' },
{ id: 'flooring', name: 'Flooring' },
{ id: 'electrical', name: 'Electrical Work' },
{ id: 'plumbing', name: 'Plumbing' },
{ id: 'hvac', name: 'HVAC' },
{ id: 'painting', name: 'Painting' },
{ id: 'other', name: 'Other' }
],
isRequired: true
}, '1fr');
row.addDropdown('projectSize', {
label: 'Project size/budget range',
placeholder: 'Select range...',
options: [
{ id: 'small', name: 'Under $5,000' },
{ id: 'medium', name: '$5,000 - $25,000' },
{ id: 'large', name: '$25,000 - $100,000' },
{ id: 'major', name: '$100,000 - $500,000' },
{ id: 'custom', name: 'Over $500,000' }
]
}, '1fr');
});
page1.addRow(row => {
row.addTextbox('otherProjectType', {
label: 'Please describe the project',
placeholder: 'What work was done?',
isVisible: () => page1.dropdown('projectType')?.value() === 'other',
isRequired: () => page1.dropdown('projectType')?.value() === 'other'
});
});
page1.addRow(row => {
row.addDatepicker('completionDate', {
label: 'Project completion date',
maxDate: () => new Date().toISOString()
}, '1fr');
row.addTextbox('projectLocation', {
label: 'Project location (city/area)',
placeholder: 'e.g., Downtown, North Side'
}, '1fr');
});
page1.addSpacer({ height: '24px' });
page1.addRow(row => {
row.addButton('nextPage1', {
label: 'Continue to Quality Review →',
onClick: () => pages.goToPage('quality')
});
});
// ----------------------------------------
// PAGE 2: Quality & Workmanship
// ----------------------------------------
const page2 = pages.addPage('quality');
page2.addRow(row => {
row.addTextPanel('page2Title', {
label: 'Step 2 of 4: Quality & Workmanship',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#b45309',
marginBottom: '16px'
}
});
});
const qualitySection = page2.addSubform('qualitySection', {
customStyles: () => {
const workmanship = qualitySection.starRating('workmanship')?.value();
if (workmanship !== null && workmanship !== undefined) {
if (workmanship >= 4) return { backgroundColor: '#dcfce7', padding: '16px', borderRadius: '8px' };
if (workmanship >= 3) return { backgroundColor: '#fef9c3', padding: '16px', borderRadius: '8px' };
return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
}
return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
}
});
qualitySection.addRow(row => {
row.addStarRating('workmanship', {
label: 'Quality of workmanship',
maxStars: 5,
size: 'xl',
alignment: 'center'
});
});
qualitySection.addRow(row => {
row.addStarRating('attention', {
label: 'Attention to detail',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
row.addStarRating('materials', {
label: 'Quality of materials used',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
});
qualitySection.addRow(row => {
row.addStarRating('finishes', {
label: 'Finishes & finishing touches',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
row.addStarRating('functionality', {
label: 'Everything works as expected',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
});
page2.addSpacer({ height: '16px' });
page2.addRow(row => {
row.addThumbRating('meetsExpectations', {
label: 'Did the finished work meet or exceed your expectations?',
showLabels: true,
upLabel: 'Yes, met/exceeded',
downLabel: 'No, fell short',
size: 'lg',
alignment: 'center'
});
});
page2.addRow(row => {
row.addTextarea('expectationDetails', {
label: () => {
const meets = page2.thumbRating('meetsExpectations')?.value();
if (meets === 'up') return 'What impressed you most about the finished work?';
if (meets === 'down') return 'Where did the work fall short of your expectations?';
return 'Any comments about the finished work?';
},
placeholder: 'Share specific details...',
rows: 2,
autoExpand: true,
isVisible: () => page2.thumbRating('meetsExpectations')?.value() !== null
});
});
page2.addSpacer({ height: '24px' });
page2.addRow(row => {
row.addButton('backPage2', {
label: '← Back',
onClick: () => pages.goToPage('projectInfo')
}, '120px');
row.addEmpty('1fr');
row.addButton('nextPage2', {
label: 'Continue to Timeline & Budget →',
onClick: () => pages.goToPage('timeline')
}, '250px');
});
// ----------------------------------------
// PAGE 3: Timeline & Budget
// ----------------------------------------
const page3 = pages.addPage('timeline');
page3.addRow(row => {
row.addTextPanel('page3Title', {
label: 'Step 3 of 4: Timeline & Budget',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#b45309',
marginBottom: '16px'
}
});
});
const timelineSection = page3.addSubform('timelineSection', {
title: 'Project Timeline',
customStyles: { backgroundColor: '#f8fafc', padding: '16px', borderRadius: '8px' }
});
timelineSection.addRow(row => {
row.addRadioButton('timelineStatus', {
label: 'How did the project timeline compare to the original estimate?',
options: [
{ id: 'early', name: 'Finished early' },
{ id: 'on-time', name: 'On schedule' },
{ id: 'slightly-late', name: 'Slightly delayed (1-2 weeks)' },
{ id: 'late', name: 'Significantly delayed' }
],
orientation: 'vertical',
isRequired: true
});
});
timelineSection.addRow(row => {
row.addTextarea('timelineDetails', {
label: 'Comments about the timeline',
placeholder: () => {
const status = timelineSection.radioButton('timelineStatus')?.value();
if (status === 'late' || status === 'significantly-late') {
return 'What caused the delays? Were you kept informed?';
}
return 'Any comments about scheduling and timeline management...';
},
rows: 2,
autoExpand: true,
isVisible: () => {
const status = timelineSection.radioButton('timelineStatus')?.value();
return status === 'slightly-late' || status === 'late';
}
});
});
const budgetSection = page3.addSubform('budgetSection', {
title: 'Project Budget',
customStyles: { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' }
});
budgetSection.addRow(row => {
row.addRadioButton('budgetStatus', {
label: 'How did the final cost compare to the original quote?',
options: [
{ id: 'under', name: 'Under budget' },
{ id: 'on-budget', name: 'On budget' },
{ id: 'slightly-over', name: 'Slightly over (< 10%)' },
{ id: 'over', name: 'Significantly over budget' }
],
orientation: 'vertical',
isRequired: true
});
});
budgetSection.addRow(row => {
row.addThumbRating('changeOrderHandling', {
label: 'Were change orders and additional costs communicated clearly?',
showLabels: true,
upLabel: 'Yes, clearly communicated',
downLabel: 'No, surprises',
size: 'md',
alignment: 'center',
isVisible: () => {
const status = budgetSection.radioButton('budgetStatus')?.value();
return status === 'slightly-over' || status === 'over';
}
});
});
budgetSection.addRow(row => {
row.addStarRating('valueForMoney', {
label: 'Overall value for money',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
page3.addSpacer({ height: '24px' });
page3.addRow(row => {
row.addButton('backPage3', {
label: '← Back',
onClick: () => pages.goToPage('quality')
}, '120px');
row.addEmpty('1fr');
row.addButton('nextPage3', {
label: 'Continue to Team & Overall →',
onClick: () => pages.goToPage('overall')
}, '230px');
});
// ----------------------------------------
// PAGE 4: Team & Overall
// ----------------------------------------
const page4 = pages.addPage('overall');
page4.addRow(row => {
row.addTextPanel('page4Title', {
label: 'Step 4 of 4: Team & Overall Experience',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#b45309',
marginBottom: '16px'
}
});
});
const teamSection = page4.addSubform('teamSection', {
title: 'Crew & Communication',
customStyles: { backgroundColor: '#eff6ff', padding: '16px', borderRadius: '8px' }
});
teamSection.addRow(row => {
row.addMatrixQuestion('teamMatrix', {
label: 'Rate the project team:',
rows: [
{ id: 'professionalism', label: 'Crew professionalism', isRequired: true },
{ id: 'communication', label: 'Communication & updates' },
{ id: 'punctuality', label: 'Punctuality / Showing up on time' },
{ id: 'cleanup', label: 'Daily cleanup & site maintenance' },
{ id: 'respect', label: 'Respect for your home/property' }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'very-good', label: 'Very Good' },
{ id: 'excellent', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
teamSection.addRow(row => {
row.addStarRating('projectManager', {
label: 'Project manager / Main point of contact',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
row.addStarRating('problemSolving', {
label: 'Problem solving when issues arose',
maxStars: 5,
size: 'md',
alignment: 'center'
}, '1fr');
});
// Overall ratings
const overallSection = page4.addSubform('overallSection', {
title: 'Overall Experience',
customStyles: () => {
const overall = overallSection.starRating('overallSatisfaction')?.value();
if (overall !== null && overall !== undefined) {
if (overall >= 4) return { backgroundColor: '#dcfce7', padding: '16px', borderRadius: '8px' };
if (overall >= 3) return { backgroundColor: '#fef9c3', padding: '16px', borderRadius: '8px' };
return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
}
return { padding: '16px', borderRadius: '8px', border: '1px dashed #d4d4d8' };
}
});
overallSection.addRow(row => {
row.addStarRating('overallSatisfaction', {
label: 'Overall project satisfaction',
maxStars: 5,
size: 'xl',
alignment: 'center',
showConfettiOnMax: true
});
});
overallSection.addRow(row => {
row.addRatingScale('recommendScore', {
preset: 'nps',
label: 'How likely are you to recommend us to friends or family?',
showSegmentColors: true,
showCategoryLabel: true,
alignment: 'center'
});
});
overallSection.addSpacer({ height: '16px' });
overallSection.addRow(row => {
row.addCheckbox('wouldHireAgain', {
label: 'I would hire this company again for future projects'
});
});
overallSection.addRow(row => {
row.addCheckbox('canUseTestimonial', {
label: 'You may use my feedback as a testimonial for marketing purposes'
});
});
overallSection.addSpacer({ height: '12px' });
overallSection.addRow(row => {
row.addTextarea('testimonial', {
label: 'Share your experience (for testimonial)',
placeholder: 'Tell others about your experience working with us...',
rows: 3,
autoExpand: true,
isVisible: () => overallSection.checkbox('canUseTestimonial')?.value() === true
});
});
overallSection.addRow(row => {
row.addTextbox('clientName', {
label: 'Your name (for testimonial)',
placeholder: 'How would you like to be credited?',
isVisible: () => overallSection.checkbox('canUseTestimonial')?.value() === true
}, '1fr');
row.addTextbox('clientLocation', {
label: 'City/Neighborhood',
placeholder: 'e.g., John D., Riverside',
isVisible: () => overallSection.checkbox('canUseTestimonial')?.value() === true
}, '1fr');
});
page4.addSpacer({ height: '24px' });
page4.addRow(row => {
row.addButton('backPage4', {
label: '← Back',
onClick: () => pages.goToPage('timeline')
}, '120px');
});
// ============================================
// SUMMARY SECTION
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Review Summary',
isVisible: () => overallSection.starRating('overallSatisfaction')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const projectType = page1.dropdown('projectType')?.value();
const workmanship = qualitySection.starRating('workmanship')?.value();
const timeline = timelineSection.radioButton('timelineStatus')?.value();
const budget = budgetSection.radioButton('budgetStatus')?.value();
const value = budgetSection.starRating('valueForMoney')?.value();
const overall = overallSection.starRating('overallSatisfaction')?.value();
const nps = overallSection.ratingScale('recommendScore')?.value();
const npsCategory = overallSection.ratingScale('recommendScore')?.npsCategory();
const wouldHire = overallSection.checkbox('wouldHireAgain')?.value();
if (!overall) return '';
const projectLabels: Record<string, string> = {
'new-construction': 'New Construction',
'renovation': 'Renovation',
'addition': 'Home Addition',
'kitchen': 'Kitchen Remodel',
'bathroom': 'Bathroom Remodel',
'roofing': 'Roofing',
'siding': 'Siding/Exterior',
'deck-patio': 'Deck/Patio',
'flooring': 'Flooring',
'electrical': 'Electrical',
'plumbing': 'Plumbing',
'hvac': 'HVAC',
'painting': 'Painting',
'other': 'Other'
};
const timelineLabels: Record<string, string> = {
'early': '✅ Early',
'on-time': '✅ On Time',
'slightly-late': '⚠️ Slightly Delayed',
'late': '❌ Delayed'
};
const budgetLabels: Record<string, string> = {
'under': '✅ Under Budget',
'on-budget': '✅ On Budget',
'slightly-over': '⚠️ Slightly Over',
'over': '❌ Over Budget'
};
let summary = '🏗️ Project Review Summary\n';
summary += `${'═'.repeat(28)}\n\n`;
summary += `📋 Project: ${projectLabels[projectType || ''] || 'Not specified'}\n`;
if (workmanship) {
summary += `\n🔨 Workmanship: ${workmanship}/5 stars`;
}
if (timeline) {
summary += `\n⏱️ Timeline: ${timelineLabels[timeline]}`;
}
if (budget) {
summary += `\n💰 Budget: ${budgetLabels[budget]}`;
}
if (value) {
summary += `\n💵 Value for Money: ${value}/5 stars`;
}
summary += `\n\n⭐ Overall: ${overall}/5 stars`;
if (nps !== null && nps !== undefined) {
const npsEmoji = npsCategory === 'promoter' ? '💚' : npsCategory === 'passive' ? '💛' : '🔴';
summary += `\n${npsEmoji} NPS: ${nps}/10 (${npsCategory?.charAt(0).toUpperCase()}${npsCategory?.slice(1)})`;
}
if (wouldHire) {
summary += '\n\n✅ Would hire again';
}
return summary;
},
customStyles: () => {
const overall = overallSection.starRating('overallSatisfaction')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (overall && overall >= 4) {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #d97706' };
} else if (overall && overall >= 3) {
return { ...baseStyles, backgroundColor: '#fef9c3', borderLeft: '4px solid #eab308' };
} else if (overall) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return baseStyles;
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Review',
isVisible: () => overallSection.starRating('overallSatisfaction')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Review!',
message: 'We appreciate you taking the time to share your feedback. Your input helps us deliver better results and serve our clients more effectively. If you have any follow-up concerns, please don\'t hesitate to contact us.'
});
}