export function cleaningServiceFeedback(form: FormTs) {
// Cleaning Service Feedback Form
// Demonstrates: RatingScale (NPS), StarRating, MatrixQuestion, EmojiRating, RadioButton, CheckboxList, Slider, SuggestionChips, ThumbRating
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'How Was Your Cleaning?',
computedValue: () => 'Your feedback helps us maintain the highest standards.',
customStyles: {
backgroundColor: '#22c55e',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Service Details
// ============================================
const serviceSection = form.addSubform('service', {
title: 'Service Details'
});
serviceSection.addRow(row => {
row.addRadioButton('serviceType', {
label: 'What type of cleaning was performed?',
options: [
{ id: 'regular', name: 'Regular/Recurring Clean' },
{ id: 'deep', name: 'Deep Clean' },
{ id: 'move', name: 'Move In/Out Clean' },
{ id: 'onetime', name: 'One-Time Service' },
{ id: 'office', name: 'Office/Commercial Clean' }
],
orientation: 'vertical',
isRequired: true
}, '1fr');
row.addRadioButton('frequency', {
label: 'How often do you use our service?',
options: [
{ id: 'first', name: 'First time' },
{ id: 'weekly', name: 'Weekly' },
{ id: 'biweekly', name: 'Every 2 weeks' },
{ id: 'monthly', name: 'Monthly' },
{ id: 'occasional', name: 'Occasional' }
],
orientation: 'vertical'
}, '1fr');
});
// ============================================
// SECTION 2: Overall Satisfaction
// ============================================
const satisfactionSection = form.addSubform('satisfaction', {
title: 'Overall Satisfaction',
customStyles: { backgroundColor: '#f0fdf4', padding: '16px', borderRadius: '8px' }
});
satisfactionSection.addRow(row => {
row.addStarRating('overallRating', {
label: 'How would you rate this cleaning overall?',
maxStars: 5,
size: 'xl',
showCounter: true,
alignment: 'center',
showConfettiOnMax: true
});
});
satisfactionSection.addSpacer({ height: '16px' });
satisfactionSection.addRow(row => {
row.addEmojiRating('satisfaction', {
label: 'How do you feel about the cleanliness of your space now?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
// ============================================
// SECTION 3: Room-by-Room Ratings
// ============================================
const roomsSection = form.addSubform('rooms', {
title: 'Room-by-Room Quality',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
roomsSection.addRow(row => {
row.addCheckboxList('roomsCleaned', {
label: 'Which rooms were cleaned? (select all)',
options: [
{ id: 'kitchen', name: 'Kitchen' },
{ id: 'bathrooms', name: 'Bathroom(s)' },
{ id: 'living', name: 'Living Room' },
{ id: 'bedrooms', name: 'Bedroom(s)' },
{ id: 'dining', name: 'Dining Room' },
{ id: 'office', name: 'Home Office' },
{ id: 'laundry', name: 'Laundry Room' },
{ id: 'common', name: 'Common Areas' }
],
orientation: 'horizontal'
});
});
roomsSection.addSpacer({ height: '16px' });
roomsSection.addRow(row => {
row.addMatrixQuestion('roomRatings', {
label: 'Rate the cleaning quality of each area:',
rows: () => {
const rooms = roomsSection.checkboxList('roomsCleaned')?.value() || [];
const roomLabels: Record<string, string> = {
'kitchen': 'Kitchen',
'bathrooms': 'Bathroom(s)',
'living': 'Living Room',
'bedrooms': 'Bedroom(s)',
'dining': 'Dining Room',
'office': 'Home Office',
'laundry': 'Laundry Room',
'common': 'Common Areas'
};
if (rooms.length === 0) {
return [
{ id: 'overall', label: 'Overall Cleanliness', isRequired: true }
];
}
return rooms.map(room => ({
id: room,
label: roomLabels[room] || room
}));
},
columns: [
{ id: 'needs_work', label: 'Needs Work' },
{ id: 'acceptable', label: 'Acceptable' },
{ id: 'good', label: 'Good' },
{ id: 'spotless', label: 'Spotless' }
],
striped: true,
fullWidth: true,
isVisible: () => {
const rooms = roomsSection.checkboxList('roomsCleaned')?.value() || [];
return rooms.length > 0;
}
});
});
// ============================================
// SECTION 4: Thoroughness Check
// ============================================
const thoroughnessSection = form.addSubform('thoroughness', {
title: 'Thoroughness',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
thoroughnessSection.addRow(row => {
row.addThumbRating('allTasksCompleted', {
label: 'Were all requested cleaning tasks completed?',
showLabels: true,
upLabel: 'Yes, all done',
downLabel: 'Something was missed',
size: 'lg',
alignment: 'center'
});
});
thoroughnessSection.addRow(row => {
row.addCheckboxList('missedAreas', {
label: 'What was missed? (select all that apply)',
options: [
{ id: 'dusting', name: 'Dusting (surfaces, fixtures)' },
{ id: 'floors', name: 'Floors (vacuuming, mopping)' },
{ id: 'surfaces', name: 'Surface wiping' },
{ id: 'mirrors', name: 'Mirrors/Glass' },
{ id: 'appliances', name: 'Appliance cleaning' },
{ id: 'trash', name: 'Trash removal' },
{ id: 'beds', name: 'Bed making' },
{ id: 'other', name: 'Other area' }
],
orientation: 'vertical',
isVisible: () => thoroughnessSection.thumbRating('allTasksCompleted')?.value() === 'down'
});
});
thoroughnessSection.addRow(row => {
row.addTextarea('missedDetails', {
label: 'Please describe what was missed',
placeholder: 'Help us understand so we can address it...',
rows: 2,
isVisible: () => thoroughnessSection.thumbRating('allTasksCompleted')?.value() === 'down'
});
});
// ============================================
// SECTION 5: Staff Rating
// ============================================
const staffSection = form.addSubform('staff', {
title: 'Cleaning Team',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
staffSection.addRow(row => {
row.addStarRating('staffRating', {
label: 'Rate the cleaning team overall',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'center'
});
});
staffSection.addSpacer({ height: '12px' });
staffSection.addRow(row => {
row.addSuggestionChips('staffPositives', {
label: 'What stood out about the team? (select all that apply)',
suggestions: [
{ id: 'professional', name: 'Professional' },
{ id: 'punctual', name: 'Punctual' },
{ id: 'thorough', name: 'Thorough' },
{ id: 'friendly', name: 'Friendly' },
{ id: 'respectful', name: 'Respectful of property' },
{ id: 'communicative', name: 'Good communication' },
{ id: 'efficient', name: 'Efficient' },
{ id: 'detail', name: 'Attention to detail' }
],
alignment: 'center'
});
});
// ============================================
// SECTION 6: Value Assessment
// ============================================
const valueSection = form.addSubform('value', {
title: 'Value for Money',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
valueSection.addRow(row => {
row.addSlider('valueRating', {
label: 'How would you rate the value for money?',
min: 1,
max: 10,
step: 1,
defaultValue: 7,
showValue: true
});
});
valueSection.addRow(row => {
row.addTextPanel('valueLabel', {
computedValue: () => {
const value = valueSection.slider('valueRating')?.value();
if (value === null || value === undefined) return '';
if (value <= 3) return '๐ Poor value - Price too high for quality';
if (value <= 5) return '๐ Fair value - Could be better';
if (value <= 7) return '๐ Good value - Worth the price';
if (value <= 9) return '๐ Great value - Quality exceeds price';
return '๐ Exceptional value!';
},
customStyles: () => {
const value = valueSection.slider('valueRating')?.value();
const baseStyles = { textAlign: 'center', padding: '8px', borderRadius: '8px', fontSize: '14px' };
if (value !== null && value !== undefined) {
if (value <= 3) return { ...baseStyles, backgroundColor: '#fee2e2' };
if (value <= 5) return { ...baseStyles, backgroundColor: '#fef3c7' };
if (value <= 7) return { ...baseStyles, backgroundColor: '#d9f99d' };
return { ...baseStyles, backgroundColor: '#bbf7d0' };
}
return baseStyles;
}
});
});
// ============================================
// SECTION 7: NPS
// ============================================
const npsSection = form.addSubform('nps', {
title: 'Recommendation',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null,
customStyles: () => {
const category = npsSection.ratingScale('npsScore')?.npsCategory();
if (category === 'promoter') return { backgroundColor: '#dcfce7', padding: '16px', borderRadius: '8px' };
if (category === 'passive') return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
if (category === 'detractor') return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #e2e8f0' };
}
});
npsSection.addRow(row => {
row.addRatingScale('npsScore', {
preset: 'nps',
label: 'How likely are you to recommend our cleaning service to friends or family?',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true
});
});
// ============================================
// SECTION 8: Additional Feedback
// ============================================
const feedbackSection = form.addSubform('feedback', {
title: 'Additional Comments',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
feedbackSection.addSpacer({ height: '8px' });
feedbackSection.addRow(row => {
row.addTextarea('additionalComments', {
label: 'Any other feedback or suggestions?',
placeholder: 'Share anything else about your experience...',
rows: 3,
autoExpand: true
});
});
feedbackSection.addRow(row => {
row.addCheckbox('canContact', {
label: 'You may contact me to discuss this feedback'
});
});
feedbackSection.addRow(row => {
row.addEmail('contactEmail', {
label: 'Email address',
placeholder: 'your@email.com',
isRequired: () => feedbackSection.checkbox('canContact')?.value() === true,
isVisible: () => feedbackSection.checkbox('canContact')?.value() === true
});
});
// ============================================
// SECTION 9: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const overallRating = satisfactionSection.starRating('overallRating')?.value();
const satisfaction = satisfactionSection.emojiRating('satisfaction')?.value();
const staffRating = staffSection.starRating('staffRating')?.value();
const valueRating = valueSection.slider('valueRating')?.value();
const npsScore = npsSection.ratingScale('npsScore')?.value();
const npsCategory = npsSection.ratingScale('npsScore')?.npsCategory();
const allCompleted = thoroughnessSection.thumbRating('allTasksCompleted')?.value();
const serviceType = serviceSection.radioButton('serviceType')?.value();
const serviceLabels: Record<string, string> = {
'regular': 'Regular Clean',
'deep': 'Deep Clean',
'move': 'Move In/Out',
'onetime': 'One-Time',
'office': 'Commercial'
};
const satisfactionLabels: Record<string, string> = {
'very-bad': '๐ข Very Unsatisfied',
'bad': '๐ Unsatisfied',
'neutral': '๐ Neutral',
'good': '๐ Satisfied',
'excellent': '๐ Very Satisfied'
};
let summary = `๐งน Cleaning Service Feedback\n`;
summary += `${'โ'.repeat(28)}\n\n`;
if (serviceType) {
summary += `๐ Service: ${serviceLabels[serviceType] || serviceType}\n`;
}
if (overallRating !== null && overallRating !== undefined) {
summary += `โญ Overall: ${'โญ'.repeat(overallRating)} (${overallRating}/5)\n`;
}
if (satisfaction) {
summary += `๐ญ Feeling: ${satisfactionLabels[satisfaction] || satisfaction}\n`;
}
if (staffRating !== null && staffRating !== undefined) {
summary += `๐ฅ Team: ${'โญ'.repeat(staffRating)} (${staffRating}/5)\n`;
}
if (valueRating !== null && valueRating !== undefined) {
summary += `๐ฐ Value: ${valueRating}/10\n`;
}
if (allCompleted !== null && allCompleted !== undefined) {
summary += `\nโ
All Tasks Done: ${allCompleted === 'up' ? 'Yes' : 'No'}`;
}
if (npsScore !== null && npsScore !== undefined) {
summary += `\n\n๐ NPS: ${npsScore}/10 (${npsCategory?.charAt(0).toUpperCase()}${npsCategory?.slice(1)})`;
}
return summary;
},
customStyles: () => {
const rating = satisfactionSection.starRating('overallRating')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (rating !== null && rating !== undefined) {
if (rating >= 4) {
return { ...baseStyles, backgroundColor: '#dcfce7', borderLeft: '4px solid #22c55e' };
} else if (rating >= 3) {
return { ...baseStyles, backgroundColor: '#fef9c3', borderLeft: '4px solid #eab308' };
} else {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
}
return baseStyles;
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: () => {
const rating = satisfactionSection.starRating('overallRating')?.value();
if (rating !== null && rating !== undefined && rating >= 5) return 'Submit Glowing Review';
return 'Submit Feedback';
},
isVisible: () => satisfactionSection.starRating('overallRating')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: () => {
const rating = satisfactionSection.starRating('overallRating')?.value();
if (rating !== null && rating !== undefined && rating >= 4) return 'Thank you for the great feedback!';
return 'Thank you for your feedback';
},
message: () => {
const rating = satisfactionSection.starRating('overallRating')?.value();
const allCompleted = thoroughnessSection.thumbRating('allTasksCompleted')?.value();
if (allCompleted === 'down') {
return "We're sorry some tasks were missed. Our team lead has been notified and will follow up to make it right. Thank you for letting us know.";
}
if (rating !== null && rating !== undefined && rating <= 3) {
return "We're sorry this cleaning didn't meet your expectations. Our quality team will review your feedback and reach out to discuss how we can improve.";
}
return "Your feedback helps us maintain high standards. We look forward to your next cleaning!";
}
});
}