export function automotiveServiceSurvey(form: FormTs) {
// Auto Service Feedback Form
// Demonstrates: MatrixQuestion, StarRating x5, ThumbRating, RadioButton, Slider, EmojiRating, CheckboxList
// Advanced: Conditional visibility, Dynamic labels, Value/pricing analysis, Multi-page wizard
// ============================================
// MULTI-PAGE WIZARD STRUCTURE
// ============================================
const pages = form.addPages('servicePages', { heightMode: 'tallest-page' });
// ============================================
// PAGE 1: Service Overview
// ============================================
const page1 = pages.addPage('overview');
page1.addRow(row => {
row.addTextPanel('header', {
label: 'Auto Service Feedback',
computedValue: () => 'Thank you for choosing us. Your feedback helps us serve you better.',
customStyles: {
backgroundColor: '#1e3a5f',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
const serviceTypeSection = page1.addSubform('serviceType', {
title: 'Service Details'
});
serviceTypeSection.addRow(row => {
row.addRadioButton('serviceType', {
label: 'What type of service did you receive?',
options: [
{ id: 'maintenance', name: 'Routine maintenance (oil change, filters, etc.)' },
{ id: 'repair', name: 'Repair work' },
{ id: 'diagnostic', name: 'Diagnostic/inspection' },
{ id: 'recall', name: 'Recall service' },
{ id: 'warranty', name: 'Warranty repair' },
{ id: 'tires', name: 'Tires/wheels' },
{ id: 'bodywork', name: 'Body/collision work' },
{ id: 'multiple', name: 'Multiple services' }
],
orientation: 'vertical'
});
});
serviceTypeSection.addSpacer({ height: '16px' });
serviceTypeSection.addRow(row => {
row.addRadioButton('appointmentType', {
label: 'How did you schedule your service?',
options: [
{ id: 'online', name: 'Online booking' },
{ id: 'phone', name: 'Phone call' },
{ id: 'walkin', name: 'Walk-in' },
{ id: 'other', name: 'Other' }
],
orientation: 'horizontal'
});
});
// ============================================
// PAGE 2: Service Quality
// ============================================
const page2 = pages.addPage('quality');
page2.addRow(row => {
row.addTextPanel('qualityHeader', {
label: 'Service Quality',
customStyles: {
backgroundColor: '#f1f5f9',
padding: '16px',
borderRadius: '8px',
textAlign: 'center',
fontWeight: 'bold'
}
});
});
const qualitySection = page2.addSubform('qualitySection', {
title: 'Repair/Service Quality'
});
qualitySection.addRow(row => {
row.addThumbRating('problemResolved', {
label: 'Was the issue fully resolved / service completed properly?',
showLabels: true,
upLabel: 'Yes, completely',
downLabel: 'No, issues remain',
size: 'lg',
alignment: 'center'
});
});
qualitySection.addRow(row => {
row.addTextarea('unresolvedDetails', {
label: 'What issues remain unresolved?',
placeholder: 'Please describe what is still not working...',
rows: 3,
autoExpand: true,
isVisible: () => qualitySection.thumbRating('problemResolved')?.value() === 'down',
isRequired: () => qualitySection.thumbRating('problemResolved')?.value() === 'down'
});
});
qualitySection.addSpacer({ height: '16px' });
qualitySection.addRow(row => {
row.addStarRating('workQuality', {
label: 'Quality of Work',
tooltip: 'How well was the service/repair performed?',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
row.addStarRating('completeness', {
label: 'Completeness',
tooltip: 'Was everything requested addressed?',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
});
qualitySection.addRow(row => {
row.addRadioButton('vehicleCondition', {
label: 'How was your vehicle returned?',
options: [
{ id: 'spotless', name: 'Spotless - better than I left it' },
{ id: 'clean', name: 'Clean - as expected' },
{ id: 'same', name: 'Same condition as I left it' },
{ id: 'dirty', name: 'Dirty - grease marks or mess' }
],
orientation: 'vertical'
});
});
// ============================================
// PAGE 3: Staff & Communication
// ============================================
const page3 = pages.addPage('staff');
page3.addRow(row => {
row.addTextPanel('staffHeader', {
label: 'Staff & Communication',
customStyles: {
backgroundColor: '#f1f5f9',
padding: '16px',
borderRadius: '8px',
textAlign: 'center',
fontWeight: 'bold'
}
});
});
const staffSection = page3.addSubform('staffSection', {
title: 'Service Advisor Performance'
});
staffSection.addRow(row => {
row.addStarRating('advisorKnowledge', {
label: 'Knowledge & Expertise',
tooltip: 'Understanding of your vehicle and issues',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
row.addStarRating('advisorHonesty', {
label: 'Honesty & Transparency',
tooltip: 'Straightforward about what was needed',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
});
staffSection.addRow(row => {
row.addStarRating('advisorCommunication', {
label: 'Communication',
tooltip: 'Kept you informed about progress',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
row.addStarRating('advisorCourtesy', {
label: 'Courtesy & Respect',
tooltip: 'Professional and respectful treatment',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
});
staffSection.addSpacer({ height: '16px' });
staffSection.addRow(row => {
row.addMatrixQuestion('communicationMatrix', {
label: 'How well did we communicate about:',
rows: [
{ id: 'diagnosis', label: 'The diagnosis/issue found' },
{ id: 'workNeeded', label: 'Work that needed to be done' },
{ id: 'timeline', label: 'Expected completion time' },
{ id: 'costs', label: 'Costs before proceeding' },
{ id: 'completion', label: 'When vehicle was ready' }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' }
],
selectionMode: 'single',
striped: true,
fullWidth: true
});
});
// ============================================
// PAGE 4: Pricing & Value
// ============================================
const page4 = pages.addPage('pricing');
page4.addRow(row => {
row.addTextPanel('pricingHeader', {
label: 'Pricing & Value',
customStyles: {
backgroundColor: '#f1f5f9',
padding: '16px',
borderRadius: '8px',
textAlign: 'center',
fontWeight: 'bold'
}
});
});
const pricingSection = page4.addSubform('pricingSection', {
title: 'Price Transparency'
});
pricingSection.addRow(row => {
row.addRadioButton('priceVsEstimate', {
label: 'How did the final price compare to the estimate?',
options: [
{ id: 'lower', name: 'Lower than estimated' },
{ id: 'same', name: 'Same as estimated' },
{ id: 'slightly-higher', name: 'Slightly higher (explained)' },
{ id: 'higher', name: 'Higher than estimated (surprised)' },
{ id: 'no-estimate', name: 'No estimate was given' }
],
orientation: 'vertical'
});
});
pricingSection.addSpacer({ height: '16px' });
pricingSection.addRow(row => {
row.addSlider('valueForMoney', {
label: 'Rate the value for money (1 = Poor value, 10 = Excellent value)',
min: 1,
max: 10,
step: 1,
showValue: true,
defaultValue: 5
});
});
pricingSection.addRow(row => {
row.addTextPanel('valueIndicator', {
computedValue: () => {
const value = pricingSection.slider('valueForMoney')?.value();
if (value == null) return '';
if (value <= 3) return 'You feel the price was too high for the service received';
if (value <= 5) return 'You feel the price was about fair';
if (value <= 7) return 'You feel you received good value';
return 'You feel you received excellent value!';
},
customStyles: () => {
const value = pricingSection.slider('valueForMoney')?.value();
const baseStyles = { fontSize: '13px', textAlign: 'center', padding: '8px' };
if (value != null && value <= 3) return { ...baseStyles, color: '#dc2626' };
if (value != null && value >= 8) return { ...baseStyles, color: '#059669' };
return { ...baseStyles, color: '#64748b' };
}
});
});
pricingSection.addSpacer({ height: '16px' });
pricingSection.addRow(row => {
row.addRadioButton('additionalRecommendations', {
label: 'Were additional services recommended?',
options: [
{ id: 'no', name: 'No additional recommendations' },
{ id: 'yes-helpful', name: 'Yes, and they were helpful/legitimate' },
{ id: 'yes-pushy', name: 'Yes, but felt pressured' },
{ id: 'yes-unnecessary', name: 'Yes, seemed unnecessary' }
],
orientation: 'vertical'
});
});
// ============================================
// PAGE 5: Facility & Convenience
// ============================================
const page5 = pages.addPage('facility');
page5.addRow(row => {
row.addTextPanel('facilityHeader', {
label: 'Facility & Convenience',
customStyles: {
backgroundColor: '#f1f5f9',
padding: '16px',
borderRadius: '8px',
textAlign: 'center',
fontWeight: 'bold'
}
});
});
const facilitySection = page5.addSubform('facilitySection', {
title: 'Facility Experience'
});
facilitySection.addRow(row => {
row.addRadioButton('waitedOnSite', {
label: 'Did you wait on-site for your service?',
options: [
{ id: 'yes', name: 'Yes' },
{ id: 'no', name: 'No, left and returned' }
],
orientation: 'horizontal'
});
});
// Waiting area ratings - only if waited
const waitingSection = facilitySection.addSubform('waitingArea', {
isVisible: () => facilitySection.radioButton('waitedOnSite')?.value() === 'yes',
customStyles: { backgroundColor: '#f8fafc', padding: '16px', borderRadius: '8px', marginTop: '16px' }
});
waitingSection.addRow(row => {
row.addStarRating('waitingComfort', {
label: 'Waiting Area Comfort',
tooltip: 'Seating, space, amenities',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
row.addStarRating('waitingCleanliness', {
label: 'Cleanliness',
tooltip: 'Overall cleanliness of facility',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'left'
}, '1fr');
});
waitingSection.addRow(row => {
row.addCheckboxList('waitingAmenities', {
label: 'Which amenities did you use/appreciate?',
options: [
{ id: 'wifi', name: 'WiFi' },
{ id: 'coffee', name: 'Coffee/beverages' },
{ id: 'tv', name: 'TV' },
{ id: 'magazines', name: 'Magazines/reading' },
{ id: 'workspace', name: 'Work area' },
{ id: 'chargers', name: 'Phone chargers' },
{ id: 'none', name: 'Did not use any' }
],
orientation: 'vertical'
});
});
facilitySection.addSpacer({ height: '16px' });
facilitySection.addRow(row => {
row.addRadioButton('timingAccuracy', {
label: 'Was your vehicle ready when promised?',
options: [
{ id: 'early', name: 'Yes, even earlier' },
{ id: 'ontime', name: 'Yes, on time' },
{ id: 'late-small', name: 'Slightly late (within 30 min)' },
{ id: 'late', name: 'Significantly late' },
{ id: 'no-time', name: 'No time was given' }
],
orientation: 'vertical'
});
});
// ============================================
// PAGE 6: Overall & Recommendations
// ============================================
const page6 = pages.addPage('overall');
page6.addRow(row => {
row.addTextPanel('overallHeader', {
label: 'Overall Experience',
customStyles: {
backgroundColor: '#f1f5f9',
padding: '16px',
borderRadius: '8px',
textAlign: 'center',
fontWeight: 'bold'
}
});
});
const overallSection = page6.addSubform('overallSection', {
title: 'Your Overall Rating'
});
overallSection.addRow(row => {
row.addEmojiRating('overallExperience', {
label: 'How was your overall service experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addSpacer({ height: '16px' });
overallSection.addRow(row => {
row.addRatingScale('recommendShop', {
preset: 'nps',
label: 'How likely are you to recommend us to friends and family?',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true,
size: 'sm',
alignment: 'center'
});
});
overallSection.addSpacer({ height: '16px' });
overallSection.addRow(row => {
row.addRadioButton('willReturn', {
label: 'Will you return for future service?',
options: [
{ id: 'definitely', name: 'Definitely' },
{ id: 'probably', name: 'Probably' },
{ id: 'unsure', name: 'Unsure' },
{ id: 'unlikely', name: 'Unlikely' },
{ id: 'never', name: 'Will not return' }
],
orientation: 'vertical'
});
});
// Comments section
const commentsSection = page6.addSubform('commentsSection', {
title: 'Additional Comments'
});
commentsSection.addRow(row => {
row.addTextarea('additionalComments', {
label: () => {
const nps = overallSection.ratingScale('recommendShop')?.value();
if (nps != null && nps <= 6) {
return "We're sorry your experience wasn't great. What could we have done better?";
}
if (nps != null && nps >= 9) {
return "We're glad you had a great experience! Anything else you'd like to share?";
}
return 'Any additional comments or suggestions?';
},
placeholder: 'Share your thoughts...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SUMMARY SECTION
// ============================================
const summarySection = page6.addSubform('summarySection', {
title: 'Feedback Summary',
isVisible: () => overallSection.emojiRating('overallExperience')?.value() != null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const serviceType = serviceTypeSection.radioButton('serviceType')?.value();
const problemResolved = qualitySection.thumbRating('problemResolved')?.value();
const workQuality = qualitySection.starRating('workQuality')?.value();
const completeness = qualitySection.starRating('completeness')?.value();
const advisorKnowledge = staffSection.starRating('advisorKnowledge')?.value();
const advisorHonesty = staffSection.starRating('advisorHonesty')?.value();
const advisorCommunication = staffSection.starRating('advisorCommunication')?.value();
const advisorCourtesy = staffSection.starRating('advisorCourtesy')?.value();
const valueForMoney = pricingSection.slider('valueForMoney')?.value();
const priceVsEstimate = pricingSection.radioButton('priceVsEstimate')?.value();
const experience = overallSection.emojiRating('overallExperience')?.value();
const nps = overallSection.ratingScale('recommendShop')?.value();
const willReturn = overallSection.radioButton('willReturn')?.value();
if (!experience) return '';
let emoji = '🚗';
if (experience === 'excellent' || experience === 'good') emoji = '✅';
else if (experience === 'bad' || experience === 'very-bad') emoji = '⚠️';
let summary = `${emoji} Auto Service Feedback\n`;
summary += `${'═'.repeat(28)}\n\n`;
const serviceLabels: Record<string, string> = {
'maintenance': 'Maintenance',
'repair': 'Repair',
'diagnostic': 'Diagnostic',
'recall': 'Recall',
'warranty': 'Warranty',
'tires': 'Tires',
'bodywork': 'Body Work',
'multiple': 'Multiple Services'
};
if (serviceType) summary += `🔧 Service: ${serviceLabels[serviceType] || serviceType}\n`;
summary += `📋 Resolved: ${problemResolved === 'up' ? 'Yes' : problemResolved === 'down' ? 'No' : '-'}\n`;
// Quality scores
if (workQuality || completeness) {
const qualityScores = [workQuality, completeness].filter(s => s != null);
const avgQuality = (qualityScores.reduce((a, b) => a + (b ?? 0), 0) / qualityScores.length).toFixed(1);
summary += `\n⭐ Work Quality: ${avgQuality}/5`;
}
// Staff scores
const staffScores = [advisorKnowledge, advisorHonesty, advisorCommunication, advisorCourtesy].filter(s => s != null);
if (staffScores.length > 0) {
const avgStaff = (staffScores.reduce((a, b) => a + (b ?? 0), 0) / staffScores.length).toFixed(1);
summary += `\n👤 Advisor: ${avgStaff}/5`;
}
// Pricing
if (valueForMoney != null) {
summary += `\n💰 Value: ${valueForMoney}/10`;
}
if (priceVsEstimate) {
const priceLabels: Record<string, string> = {
'lower': 'Lower',
'same': 'As Estimated',
'slightly-higher': 'Slightly Higher',
'higher': 'Higher (Surprised)',
'no-estimate': 'No Estimate'
};
summary += `\n📊 vs Estimate: ${priceLabels[priceVsEstimate] || priceVsEstimate}`;
}
// Overall
const expLabels: Record<string, string> = {
'very-bad': 'Very Poor',
'bad': 'Poor',
'neutral': 'Average',
'good': 'Good',
'excellent': 'Excellent'
};
summary += `\n\n✨ Overall: ${expLabels[experience] || experience}`;
if (nps != null) {
summary += `\n📣 NPS: ${nps}/10`;
}
const returnLabels: Record<string, string> = {
'definitely': 'Definitely',
'probably': 'Probably',
'unsure': 'Unsure',
'unlikely': 'Unlikely',
'never': 'Will Not Return'
};
if (willReturn) {
summary += `\n🔄 Return: ${returnLabels[willReturn] || willReturn}`;
}
return summary;
},
customStyles: () => {
const experience = overallSection.emojiRating('overallExperience')?.value();
const baseStyles = {
padding: '20px',
borderRadius: '10px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px',
lineHeight: '1.6'
};
if (experience === 'excellent' || experience === 'good') {
return { ...baseStyles, backgroundColor: '#ecfdf5', borderLeft: '4px solid #10b981' };
} else if (experience === 'bad' || experience === 'very-bad') {
return { ...baseStyles, backgroundColor: '#fef2f2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Service Feedback'
});
form.configureCompletionScreen({
type: 'text',
title: () => {
const nps = overallSection.ratingScale('recommendShop')?.value();
if (nps != null && nps >= 9) {
return 'Thank You for Your Kind Feedback!';
}
if (nps != null && nps <= 6) {
return 'We Appreciate Your Honest Feedback';
}
return 'Thank You for Your Feedback!';
},
message: () => {
const nps = overallSection.ratingScale('recommendShop')?.value();
if (nps != null && nps <= 6) {
return 'We take your feedback seriously. Our service manager will reach out within 24 hours to address your concerns and make things right.';
}
return 'Your feedback helps us improve our service. We look forward to serving you again and keeping your vehicle running smoothly.';
}
});
}