export function installationFeedbackForm(form: FormTs) {
// Installation Experience Survey
// Demonstrates: StarRating, RatingScale, MatrixQuestion, EmojiRating,
// ThumbRating, Datepicker, Timepicker, Dropdown, conditional visibility
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Installation Feedback',
computedValue: () => 'Help us maintain our service excellence. Your feedback improves our technicians.',
customStyles: {
backgroundColor: '#0891b2',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center',
fontSize: '15px'
}
});
});
// ============================================
// SECTION 1: Service Details
// ============================================
const detailsSection = form.addSubform('details', {
title: 'Service Details',
customStyles: { backgroundColor: '#f0fdfa', padding: '20px', borderRadius: '10px' }
});
detailsSection.addRow(row => {
row.addDropdown('serviceType', {
label: 'Type of service performed',
options: [
{ id: 'new-install', name: 'New Installation' },
{ id: 'upgrade', name: 'Equipment Upgrade' },
{ id: 'repair', name: 'Repair Service' },
{ id: 'maintenance', name: 'Scheduled Maintenance' },
{ id: 'inspection', name: 'Inspection' },
{ id: 'other', name: 'Other Service' }
],
placeholder: 'Select service type...',
isRequired: true
}, '1fr');
row.addTextbox('technicianName', {
label: 'Technician name (if known)',
placeholder: 'Enter technician name...'
}, '1fr');
});
detailsSection.addRow(row => {
row.addDatepicker('serviceDate', {
label: 'Date of service',
maxDate: () => new Date().toISOString(),
isRequired: true
}, '1fr');
row.addTimepicker('arrivalTime', {
label: 'Approximate arrival time'
}, '1fr');
});
// ============================================
// SECTION 2: Scheduling & Punctuality
// ============================================
const schedulingSection = form.addSubform('scheduling', {
title: 'Scheduling & Punctuality',
isVisible: () => detailsSection.dropdown('serviceType')?.value() !== null
});
schedulingSection.addRow(row => {
row.addStarRating('schedulingEase', {
label: 'How easy was it to schedule the appointment?',
maxStars: 5,
size: 'lg',
alignment: 'center',
filledColor: '#0891b2'
});
});
schedulingSection.addRow(row => {
row.addRadioButton('arrivedOnTime', {
label: 'Did the technician arrive within the scheduled window?',
options: [
{ id: 'early', name: 'Arrived early' },
{ id: 'on-time', name: 'On time' },
{ id: 'late-15', name: 'Up to 15 min late' },
{ id: 'late-30', name: '15-30 min late' },
{ id: 'late-60', name: 'More than 30 min late' },
{ id: 'no-show', name: 'Did not arrive / Had to reschedule' }
],
orientation: 'vertical'
});
});
// Late arrival follow-up
schedulingSection.addRow(row => {
row.addCheckbox('wasNotified', {
label: 'Were you notified about the delay?',
isVisible: () => {
const arrival = schedulingSection.radioButton('arrivedOnTime')?.value();
return arrival === 'late-30' || arrival === 'late-60' || arrival === 'no-show';
}
});
});
// ============================================
// SECTION 3: Technician Rating Matrix
// ============================================
const technicianSection = form.addSubform('technician', {
title: 'Technician Evaluation',
isVisible: () => detailsSection.dropdown('serviceType')?.value() !== null,
customStyles: { backgroundColor: '#ecfeff', padding: '20px', borderRadius: '10px' }
});
technicianSection.addRow(row => {
row.addMatrixQuestion('technicianMatrix', {
label: 'Rate the technician on the following aspects:',
rows: [
{ id: 'professional', label: 'Professional appearance', isRequired: true },
{ id: 'courteous', label: 'Courtesy and friendliness', isRequired: true },
{ id: 'knowledgeable', label: 'Technical knowledge', isRequired: true },
{ id: 'explained', label: 'Explained the work clearly', isRequired: true },
{ id: 'efficient', label: 'Worked efficiently', isRequired: true },
{ id: 'respectful', label: 'Respectful of your property', isRequired: true }
],
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 4: Work Quality
// ============================================
const qualitySection = form.addSubform('quality', {
title: 'Installation Quality',
isVisible: () => detailsSection.dropdown('serviceType')?.value() !== null
});
qualitySection.addRow(row => {
row.addStarRating('workQuality', {
label: 'Overall quality of the installation work',
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true,
filledColor: '#10b981'
}, '1fr');
row.addStarRating('siteCleanup', {
label: 'Cleanliness of the work area after completion',
maxStars: 5,
size: 'lg',
alignment: 'center',
filledColor: '#10b981'
}, '1fr');
});
qualitySection.addSpacer({ height: '16px' });
qualitySection.addRow(row => {
row.addThumbRating('equipmentWorking', {
label: 'Is the installed equipment working properly?',
showLabels: true,
upLabel: 'Yes, working great',
downLabel: 'No, having issues',
alignment: 'center',
size: 'lg'
});
});
// Issues follow-up
qualitySection.addSpacer({
height: '16px',
isVisible: () => qualitySection.thumbRating('equipmentWorking')?.value() === 'down'
});
qualitySection.addRow(row => {
row.addTextarea('issueDescription', {
label: 'Please describe the issue you are experiencing',
placeholder: 'Describe what is not working correctly...',
rows: 3,
isRequired: () => qualitySection.thumbRating('equipmentWorking')?.value() === 'down',
isVisible: () => qualitySection.thumbRating('equipmentWorking')?.value() === 'down'
});
});
qualitySection.addRow(row => {
row.addCheckbox('needsCallback', {
label: 'I would like a callback to resolve this issue',
isVisible: () => qualitySection.thumbRating('equipmentWorking')?.value() === 'down'
});
});
// ============================================
// SECTION 5: Overall Experience
// ============================================
const overallSection = form.addSubform('overall', {
title: 'Overall Experience',
isVisible: () => detailsSection.dropdown('serviceType')?.value() !== null,
customStyles: () => {
const rating = overallSection.starRating('overallRating')?.value();
if (rating !== null && rating !== undefined) {
if (rating >= 5) return { backgroundColor: '#d1fae5', padding: '20px', borderRadius: '10px' };
if (rating >= 4) return { backgroundColor: '#ecfdf5', padding: '20px', borderRadius: '10px' };
if (rating >= 3) return { backgroundColor: '#fffbeb', padding: '20px', borderRadius: '10px' };
return { backgroundColor: '#fef2f2', padding: '20px', borderRadius: '10px' };
}
return { padding: '20px', borderRadius: '10px', border: '1px solid #e5e7eb' };
}
});
overallSection.addRow(row => {
row.addStarRating('overallRating', {
label: 'How would you rate your overall installation experience?',
maxStars: 5,
size: 'xl',
alignment: 'center',
showConfettiOnMax: true,
filledColor: '#0891b2'
});
});
overallSection.addRow(row => {
row.addEmojiRating('emotionAfter', {
label: 'How do you feel about the completed installation?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
});
// NPS for installation
overallSection.addSpacer({ height: '16px' });
overallSection.addRow(row => {
row.addRatingScale('npsScore', {
label: 'How likely are you to recommend our installation services?',
preset: 'nps',
showCategoryLabel: true,
showSegmentColors: true,
alignment: 'center',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
});
// ============================================
// SECTION 6: Feedback & Suggestions
// ============================================
const feedbackSection = form.addSubform('feedback', {
title: 'Additional Feedback',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
feedbackSection.addRow(row => {
row.addSuggestionChips('whatWentWell', {
label: 'What went particularly well?',
suggestions: [
{ id: 'punctual', name: 'On-time arrival' },
{ id: 'professional', name: 'Professional technician' },
{ id: 'clean', name: 'Clean work area' },
{ id: 'thorough', name: 'Thorough work' },
{ id: 'fast', name: 'Quick completion' },
{ id: 'explained', name: 'Good explanations' }
],
alignment: 'left'
}, '1fr');
row.addSuggestionChips('whatToImprove', {
label: 'What could we improve?',
suggestions: [
{ id: 'scheduling', name: 'Scheduling process' },
{ id: 'communication', name: 'Communication' },
{ id: 'punctuality', name: 'Punctuality' },
{ id: 'cleanup', name: 'Cleanup' },
{ id: 'pricing', name: 'Pricing clarity' },
{ id: 'followup', name: 'Follow-up support' }
],
alignment: 'left'
}, '1fr');
});
feedbackSection.addSpacer({ height: '16px' });
feedbackSection.addRow(row => {
row.addTextarea('additionalComments', {
label: () => {
const rating = overallSection.starRating('overallRating')?.value();
if (rating !== null && rating !== undefined && rating <= 2) {
return 'Please tell us what went wrong so we can make it right';
}
if (rating !== null && rating !== undefined && rating >= 5) {
return 'Anything else you\'d like to share about your great experience?';
}
return 'Any additional comments or suggestions?';
},
placeholder: 'Your feedback helps us improve...',
rows: 4,
autoExpand: true
});
});
// ============================================
// SECTION 7: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const serviceType = detailsSection.dropdown('serviceType')?.value();
const technician = detailsSection.textbox('technicianName')?.value();
const scheduling = schedulingSection.starRating('schedulingEase')?.value();
const arrival = schedulingSection.radioButton('arrivedOnTime')?.value();
const workQuality = qualitySection.starRating('workQuality')?.value();
const cleanup = qualitySection.starRating('siteCleanup')?.value();
const equipmentOk = qualitySection.thumbRating('equipmentWorking')?.value();
const overall = overallSection.starRating('overallRating')?.value();
const nps = overallSection.ratingScale('npsScore')?.value();
if (overall === null || overall === undefined) return '';
const serviceLabels: Record<string, string> = {
'new-install': 'New Installation',
'upgrade': 'Equipment Upgrade',
'repair': 'Repair Service',
'maintenance': 'Scheduled Maintenance',
'inspection': 'Inspection',
'other': 'Other Service'
};
const arrivalLabels: Record<string, string> = {
'early': 'Early', 'on-time': 'On time', 'late-15': 'Slightly late',
'late-30': 'Late', 'late-60': 'Very late', 'no-show': 'Rescheduled'
};
let summary = '๐ง Installation Feedback Summary\n';
summary += `${'โ'.repeat(35)}\n\n`;
// Service info
if (serviceType) {
summary += `๐ Service: ${serviceLabels[serviceType] || serviceType}\n`;
}
if (technician) {
summary += `๐ท Technician: ${technician}\n`;
}
summary += '\n๐ Ratings:\n';
summary += `${'โ'.repeat(25)}\n`;
// Ratings
if (scheduling !== null && scheduling !== undefined) {
summary += `๐
Scheduling: ${'โ
'.repeat(scheduling)}${'โ'.repeat(5 - scheduling)}\n`;
}
if (arrival) {
const arrivalEmoji = arrival === 'on-time' || arrival === 'early' ? 'โ
' : 'โฐ';
summary += `${arrivalEmoji} Arrival: ${arrivalLabels[arrival] || arrival}\n`;
}
if (workQuality !== null && workQuality !== undefined) {
summary += `๐จ Work Quality: ${'โ
'.repeat(workQuality)}${'โ'.repeat(5 - workQuality)}\n`;
}
if (cleanup !== null && cleanup !== undefined) {
summary += `๐งน Site Cleanup: ${'โ
'.repeat(cleanup)}${'โ'.repeat(5 - cleanup)}\n`;
}
if (equipmentOk !== null) {
const equipEmoji = equipmentOk === 'up' ? 'โ
' : 'โ ๏ธ';
summary += `${equipEmoji} Equipment: ${equipmentOk === 'up' ? 'Working properly' : 'Has issues'}\n`;
}
// Overall
summary += `\nโญ Overall: ${'โ
'.repeat(overall)}${'โ'.repeat(5 - overall)} (${overall}/5)\n`;
// NPS
if (nps !== null && nps !== undefined) {
const category = overallSection.ratingScale('npsScore')?.npsCategory();
const emoji = category === 'promoter' ? '๐' : category === 'passive' ? '๐ค' : '๐';
summary += `${emoji} NPS: ${nps}/10 (${category})`;
}
return summary;
},
customStyles: () => {
const overall = overallSection.starRating('overallRating')?.value();
const base = {
padding: '20px',
borderRadius: '10px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (overall !== null && overall !== undefined) {
if (overall >= 4) return { ...base, backgroundColor: '#ecfdf5', borderLeft: '4px solid #10b981' };
if (overall >= 3) return { ...base, backgroundColor: '#fffbeb', borderLeft: '4px solid #f59e0b' };
return { ...base, backgroundColor: '#fef2f2', borderLeft: '4px solid #ef4444' };
}
return { ...base, backgroundColor: '#f8fafc' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: () => {
const overall = overallSection.starRating('overallRating')?.value();
if (overall !== null && overall !== undefined && overall >= 5) return 'Submit Feedback โญ';
return 'Submit Feedback';
},
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank you for your feedback!',
message: 'Your input helps us maintain high service standards. If you reported any issues, our team will follow up with you shortly.'
});
}