export function patientSatisfaction(form: FormTs) {
// Patient Satisfaction Survey - Healthcare feedback based on HCAHPS methodology
// Demonstrates: MatrixQuestion, RatingScale (Likert), Slider, conditional sections, multi-page potential
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Patient Experience Survey',
computedValue: () => 'Your feedback helps us provide better care',
customStyles: {
backgroundColor: '#0891b2',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Visit Information
// ============================================
const visitInfo = form.addSubform('visitInfo', {
title: 'Visit Information',
customStyles: { backgroundColor: '#ecfeff', padding: '16px', borderRadius: '8px' }
});
visitInfo.addRow(row => {
row.addDropdown('visitType', {
label: 'Type of Visit',
options: [
{ id: 'hospital_inpatient', name: 'Hospital Stay (Inpatient)' },
{ id: 'hospital_outpatient', name: 'Hospital (Outpatient)' },
{ id: 'emergency', name: 'Emergency Room' },
{ id: 'clinic', name: 'Clinic/Doctor Office' },
{ id: 'urgent_care', name: 'Urgent Care' },
{ id: 'telehealth', name: 'Telehealth/Virtual' }
],
placeholder: 'Select visit type',
isRequired: true
}, '1fr');
row.addDropdown('department', {
label: 'Department/Specialty',
options: [
{ id: 'primary_care', name: 'Primary Care' },
{ id: 'cardiology', name: 'Cardiology' },
{ id: 'orthopedics', name: 'Orthopedics' },
{ id: 'pediatrics', name: 'Pediatrics' },
{ id: 'obgyn', name: 'OB/GYN' },
{ id: 'surgery', name: 'Surgery' },
{ id: 'oncology', name: 'Oncology' },
{ id: 'neurology', name: 'Neurology' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select department'
}, '1fr');
});
visitInfo.addRow(row => {
row.addDatepicker('visitDate', {
label: 'Date of Visit',
maxDate: new Date().toISOString()
}, '1fr');
row.addRadioButton('isFirstVisit', {
label: 'Is this your first visit with us?',
options: [
{ id: 'yes', name: 'Yes' },
{ id: 'no', name: 'No' }
],
orientation: 'horizontal'
}, '1fr');
});
// ============================================
// SECTION 2: Overall Experience
// ============================================
const overallSection = form.addSubform('overallSection', {
title: 'Overall Experience',
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' }
});
overallSection.addRow(row => {
row.addEmojiRating('overallMood', {
label: 'How would you rate your overall experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addStarRating('overallRating', {
label: 'Overall Quality of Care',
maxStars: 5,
size: 'xl',
alignment: 'center',
showConfettiOnMax: true
});
});
// ============================================
// SECTION 3: Communication with Nurses
// ============================================
const nurseSection = form.addSubform('nurseSection', {
title: 'Nursing Care',
isVisible: () => {
const visitType = visitInfo.dropdown('visitType')?.value();
return visitType === 'hospital_inpatient' || visitType === 'hospital_outpatient' || visitType === 'emergency';
},
customStyles: { padding: '16px', borderRadius: '8px', backgroundColor: '#f0f9ff' }
});
nurseSection.addRow(row => {
row.addMatrixQuestion('nurseRatings', {
label: 'During your visit, how often did nurses...',
rows: [
{ id: 'courtesy', label: 'Treat you with courtesy and respect', isRequired: true },
{ id: 'listen', label: 'Listen carefully to you', isRequired: true },
{ id: 'explain', label: 'Explain things clearly', isRequired: true },
{ id: 'respond', label: 'Respond quickly when you needed help', isRequired: false },
{ id: 'pain', label: 'Address your pain or discomfort', isRequired: false }
],
columns: [
{ id: '1', label: 'Never' },
{ id: '2', label: 'Sometimes' },
{ id: '3', label: 'Usually' },
{ id: '4', label: 'Always' }
],
striped: true,
fullWidth: true
});
});
// ============================================
// SECTION 4: Communication with Doctors
// ============================================
const doctorSection = form.addSubform('doctorSection', {
title: 'Doctor/Provider Communication',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null,
customStyles: { padding: '16px', borderRadius: '8px', backgroundColor: '#f0fdf4' }
});
doctorSection.addRow(row => {
row.addMatrixQuestion('doctorRatings', {
label: 'During your visit, how often did doctors/providers...',
rows: [
{ id: 'courtesy', label: 'Treat you with courtesy and respect', isRequired: true },
{ id: 'listen', label: 'Listen carefully to you', isRequired: true },
{ id: 'explain', label: 'Explain things in a way you could understand', isRequired: true },
{ id: 'time', label: 'Spend enough time with you', isRequired: false },
{ id: 'involve', label: 'Involve you in decisions about your care', isRequired: false }
],
columns: [
{ id: '1', label: 'Never' },
{ id: '2', label: 'Sometimes' },
{ id: '3', label: 'Usually' },
{ id: '4', label: 'Always' }
],
striped: true,
fullWidth: true
});
});
// ============================================
// SECTION 5: Staff Responsiveness
// ============================================
const staffSection = form.addSubform('staffSection', {
title: 'Staff & Responsiveness',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null,
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' }
});
staffSection.addRow(row => {
row.addStarRating('receptionRating', {
label: 'Front Desk/Reception',
maxStars: 5,
size: 'md'
}, '1fr');
row.addStarRating('checkInRating', {
label: 'Check-in Process',
maxStars: 5,
size: 'md'
}, '1fr');
});
staffSection.addRow(row => {
row.addSlider('waitTimeMinutes', {
label: 'How long did you wait past your appointment time? (minutes)',
min: 0,
max: 90,
step: 5,
showValue: true,
unit: 'min'
});
});
staffSection.addRow(row => {
row.addRatingScale('waitTimeAcceptability', {
preset: 'likert-5',
label: 'The wait time was acceptable',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
size: 'md'
});
});
// ============================================
// SECTION 6: Facility & Environment
// ============================================
const facilitySection = form.addSubform('facilitySection', {
title: 'Facility & Environment',
isVisible: () => {
const visitType = visitInfo.dropdown('visitType')?.value();
return visitType !== 'telehealth' && overallSection.starRating('overallRating')?.value() !== null;
},
customStyles: { padding: '16px', borderRadius: '8px', backgroundColor: '#faf5ff' }
});
facilitySection.addRow(row => {
row.addStarRating('cleanlinessRating', {
label: 'Cleanliness',
maxStars: 5,
size: 'md'
}, '1fr');
row.addStarRating('comfortRating', {
label: 'Comfort',
maxStars: 5,
size: 'md'
}, '1fr');
});
facilitySection.addRow(row => {
row.addStarRating('quietnessRating', {
label: 'Quietness/Noise Level',
maxStars: 5,
size: 'md',
tooltip: '5 stars = Very quiet and peaceful'
}, '1fr');
row.addStarRating('parkingRating', {
label: 'Parking/Accessibility',
maxStars: 5,
size: 'md'
}, '1fr');
});
// ============================================
// SECTION 7: Telehealth Experience (Conditional)
// ============================================
const telehealthSection = form.addSubform('telehealthSection', {
title: 'Telehealth Experience',
isVisible: () => visitInfo.dropdown('visitType')?.value() === 'telehealth',
customStyles: { padding: '16px', borderRadius: '8px', backgroundColor: '#eff6ff' }
});
telehealthSection.addRow(row => {
row.addStarRating('techEaseRating', {
label: 'Ease of Technology Use',
maxStars: 5,
size: 'md'
}, '1fr');
row.addStarRating('videoQualityRating', {
label: 'Video/Audio Quality',
maxStars: 5,
size: 'md'
}, '1fr');
});
telehealthSection.addRow(row => {
row.addThumbRating('wouldUseTelehealthAgain', {
label: 'Would you use telehealth again?',
showLabels: true,
upLabel: 'Yes, definitely',
downLabel: 'Prefer in-person',
alignment: 'left'
});
});
// ============================================
// SECTION 8: Discharge/After Care (For Hospital)
// ============================================
const dischargeSection = form.addSubform('dischargeSection', {
title: 'Discharge & Follow-up',
isVisible: () => {
const visitType = visitInfo.dropdown('visitType')?.value();
return (visitType === 'hospital_inpatient' || visitType === 'emergency') &&
overallSection.starRating('overallRating')?.value() !== null;
},
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' }
});
dischargeSection.addRow(row => {
row.addRatingScale('dischargeInstructions', {
preset: 'likert-5',
label: 'I clearly understood my discharge instructions',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
size: 'md'
});
});
dischargeSection.addRow(row => {
row.addRatingScale('medicationExplained', {
preset: 'likert-5',
label: 'My medications were clearly explained',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
size: 'md'
});
});
dischargeSection.addRow(row => {
row.addThumbRating('followUpScheduled', {
label: 'Was a follow-up appointment scheduled?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
alignment: 'left'
});
});
// ============================================
// SECTION 9: Issues/Concerns (Low Rating)
// ============================================
const issuesSection = form.addSubform('issuesSection', {
title: 'Help Us Improve',
isVisible: () => {
const rating = overallSection.starRating('overallRating')?.value();
return rating !== null && rating !== undefined && rating <= 2;
},
customStyles: { padding: '16px', borderRadius: '8px', backgroundColor: '#fef2f2', borderLeft: '4px solid #ef4444' }
});
issuesSection.addRow(row => {
row.addCheckboxList('concernAreas', {
label: 'What areas need improvement?',
options: [
{ id: 'wait_time', name: 'Wait time too long' },
{ id: 'communication', name: 'Poor communication' },
{ id: 'staff_attitude', name: 'Staff attitude/bedside manner' },
{ id: 'pain_management', name: 'Pain not adequately managed' },
{ id: 'cleanliness', name: 'Cleanliness issues' },
{ id: 'billing', name: 'Billing/insurance confusion' },
{ id: 'coordination', name: 'Care coordination problems' },
{ id: 'other', name: 'Other' }
],
orientation: 'vertical'
});
});
issuesSection.addRow(row => {
row.addTextarea('concernDetails', {
label: 'Please share more details',
placeholder: 'Your feedback helps us improve care for all patients...',
rows: 3,
autoExpand: true,
isRequired: () => {
const concerns = issuesSection.checkboxList('concernAreas')?.value() || [];
return concerns.length > 0;
}
});
});
// ============================================
// SECTION 10: Positive Feedback (High Rating)
// ============================================
const positiveSection = form.addSubform('positiveSection', {
title: 'Thank You!',
isVisible: () => {
const rating = overallSection.starRating('overallRating')?.value();
return rating !== null && rating !== undefined && rating >= 4;
},
customStyles: { padding: '16px', borderRadius: '8px', backgroundColor: '#ecfdf5', borderLeft: '4px solid #10b981' }
});
positiveSection.addRow(row => {
row.addSuggestionChips('excellenceAreas', {
label: 'What made your experience excellent?',
suggestions: [
{ id: 'caring_staff', name: 'Caring Staff' },
{ id: 'clear_communication', name: 'Clear Communication' },
{ id: 'short_wait', name: 'Short Wait' },
{ id: 'clean_facility', name: 'Clean Facility' },
{ id: 'thorough_exam', name: 'Thorough Examination' },
{ id: 'pain_managed', name: 'Pain Well Managed' },
{ id: 'felt_heard', name: 'Felt Heard' },
{ id: 'good_outcome', name: 'Good Outcome' }
],
max: 4,
alignment: 'center'
});
});
positiveSection.addRow(row => {
row.addTextarea('staffRecognition', {
label: 'Would you like to recognize any staff member?',
placeholder: 'Let us know who made a difference in your care...',
rows: 2,
autoExpand: true
});
});
// ============================================
// SECTION 11: Recommendation
// ============================================
const recommendSection = form.addSubform('recommendSection', {
title: 'Would You Recommend Us?',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null,
customStyles: () => {
const nps = recommendSection.ratingScale('npsScore')?.npsCategory();
if (nps === 'promoter') return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (nps === 'passive') return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
if (nps === 'detractor') return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #cbd5e1' };
}
});
recommendSection.addRow(row => {
row.addRatingScale('npsScore', {
preset: 'nps',
label: 'How likely are you to recommend our facility to friends or family?',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true
});
});
// ============================================
// SECTION 12: Contact
// ============================================
const contactSection = form.addSubform('contactSection', {
title: 'Contact Information',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
contactSection.addRow(row => {
row.addTextarea('additionalComments', {
label: 'Any additional comments or suggestions?',
placeholder: 'We value all feedback...',
rows: 3,
autoExpand: true
});
});
contactSection.addRow(row => {
row.addCheckbox('allowContact', {
label: 'You may contact me to discuss my feedback'
});
});
contactSection.addRow(row => {
row.addEmail('contactEmail', {
label: 'Email Address',
placeholder: 'your@email.com',
isVisible: () => contactSection.checkbox('allowContact')?.value() === true,
isRequired: () => contactSection.checkbox('allowContact')?.value() === true
});
});
// ============================================
// SUMMARY SECTION
// ============================================
const summarySection = form.addSubform('summarySection', {
title: 'Your Feedback Summary',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const overall = overallSection.starRating('overallRating')?.value();
const visitType = visitInfo.dropdown('visitType')?.value();
const nps = recommendSection.ratingScale('npsScore')?.value();
const npsCategory = recommendSection.ratingScale('npsScore')?.npsCategory();
const excellence = positiveSection.suggestionChips('excellenceAreas')?.value() || [];
const concerns = issuesSection.checkboxList('concernAreas')?.value() || [];
if (overall === null || overall === undefined) return '';
let emoji = overall >= 4 ? '💚' : overall >= 3 ? '💛' : '❤️🩹';
let summary = `${emoji} Patient Feedback Summary\n`;
summary += `${'═'.repeat(28)}\n\n`;
summary += `⭐ Care Rating: ${overall}/5 stars\n`;
const visitLabels: Record<string, string> = {
'hospital_inpatient': 'Hospital Stay',
'hospital_outpatient': 'Outpatient Visit',
'emergency': 'Emergency Visit',
'clinic': 'Clinic Visit',
'urgent_care': 'Urgent Care',
'telehealth': 'Telehealth'
};
if (visitType) {
summary += `🏥 Visit Type: ${visitLabels[visitType] || visitType}\n`;
}
if (nps !== null && nps !== undefined) {
summary += `\n📊 Recommendation: ${nps}/10`;
if (npsCategory) {
summary += ` (${npsCategory.charAt(0).toUpperCase()}${npsCategory.slice(1)})`;
}
}
if (excellence.length > 0) {
summary += `\n\n✨ Excellence areas: ${excellence.length} noted`;
}
if (concerns.length > 0) {
summary += `\n⚠️ Concerns: ${concerns.length} flagged`;
}
return summary;
},
customStyles: () => {
const rating = overallSection.starRating('overallRating')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (rating !== null && rating !== undefined && rating >= 4) {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (rating !== null && rating !== undefined && rating >= 3) {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
} else if (rating !== null && rating !== undefined) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return baseStyles;
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your response helps us improve the quality of care we provide. We are committed to delivering the best possible patient experience.'
});
}