export function appointmentFeedbackSurvey(form: FormTs) {
// Appointment Experience Survey - Healthcare Feedback
// Demonstrates: StarRating, RatingScale (CES), EmojiRating, MatrixQuestion, Slider, Integer, Dropdown, ThumbRating
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'How Was Your Visit?',
computedValue: () => 'Your feedback helps us provide better care. This survey takes about 2 minutes.',
customStyles: {
backgroundColor: '#0d9488',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Appointment Details
// ============================================
const detailsSection = form.addSubform('detailsSection', {
title: 'Appointment Details'
});
detailsSection.addRow(row => {
row.addDropdown('appointmentType', {
label: 'What type of appointment was this?',
options: [
{ id: 'routine', name: 'Routine Checkup' },
{ id: 'followup', name: 'Follow-up Visit' },
{ id: 'new-issue', name: 'New Health Concern' },
{ id: 'specialist', name: 'Specialist Consultation' },
{ id: 'procedure', name: 'Procedure/Treatment' },
{ id: 'telehealth', name: 'Telehealth/Virtual Visit' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select appointment type...'
});
});
detailsSection.addRow(row => {
row.addThumbRating('firstVisit', {
label: 'Was this your first visit to our practice?',
size: 'md',
showLabels: true,
upLabel: 'Yes, first time',
downLabel: 'No, returning patient',
alignment: 'left'
});
});
// ============================================
// SECTION 2: Scheduling & Arrival
// ============================================
const schedulingSection = form.addSubform('schedulingSection', {
title: 'Scheduling Experience',
isVisible: () => detailsSection.dropdown('appointmentType')?.value() !== null
});
schedulingSection.addRow(row => {
row.addRatingScale('schedulingEase', {
label: 'How easy was it to schedule your appointment?',
preset: 'ces',
size: 'md',
alignment: 'center'
});
});
schedulingSection.addRow(row => {
row.addRadioButton('schedulingMethod', {
label: 'How did you schedule?',
options: [
{ id: 'phone', name: 'Phone call' },
{ id: 'online', name: 'Online portal' },
{ id: 'app', name: 'Mobile app' },
{ id: 'in-person', name: 'In person' },
{ id: 'referral', name: 'Provider referral' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 3: Wait Time
// ============================================
const waitSection = form.addSubform('waitSection', {
title: 'Wait Time',
isVisible: () => detailsSection.dropdown('appointmentType')?.value() !== null &&
detailsSection.dropdown('appointmentType')?.value() !== 'telehealth',
customStyles: () => {
const wait = waitSection.integer('waitMinutes')?.value();
if (wait && wait <= 10) return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (wait && wait >= 30) return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #cbd5e1' };
}
});
waitSection.addRow(row => {
row.addInteger('waitMinutes', {
label: 'Approximately how long did you wait past your scheduled time? (in minutes)',
min: 0,
max: 120,
step: 5,
placeholder: 'Enter minutes...'
});
});
waitSection.addRow(row => {
row.addTextPanel('waitFeedback', {
computedValue: () => {
const wait = waitSection.integer('waitMinutes')?.value();
if (wait === 0) return '🎯 Perfect! Seen right on time.';
if (wait && wait <= 10) return '✅ That\'s a reasonable wait time.';
if (wait && wait <= 20) return '⏱️ Thanks for your patience.';
if (wait && wait <= 30) return '😕 We apologize for the delay.';
if (wait && wait > 30) return '😔 We\'re sorry for the long wait. This shouldn\'t happen.';
return '';
},
customStyles: { fontStyle: 'italic', textAlign: 'center', padding: '8px' },
isVisible: () => waitSection.integer('waitMinutes')?.value() !== null
});
});
waitSection.addRow(row => {
row.addSlider('waitAcceptability', {
label: 'How acceptable was the wait time?',
min: 1,
max: 5,
step: 1,
showValue: true,
defaultValue: 3,
isVisible: () => {
const wait = waitSection.integer('waitMinutes')?.value();
return wait !== null && wait !== undefined && wait > 0;
}
});
});
// ============================================
// SECTION 4: Staff Experience
// ============================================
const staffSection = form.addSubform('staffSection', {
title: 'Staff & Facility',
isVisible: () => detailsSection.dropdown('appointmentType')?.value() !== null
});
staffSection.addRow(row => {
row.addMatrixQuestion('staffRatings', {
label: 'Please rate our staff and facility:',
rows: [
{ id: 'front-desk', label: 'Front desk/reception', isRequired: true },
{ id: 'nursing', label: 'Nursing staff', isRequired: false },
{ id: 'cleanliness', label: 'Facility cleanliness', isRequired: true },
{ id: 'comfort', label: 'Waiting area comfort', isRequired: false },
{ id: 'privacy', label: 'Privacy & confidentiality', isRequired: false }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' },
{ id: 'na', label: 'N/A' }
],
striped: true,
fullWidth: true
});
});
// ============================================
// SECTION 5: Provider Experience
// ============================================
const providerSection = form.addSubform('providerSection', {
title: 'Your Care Provider',
isVisible: () => detailsSection.dropdown('appointmentType')?.value() !== null,
customStyles: () => {
const rating = providerSection.starRating('providerRating')?.value();
if (rating && rating >= 4) return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (rating && rating <= 2) return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #cbd5e1' };
}
});
providerSection.addRow(row => {
row.addStarRating('providerRating', {
label: 'How would you rate your care provider?',
maxStars: 5,
size: 'xl',
alignment: 'center',
showConfettiOnMax: true
});
});
providerSection.addRow(row => {
row.addMatrixQuestion('providerAttributes', {
label: 'Please rate your provider on:',
rows: [
{ id: 'listening', label: 'Listened to my concerns', isRequired: true },
{ id: 'explaining', label: 'Explained things clearly', isRequired: true },
{ id: 'time', label: 'Spent adequate time with me', isRequired: false },
{ id: 'respect', label: 'Treated me with respect', isRequired: false },
{ id: 'involvement', label: 'Involved me in decisions', isRequired: false }
],
columns: [
{ id: 'strongly-disagree', label: 'Strongly Disagree' },
{ id: 'disagree', label: 'Disagree' },
{ id: 'neutral', label: 'Neutral' },
{ id: 'agree', label: 'Agree' },
{ id: 'strongly-agree', label: 'Strongly Agree' }
],
striped: true,
fullWidth: true,
isVisible: () => providerSection.starRating('providerRating')?.value() !== null
});
});
// ============================================
// SECTION 6: Overall Experience
// ============================================
const overallSection = form.addSubform('overallSection', {
title: 'Overall Experience',
isVisible: () => providerSection.starRating('providerRating')?.value() !== null
});
overallSection.addRow(row => {
row.addEmojiRating('overallMood', {
label: 'Overall, how did you feel about your visit?',
preset: 'satisfaction',
size: 'lg',
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addThumbRating('wouldReturn', {
label: 'Would you return to our practice for future care?',
size: 'lg',
showLabels: true,
upLabel: 'Yes, definitely',
downLabel: 'Probably not',
alignment: 'center',
isVisible: () => overallSection.emojiRating('overallMood')?.value() !== null
});
});
// ============================================
// SECTION 7: Concerns & Improvements
// ============================================
const concernsSection = form.addSubform('concernsSection', {
title: 'Help Us Improve',
isVisible: () => {
const mood = overallSection.emojiRating('overallMood')?.value();
const provider = providerSection.starRating('providerRating')?.value();
return (mood === 'very-bad' || mood === 'bad' || mood === 'neutral') ||
(provider !== null && provider !== undefined && provider <= 3);
},
customStyles: { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' }
});
concernsSection.addRow(row => {
row.addCheckboxList('concernAreas', {
label: 'What areas need improvement? (select all that apply)',
options: [
{ id: 'wait-time', name: 'Wait time too long' },
{ id: 'scheduling', name: 'Scheduling difficulties' },
{ id: 'communication', name: 'Communication issues' },
{ id: 'staff-attitude', name: 'Staff attitude' },
{ id: 'provider-time', name: 'Not enough time with provider' },
{ id: 'facility', name: 'Facility issues' },
{ id: 'billing', name: 'Billing/insurance confusion' },
{ id: 'follow-up', name: 'Follow-up care unclear' }
],
orientation: 'vertical'
});
});
concernsSection.addSpacer();
concernsSection.addRow(row => {
row.addTextarea('improvementDetails', {
label: 'Please share more details about your concerns:',
placeholder: 'Your feedback helps us improve care for all patients...',
rows: 3
});
});
// ============================================
// SECTION 8: Positive Feedback (for good ratings)
// ============================================
const praiseSection = form.addSubform('praiseSection', {
title: 'What Went Well',
isVisible: () => {
const mood = overallSection.emojiRating('overallMood')?.value();
const provider = providerSection.starRating('providerRating')?.value();
return (mood === 'good' || mood === 'excellent') &&
(provider !== null && provider !== undefined && provider >= 4);
},
customStyles: { backgroundColor: '#ecfdf5', padding: '16px', borderRadius: '8px' }
});
praiseSection.addRow(row => {
row.addTextarea('positiveComments', {
label: 'What made your experience positive?',
placeholder: 'We love hearing what we did well!',
rows: 2
});
});
// ============================================
// SECTION 9: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Visit Summary',
isVisible: () => overallSection.emojiRating('overallMood')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const appointmentType = detailsSection.dropdown('appointmentType')?.value();
const schedulingEase = schedulingSection.ratingScale('schedulingEase')?.value();
const waitTime = waitSection.integer('waitMinutes')?.value();
const providerRating = providerSection.starRating('providerRating')?.value();
const overallMood = overallSection.emojiRating('overallMood')?.value();
const wouldReturn = overallSection.thumbRating('wouldReturn')?.value();
let summary = '🏥 Appointment Feedback Summary\n';
summary += '━'.repeat(30) + '\n\n';
// Appointment type
if (appointmentType) {
const typeLabels: Record<string, string> = {
'routine': 'Routine Checkup',
'followup': 'Follow-up Visit',
'new-issue': 'New Health Concern',
'specialist': 'Specialist Consultation',
'procedure': 'Procedure/Treatment',
'telehealth': 'Telehealth Visit',
'other': 'Other'
};
summary += `📋 Visit Type: ${typeLabels[appointmentType] || appointmentType}\n`;
}
// Scheduling ease
if (schedulingEase) {
const easeLevel = schedulingEase >= 6 ? 'Easy' : schedulingEase >= 4 ? 'Moderate' : 'Difficult';
summary += `📅 Scheduling: ${easeLevel} (${schedulingEase}/7)\n`;
}
// Wait time
if (waitTime !== null && waitTime !== undefined) {
const waitEmoji = waitTime <= 10 ? '✅' : waitTime <= 20 ? '⏱️' : '⚠️';
summary += `${waitEmoji} Wait Time: ${waitTime} minutes\n`;
}
summary += '\n';
// Provider
if (providerRating) {
summary += `👨⚕️ Provider: ${'⭐'.repeat(providerRating)}${providerRating}/5\n`;
}
// Overall
if (overallMood) {
const moodLabels: Record<string, string> = {
'very-bad': '😢 Very Unsatisfied',
'bad': '😕 Unsatisfied',
'neutral': '😐 Neutral',
'good': '🙂 Satisfied',
'excellent': '😊 Very Satisfied'
};
summary += `\n${moodLabels[overallMood] || overallMood}\n`;
}
// Would return
if (wouldReturn === 'up') {
summary += '✅ Would return';
} else if (wouldReturn === 'down') {
summary += '❌ May not return';
}
return summary;
},
customStyles: () => {
const mood = overallSection.emojiRating('overallMood')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (mood === 'good' || mood === 'excellent') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (mood === 'very-bad' || mood === 'bad') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => overallSection.emojiRating('overallMood')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your input helps us improve the care we provide to all patients. If you have any immediate concerns, please contact our office directly. We appreciate you trusting us with your healthcare.'
});
}