export function salonFeedback(form: FormTs) {
// Hair & Beauty Salon Feedback - Comprehensive service and stylist evaluation
// Demonstrates: StarRating x6, EmojiRating, MatrixQuestion, Slider, ThumbRating, conditional sections, dynamic labels
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: '💇 How Was Your Salon Experience?',
computedValue: () => 'Your feedback helps us perfect your next visit. This takes about 2 minutes.',
customStyles: {
background: 'linear-gradient(135deg, #be185d 0%, #ec4899 100%)',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Visit Details
// ============================================
const visitSection = form.addSubform('visit', {
title: 'Your Visit'
});
visitSection.addRow(row => {
row.addDatepicker('visitDate', {
label: 'Date of your visit',
maxDate: () => new Date().toISOString().split('T')[0]
}, '1fr');
row.addTextbox('stylistName', {
label: 'Your stylist\'s name',
placeholder: 'e.g., Sarah, Miguel'
}, '1fr');
});
visitSection.addRow(row => {
row.addCheckboxList('services', {
label: 'What services did you receive? (Select all that apply)',
options: [
{ id: 'haircut', name: 'Haircut / Trim' },
{ id: 'color', name: 'Hair Color / Highlights' },
{ id: 'blowout', name: 'Blowout / Styling' },
{ id: 'treatment', name: 'Treatment (Keratin, Deep Conditioning)' },
{ id: 'extensions', name: 'Extensions' },
{ id: 'makeup', name: 'Makeup' },
{ id: 'nails', name: 'Nail Services' },
{ id: 'waxing', name: 'Waxing / Hair Removal' },
{ id: 'facial', name: 'Facial / Skincare' },
{ id: 'other', name: 'Other' }
],
orientation: 'vertical',
isRequired: true
});
});
// ============================================
// SECTION 2: Booking & Arrival
// ============================================
const bookingSection = form.addSubform('booking', {
title: 'Booking & Arrival'
});
bookingSection.addRow(row => {
row.addRadioButton('bookingMethod', {
label: 'How did you book your appointment?',
options: [
{ id: 'online', name: 'Online / App' },
{ id: 'phone', name: 'Phone call' },
{ id: 'walkin', name: 'Walk-in' },
{ id: 'inperson', name: 'Booked in person at previous visit' }
],
orientation: 'horizontal'
});
});
bookingSection.addRow(row => {
row.addStarRating('bookingEase', {
label: 'How easy was it to book your appointment?',
maxStars: 5,
size: 'md',
alignment: 'center',
isVisible: () => bookingSection.radioButton('bookingMethod')?.value() !== 'walkin'
});
});
bookingSection.addRow(row => {
row.addRadioButton('waitTime', {
label: 'How long did you wait past your appointment time?',
options: [
{ id: 'early', name: 'Seen early/on time' },
{ id: '5min', name: '1-5 minutes' },
{ id: '15min', name: '5-15 minutes' },
{ id: '30min', name: '15-30 minutes' },
{ id: 'over30', name: 'Over 30 minutes' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 3: Stylist Evaluation
// ============================================
const stylistSection = form.addSubform('stylist', {
title: () => {
const name = visitSection.textbox('stylistName')?.value();
return name ? `About ${name}` : 'Your Stylist';
}
});
stylistSection.addRow(row => {
row.addMatrixQuestion('stylistRatings', {
label: () => {
const name = visitSection.textbox('stylistName')?.value();
return `Rate ${name || 'your stylist'} on the following:`;
},
rows: [
{ id: 'consultation', label: 'Consultation', description: 'Listened to your needs and gave advice', isRequired: true },
{ id: 'skill', label: 'Technical Skill', description: 'Expertise in performing the service', isRequired: true },
{ id: 'communication', label: 'Communication', description: 'Friendly, professional conversation', isRequired: true },
{ id: 'attention', label: 'Attention to Detail', description: 'Care and precision in their work', isRequired: true },
{ id: 'time', label: 'Time Management', description: 'Efficient without rushing' }
],
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
});
});
stylistSection.addRow(row => {
row.addStarRating('overallStylist', {
label: () => {
const name = visitSection.textbox('stylistName')?.value();
return `Overall rating for ${name || 'your stylist'}`;
},
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true
});
});
stylistSection.addRow(row => {
row.addThumbRating('bookAgain', {
label: () => {
const name = visitSection.textbox('stylistName')?.value();
return `Would you book with ${name || 'this stylist'} again?`;
},
showLabels: true,
upLabel: 'Definitely!',
downLabel: 'Probably not',
alignment: 'center',
size: 'lg'
});
});
// ============================================
// SECTION 4: Service-Specific Feedback
// ============================================
// Haircut feedback
const haircutSection = form.addSubform('haircutFeedback', {
title: 'Haircut Results',
isVisible: () => {
const services = visitSection.checkboxList('services')?.value() || [];
return services.includes('haircut');
},
customStyles: { backgroundColor: '#fdf4ff', padding: '16px', borderRadius: '8px' }
});
haircutSection.addRow(row => {
row.addStarRating('haircutAccuracy', {
label: 'How well did the cut match what you asked for?',
maxStars: 5,
size: 'md',
alignment: 'left'
}, '1fr');
row.addStarRating('haircutStyling', {
label: 'How easy is it to style at home?',
maxStars: 5,
size: 'md',
alignment: 'left'
}, '1fr');
});
// Color feedback
const colorSection = form.addSubform('colorFeedback', {
title: 'Hair Color Results',
isVisible: () => {
const services = visitSection.checkboxList('services')?.value() || [];
return services.includes('color');
},
customStyles: { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' }
});
colorSection.addRow(row => {
row.addStarRating('colorAccuracy', {
label: 'Color accuracy vs. what you wanted',
maxStars: 5,
size: 'md',
alignment: 'left'
}, '1fr');
row.addStarRating('colorCoverage', {
label: 'Coverage quality (grays, roots)',
maxStars: 5,
size: 'md',
alignment: 'left'
}, '1fr');
});
colorSection.addRow(row => {
row.addRadioButton('colorScalpComfort', {
label: 'Any discomfort during color application?',
options: [
{ id: 'none', name: 'No discomfort' },
{ id: 'mild', name: 'Mild tingling' },
{ id: 'moderate', name: 'Moderate discomfort' },
{ id: 'severe', name: 'Significant discomfort' }
],
orientation: 'horizontal'
});
});
// Treatment feedback
const treatmentSection = form.addSubform('treatmentFeedback', {
title: 'Treatment Results',
isVisible: () => {
const services = visitSection.checkboxList('services')?.value() || [];
return services.includes('treatment');
},
customStyles: { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' }
});
treatmentSection.addRow(row => {
row.addStarRating('treatmentResults', {
label: 'How noticeable are the treatment results?',
maxStars: 5,
size: 'md',
alignment: 'center'
});
});
treatmentSection.addRow(row => {
row.addRadioButton('treatmentDuration', {
label: 'How long have the results lasted? (If applicable)',
options: [
{ id: 'tooEarly', name: 'Too early to tell' },
{ id: 'short', name: 'Shorter than expected' },
{ id: 'expected', name: 'As expected' },
{ id: 'longer', name: 'Longer than expected' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 5: Salon Environment
// ============================================
const salonSection = form.addSubform('salon', {
title: 'Salon Environment'
});
salonSection.addRow(row => {
row.addMatrixQuestion('salonRatings', {
label: 'Rate the salon on the following:',
rows: [
{ id: 'cleanliness', label: 'Cleanliness', description: 'Station, tools, overall hygiene', isRequired: true },
{ id: 'ambiance', label: 'Ambiance', description: 'Music, lighting, decor', isRequired: true },
{ id: 'comfort', label: 'Comfort', description: 'Chair, shampoo station, temperature' },
{ id: 'beverages', label: 'Refreshments', description: 'Drinks, snacks offered' }
],
columns: [
{ id: 'na', label: 'N/A' },
{ 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 6: Value & Pricing
// ============================================
const valueSection = form.addSubform('value', {
title: 'Value & Pricing'
});
valueSection.addRow(row => {
row.addSlider('valueForMoney', {
label: 'Value for money',
min: 1,
max: 10,
defaultValue: 7,
showValue: true,
unit: '/10'
});
});
valueSection.addRow(row => {
row.addRadioButton('pricePerception', {
label: 'How would you describe the pricing?',
options: [
{ id: 'excellent', name: 'Excellent value' },
{ id: 'fair', name: 'Fair for the quality' },
{ id: 'bit-high', name: 'Slightly expensive' },
{ id: 'expensive', name: 'Overpriced' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 7: Overall Experience
// ============================================
const overallSection = form.addSubform('overall', {
title: 'Overall Experience'
});
overallSection.addRow(row => {
row.addEmojiRating('overallExperience', {
label: 'How would you rate your overall salon experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRatingScale('npsScore', {
label: 'How likely are you to recommend our salon to friends?',
preset: 'nps',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true
});
});
// ============================================
// SECTION 8: Conditional Follow-up
// ============================================
// Happy customer section
const happySection = form.addSubform('happy', {
title: 'We\'re Thrilled You Loved It! 💕',
isVisible: () => {
const emoji = overallSection.emojiRating('overallExperience')?.value();
return emoji === 'excellent' || emoji === 'good';
},
customStyles: { backgroundColor: '#fce7f3', padding: '16px', borderRadius: '8px' }
});
happySection.addRow(row => {
row.addSuggestionChips('whatMadeItGreat', {
label: 'What made your visit special?',
suggestions: [
{ id: 'stylist', name: 'Amazing stylist' },
{ id: 'results', name: 'Love the results' },
{ id: 'atmosphere', name: 'Great atmosphere' },
{ id: 'friendly', name: 'Friendly staff' },
{ id: 'relaxing', name: 'Relaxing experience' },
{ id: 'value', name: 'Great value' }
],
alignment: 'center'
});
});
happySection.addRow(row => {
row.addCheckbox('photoPermission', {
label: 'May we feature your results on our social media? (We\'ll contact you first)'
});
});
// Improvement section
const improvementSection = form.addSubform('improvement', {
title: 'Help Us Do Better',
isVisible: () => {
const emoji = overallSection.emojiRating('overallExperience')?.value();
return emoji === 'very-bad' || emoji === 'bad' || emoji === 'neutral';
},
customStyles: { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' }
});
improvementSection.addRow(row => {
row.addSuggestionChips('improvementAreas', {
label: 'What could we improve?',
suggestions: [
{ id: 'wait', name: 'Wait time' },
{ id: 'communication', name: 'Consultation' },
{ id: 'results', name: 'Service results' },
{ id: 'cleanliness', name: 'Cleanliness' },
{ id: 'pricing', name: 'Pricing' },
{ id: 'atmosphere', name: 'Atmosphere' }
],
alignment: 'center'
});
});
improvementSection.addRow(row => {
row.addCheckbox('wantCallback', {
label: 'I would like a manager to contact me about my experience'
});
});
// ============================================
// SECTION 9: Additional Comments
// ============================================
const commentsSection = form.addSubform('comments', {
title: 'Additional Feedback'
});
commentsSection.addSpacer();
commentsSection.addRow(row => {
row.addTextarea('additionalComments', {
label: 'Any other comments or suggestions?',
placeholder: 'Tell us anything else about your experience...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 10: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Your Feedback Summary',
isVisible: () => overallSection.emojiRating('overallExperience')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const stylistName = visitSection.textbox('stylistName')?.value();
const services = visitSection.checkboxList('services')?.value() || [];
const stylistRating = stylistSection.starRating('overallStylist')?.value();
const experience = overallSection.emojiRating('overallExperience')?.value();
const nps = overallSection.ratingScale('npsScore')?.value();
const bookAgain = stylistSection.thumbRating('bookAgain')?.value();
const experienceLabels: Record<string, string> = {
'very-bad': '😢 Very Unsatisfied',
'bad': '😕 Unsatisfied',
'neutral': '😐 Neutral',
'good': '😊 Satisfied',
'excellent': '😃 Delighted'
};
let summary = `💇 Salon Visit Summary\n`;
summary += `${'═'.repeat(24)}\n\n`;
if (stylistName) {
summary += `💄 Stylist: ${stylistName}\n`;
}
summary += `✂️ Services: ${services.length} received\n`;
if (stylistRating) {
summary += `⭐ Stylist Rating: ${stylistRating}/5\n`;
}
if (bookAgain) {
summary += `🔄 Would Rebook: ${bookAgain === 'up' ? 'Yes!' : 'No'}\n`;
}
if (experience) {
summary += `\n${experienceLabels[experience]}`;
}
if (nps !== null && nps !== undefined) {
summary += `\n📊 NPS: ${nps}/10`;
}
return summary;
},
customStyles: () => {
const experience = overallSection.emojiRating('overallExperience')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (experience === 'excellent' || experience === 'good') {
return { ...baseStyles, backgroundColor: '#fce7f3', borderLeft: '4px solid #ec4899' };
} else if (experience === 'very-bad' || experience === 'bad') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#f8fafc', borderLeft: '4px solid #be185d' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Salon Feedback'
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback! 💕',
message: () => {
const experience = overallSection.emojiRating('overallExperience')?.value();
if (experience === 'excellent' || experience === 'good') {
return 'We\'re so glad you had a great experience! We can\'t wait to see you again. Show this screen for 10% off your next booking!';
}
return 'We appreciate your honest feedback and will use it to improve. A team member may reach out to follow up on your experience.';
}
});
}