export function pharmacyFeedbackSurvey(form: FormTs) {
// Pharmacy Service Review - Patient feedback for pharmacy visits
// Demonstrates: StarRating, Slider, MatrixQuestion, ThumbRating, EmojiRating, RadioButton, Timepicker
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Pharmacy Feedback',
computedValue: () => 'Your feedback helps us serve you better. Thank you for visiting!',
customStyles: {
background: 'linear-gradient(135deg, #0891b2 0%, #06b6d4 100%)',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Visit Details
// ============================================
const visitSection = form.addSubform('visitSection', {
title: 'Visit Details'
});
visitSection.addRow(row => {
row.addDatepicker('visitDate', {
label: 'Date of visit',
isRequired: true
}, '1fr');
row.addTimepicker('visitTime', {
label: 'Approximate time',
defaultValue: '14:00'
}, '1fr');
});
visitSection.addRow(row => {
row.addRadioButton('visitType', {
label: 'What was the purpose of your visit?',
options: [
{ id: 'prescription', name: 'Prescription pickup' },
{ id: 'new-rx', name: 'New prescription' },
{ id: 'consultation', name: 'Pharmacist consultation' },
{ id: 'otc', name: 'Over-the-counter purchase' },
{ id: 'vaccination', name: 'Vaccination/immunization' },
{ id: 'other', name: 'Other' }
],
orientation: 'vertical'
});
});
// ============================================
// SECTION 2: Wait Time Experience
// ============================================
const waitSection = form.addSubform('waitSection', {
title: 'Wait Time Experience'
});
waitSection.addRow(row => {
row.addSlider('waitMinutes', {
label: 'How long did you wait? (minutes)',
min: 0,
max: 60,
step: 5,
defaultValue: 15,
showValue: true,
unit: 'min'
});
});
waitSection.addRow(row => {
row.addTextPanel('waitContext', {
computedValue: () => {
const minutes = waitSection.slider('waitMinutes')?.value() ?? 15;
if (minutes === 0) return 'Served immediately - excellent!';
if (minutes <= 10) return 'Very quick service';
if (minutes <= 20) return 'Reasonable wait time';
if (minutes <= 30) return 'Longer than ideal';
if (minutes <= 45) return 'Extended wait';
return 'Very long wait - we apologize';
},
customStyles: () => {
const minutes = waitSection.slider('waitMinutes')?.value() ?? 15;
const baseStyle = { padding: '8px 16px', borderRadius: '6px', textAlign: 'center' };
if (minutes <= 10) return { ...baseStyle, backgroundColor: '#d1fae5', color: '#065f46' };
if (minutes <= 20) return { ...baseStyle, backgroundColor: '#e0f2fe', color: '#0369a1' };
if (minutes <= 30) return { ...baseStyle, backgroundColor: '#fef3c7', color: '#92400e' };
return { ...baseStyle, backgroundColor: '#fee2e2', color: '#991b1b' };
}
});
});
waitSection.addRow(row => {
row.addStarRating('waitSatisfaction', {
label: 'How satisfied were you with the wait time?',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
// Long wait follow-up
waitSection.addRow(row => {
row.addTextarea('waitFeedback', {
label: 'What would have made the wait more acceptable?',
placeholder: 'e.g., better seating, status updates, text notification...',
rows: 2,
isVisible: () => {
const minutes = waitSection.slider('waitMinutes')?.value() ?? 15;
const satisfaction = waitSection.starRating('waitSatisfaction')?.value() ?? 0;
return minutes > 20 || (satisfaction > 0 && satisfaction <= 2);
}
});
});
// ============================================
// SECTION 3: Staff & Service Quality
// ============================================
const staffSection = form.addSubform('staffSection', {
title: 'Staff & Service Quality'
});
staffSection.addRow(row => {
row.addMatrixQuestion('staffRatings', {
label: 'Rate your experience with our staff:',
rows: [
{ id: 'friendly', label: 'Friendliness & courtesy', isRequired: true },
{ id: 'helpful', label: 'Helpfulness & attentiveness', isRequired: true },
{ id: 'knowledge', label: 'Knowledge & expertise', isRequired: true },
{ id: 'communication', label: 'Clear communication', isRequired: false },
{ id: 'privacy', label: 'Respect for privacy', isRequired: false }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
// ============================================
// SECTION 4: Pharmacist Consultation
// ============================================
const consultSection = form.addSubform('consultSection', {
title: 'Pharmacist Consultation',
isVisible: () => {
const visitType = visitSection.radioButton('visitType')?.value();
return visitType === 'prescription' || visitType === 'new-rx' || visitType === 'consultation';
}
});
consultSection.addRow(row => {
row.addThumbRating('hadConsultation', {
label: 'Did you speak with a pharmacist?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
size: 'lg',
alignment: 'center'
});
});
// Consultation details if yes
const consultDetailsSection = form.addSubform('consultDetailsSection', {
title: 'Consultation Experience',
isVisible: () => consultSection.thumbRating('hadConsultation')?.value() === 'up'
});
consultDetailsSection.addRow(row => {
row.addStarRating('consultQuality', {
label: 'How helpful was the pharmacist consultation?',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
consultDetailsSection.addRow(row => {
row.addCheckboxList('consultTopics', {
label: 'What did you discuss?',
options: [
{ id: 'dosage', name: 'Dosage instructions' },
{ id: 'side-effects', name: 'Side effects' },
{ id: 'interactions', name: 'Drug interactions' },
{ id: 'generic', name: 'Generic alternatives' },
{ id: 'cost', name: 'Cost/insurance' },
{ id: 'otc', name: 'OTC recommendations' },
{ id: 'other', name: 'Other health questions' }
],
orientation: 'vertical'
});
});
consultDetailsSection.addRow(row => {
row.addThumbRating('questionsAnswered', {
label: 'Were all your questions answered?',
showLabels: true,
upLabel: 'Yes, completely',
downLabel: 'No, I still have questions',
size: 'md',
alignment: 'center'
});
});
consultDetailsSection.addRow(row => {
row.addTextarea('unansweredQuestions', {
label: 'What questions remain unanswered?',
placeholder: 'Please share so we can follow up...',
rows: 2,
isVisible: () => consultDetailsSection.thumbRating('questionsAnswered')?.value() === 'down'
});
});
// ============================================
// SECTION 5: Prescription Accuracy
// ============================================
const accuracySection = form.addSubform('accuracySection', {
title: 'Prescription Accuracy',
isVisible: () => {
const visitType = visitSection.radioButton('visitType')?.value();
return visitType === 'prescription' || visitType === 'new-rx';
}
});
accuracySection.addRow(row => {
row.addThumbRating('rxAccurate', {
label: 'Was your prescription filled correctly?',
showLabels: true,
upLabel: 'Yes, everything correct',
downLabel: 'No, there was an issue',
size: 'lg',
alignment: 'center'
});
});
// Accuracy issue follow-up
accuracySection.addRow(row => {
row.addRadioButton('accuracyIssue', {
label: 'What issue did you experience?',
options: [
{ id: 'wrong-med', name: 'Wrong medication' },
{ id: 'wrong-dose', name: 'Wrong dosage/quantity' },
{ id: 'wrong-label', name: 'Incorrect label information' },
{ id: 'missing', name: 'Medication missing from order' },
{ id: 'insurance', name: 'Insurance/billing error' },
{ id: 'other', name: 'Other issue' }
],
orientation: 'vertical',
isRequired: true,
isVisible: () => accuracySection.thumbRating('rxAccurate')?.value() === 'down'
});
});
accuracySection.addRow(row => {
row.addTextarea('accuracyDetails', {
label: 'Please describe the issue',
placeholder: 'Details help us prevent future errors...',
rows: 2,
isRequired: true,
isVisible: () => accuracySection.thumbRating('rxAccurate')?.value() === 'down'
});
});
accuracySection.addRow(row => {
row.addTextPanel('accuracyWarning', {
computedValue: () => '⚠️ We take accuracy very seriously. Our quality team will review this feedback and may contact you.',
customStyles: {
backgroundColor: '#fee2e2',
color: '#991b1b',
padding: '12px',
borderRadius: '6px',
borderLeft: '4px solid #ef4444'
},
isVisible: () => accuracySection.thumbRating('rxAccurate')?.value() === 'down'
});
});
// ============================================
// SECTION 6: Store Environment
// ============================================
const storeSection = form.addSubform('storeSection', {
title: 'Store Environment'
});
storeSection.addRow(row => {
row.addStarRating('cleanliness', {
label: 'Cleanliness',
maxStars: 5,
size: 'md',
showCounter: true
}, '1fr');
row.addStarRating('organization', {
label: 'Organization & ease of finding items',
maxStars: 5,
size: 'md',
showCounter: true
}, '1fr');
});
// ============================================
// SECTION 7: Overall Experience
// ============================================
const overallSection = form.addSubform('overallSection', {
title: 'Overall Experience',
customStyles: () => {
const mood = overallSection.emojiRating('overallMood')?.value();
if (mood === 'excellent' || mood === 'good') return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (mood === 'very-bad' || mood === 'bad') return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' };
}
});
overallSection.addRow(row => {
row.addEmojiRating('overallMood', {
label: 'How was your overall pharmacy experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRatingScale('recommendLikelihood', {
label: 'How likely are you to recommend our pharmacy to others?',
preset: 'nps',
showCategoryLabel: true,
showSegmentColors: true,
alignment: 'center'
});
});
overallSection.addSpacer();
overallSection.addRow(row => {
row.addTextarea('additionalComments', {
label: () => {
const mood = overallSection.emojiRating('overallMood')?.value();
if (mood === 'excellent' || mood === 'good') return 'What made your experience great?';
if (mood === 'very-bad' || mood === 'bad') return 'How can we make it right?';
return 'Any additional feedback?';
},
placeholder: 'Your feedback is valuable to us...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 8: Summary
// ============================================
const summarySection = form.addSubform('summarySection', {
title: 'Feedback Summary',
isVisible: () => overallSection.emojiRating('overallMood')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summary', {
computedValue: () => {
const visitType = visitSection.radioButton('visitType')?.value() || 'Visit';
const waitMinutes = waitSection.slider('waitMinutes')?.value() ?? 0;
const waitSat = waitSection.starRating('waitSatisfaction')?.value() ?? 0;
const mood = overallSection.emojiRating('overallMood')?.value();
const nps = overallSection.ratingScale('recommendLikelihood')?.value() ?? 0;
const rxAccurate = accuracySection.thumbRating('rxAccurate')?.value();
const consultQuality = consultDetailsSection.starRating('consultQuality')?.value() ?? 0;
const visitLabels: Record<string, string> = {
'prescription': 'Prescription Pickup', 'new-rx': 'New Prescription',
'consultation': 'Consultation', 'otc': 'OTC Purchase',
'vaccination': 'Vaccination', 'other': 'Other'
};
const moodLabels: Record<string, string> = {
'very-bad': 'Very Unsatisfied', 'bad': 'Unsatisfied', 'neutral': 'Neutral',
'good': 'Satisfied', 'excellent': 'Very Satisfied'
};
let summary = 'Pharmacy Visit Summary\n';
summary += '═'.repeat(25) + '\n\n';
summary += `Visit Type: ${visitLabels[visitType] || visitType}\n`;
summary += `Wait Time: ${waitMinutes} minutes\n`;
summary += `Wait Satisfaction: ${waitSat > 0 ? '★'.repeat(waitSat) + '☆'.repeat(5 - waitSat) : 'Not rated'}\n`;
if (consultQuality > 0) {
summary += `Consultation Quality: ${consultQuality}/5\n`;
}
if (rxAccurate) {
summary += `Prescription Accurate: ${rxAccurate === 'up' ? 'Yes' : '⚠️ ISSUE REPORTED'}\n`;
}
summary += `\nOverall: ${moodLabels[mood || ''] || 'Not rated'}\n`;
summary += `NPS Score: ${nps > 0 ? nps + '/10' : 'Not rated'}\n`;
// Alerts
if (rxAccurate === 'down') {
summary += '\n⚠️ ATTENTION: Prescription accuracy issue reported';
}
if (waitMinutes > 30) {
summary += '\n⚠️ Long wait time - review staffing levels';
}
return summary;
},
customStyles: () => {
const rxAccurate = accuracySection.thumbRating('rxAccurate')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px'
};
if (rxAccurate === 'down') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#ecfeff', borderLeft: '4px solid #06b6d4' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback'
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank you for your feedback!',
message: 'Your input helps us provide better pharmacy care. If you reported any concerns, our team will review and follow up if needed. Thank you for choosing our pharmacy!'
});
}