export function permitProcessFeedback(form: FormTs) {
// Permit Process Feedback - Government/Municipal Services Survey
// Demonstrates: CES Scale, Timeline ratings, StarRating, EmojiRating, Conditional Flow, Slider
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Your Feedback Matters',
computedValue: () => 'Help us improve our permit and licensing services for all citizens.',
customStyles: {
backgroundColor: '#1e40af',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Permit Information
// ============================================
const permitSection = form.addSubform('permitSection', {
title: 'Permit/License Information'
});
permitSection.addRow(row => {
row.addDropdown('permitType', {
label: 'Type of Permit/License',
placeholder: 'Select permit type...',
options: [
{ id: 'building', name: 'Building Permit' },
{ id: 'renovation', name: 'Renovation/Remodeling Permit' },
{ id: 'electrical', name: 'Electrical Permit' },
{ id: 'plumbing', name: 'Plumbing Permit' },
{ id: 'zoning', name: 'Zoning/Land Use Permit' },
{ id: 'business', name: 'Business License' },
{ id: 'vendor', name: 'Vendor/Food Permit' },
{ id: 'special-event', name: 'Special Event Permit' },
{ id: 'parking', name: 'Parking Permit' },
{ id: 'other', name: 'Other' }
],
isRequired: true
}, '1fr');
row.addTextbox('permitNumber', {
label: 'Permit/Application Number (optional)',
placeholder: 'e.g., BP-2024-12345'
}, '200px');
});
permitSection.addRow(row => {
row.addRadioButton('applicationMethod', {
label: 'How did you submit your application?',
options: [
{ id: 'online', name: 'Online Portal' },
{ id: 'in-person', name: 'In Person' },
{ id: 'mail', name: 'By Mail' },
{ id: 'email', name: 'Email' }
],
orientation: 'horizontal'
});
});
permitSection.addRow(row => {
row.addRadioButton('applicationOutcome', {
label: 'Application Outcome',
options: [
{ id: 'approved', name: 'Approved' },
{ id: 'approved-conditions', name: 'Approved with Conditions' },
{ id: 'pending', name: 'Still Pending' },
{ id: 'denied', name: 'Denied' },
{ id: 'withdrawn', name: 'Withdrawn' }
],
orientation: 'horizontal',
isRequired: true
});
});
// ============================================
// SECTION 2: Customer Effort Score
// ============================================
const effortSection = form.addSubform('effortSection', {
title: 'Ease of Process'
});
effortSection.addRow(row => {
row.addRatingScale('effortScore', {
preset: 'ces',
label: 'How easy was it to complete the permit application process?',
size: 'lg',
isRequired: true
});
});
// Dynamic follow-up based on effort score
effortSection.addSpacer();
effortSection.addRow(row => {
row.addSuggestionChips('difficultAspects', {
label: 'What made the process difficult? (Select all that apply)',
suggestions: [
{ id: 'forms', name: 'Complex Forms' },
{ id: 'requirements', name: 'Unclear Requirements' },
{ id: 'documentation', name: 'Too Much Documentation' },
{ id: 'navigation', name: 'Hard to Navigate Website' },
{ id: 'wait-times', name: 'Long Wait Times' },
{ id: 'multiple-visits', name: 'Required Multiple Visits' },
{ id: 'staff', name: 'Staff Availability' },
{ id: 'fees', name: 'Fee Structure Unclear' }
],
alignment: 'left',
isVisible: () => {
const effort = effortSection.ratingScale('effortScore')?.value();
return effort !== null && effort !== undefined && effort <= 3;
}
});
});
effortSection.addRow(row => {
row.addSuggestionChips('easyAspects', {
label: 'What worked well? (Select all that apply)',
suggestions: [
{ id: 'online', name: 'Easy Online Submission' },
{ id: 'clear-instructions', name: 'Clear Instructions' },
{ id: 'helpful-staff', name: 'Helpful Staff' },
{ id: 'quick-response', name: 'Quick Response Time' },
{ id: 'status-updates', name: 'Good Status Updates' },
{ id: 'simple-forms', name: 'Simple Forms' },
{ id: 'one-visit', name: 'Completed in One Visit' }
],
alignment: 'left',
isVisible: () => {
const effort = effortSection.ratingScale('effortScore')?.value();
return effort !== null && effort !== undefined && effort >= 5;
}
});
});
// ============================================
// SECTION 3: Timeline Experience
// ============================================
const timelineSection = form.addSubform('timelineSection', {
title: 'Timeline & Processing'
});
timelineSection.addRow(row => {
row.addSlider('processingDays', {
label: 'How many business days did the process take?',
min: 1,
max: 90,
step: 1,
unit: 'days',
showValue: true,
defaultValue: 14
});
});
timelineSection.addRow(row => {
row.addStarRating('timelineSatisfaction', {
label: 'How satisfied are you with the processing time?',
maxStars: 5,
size: 'md',
alignment: 'left'
}, '1fr');
row.addRadioButton('metExpectations', {
label: 'Did the timeline meet your expectations?',
options: [
{ id: 'faster', name: 'Faster than expected' },
{ id: 'as-expected', name: 'As expected' },
{ id: 'slower', name: 'Slower than expected' }
],
orientation: 'vertical'
}, '1fr');
});
timelineSection.addRow(row => {
row.addRadioButton('statusUpdates', {
label: 'Did you receive adequate status updates during the process?',
options: [
{ id: 'yes', name: 'Yes, regular updates' },
{ id: 'some', name: 'Some updates' },
{ id: 'few', name: 'Very few updates' },
{ id: 'none', name: 'No updates at all' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 4: Staff Interaction
// ============================================
const staffSection = form.addSubform('staffSection', {
title: 'Staff Interaction',
isVisible: () => {
const method = permitSection.radioButton('applicationMethod')?.value();
return method === 'in-person' || method === 'email';
}
});
staffSection.addRow(row => {
row.addMatrixQuestion('staffRatings', {
label: 'Please rate your interactions with our staff',
rows: [
{ id: 'knowledge', label: 'Knowledge & Expertise', isRequired: true },
{ id: 'helpfulness', label: 'Helpfulness', isRequired: true },
{ id: 'professionalism', label: 'Professionalism', isRequired: true },
{ id: 'responsiveness', label: 'Responsiveness' },
{ id: 'clarity', label: 'Clear Communication' }
],
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
});
});
// ============================================
// SECTION 5: Online Experience
// ============================================
const onlineSection = form.addSubform('onlineSection', {
title: 'Online Portal Experience',
isVisible: () => permitSection.radioButton('applicationMethod')?.value() === 'online'
});
onlineSection.addRow(row => {
row.addStarRating('portalEase', {
label: 'Ease of using the online portal',
maxStars: 5,
size: 'md'
}, '1fr');
row.addStarRating('portalReliability', {
label: 'Portal reliability (no errors/crashes)',
maxStars: 5,
size: 'md'
}, '1fr');
});
onlineSection.addRow(row => {
row.addThumbRating('mobileUsability', {
label: 'Was the portal easy to use on a mobile device?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
alignment: 'left'
});
});
// ============================================
// SECTION 6: Denied Application Follow-up
// ============================================
const deniedSection = form.addSubform('deniedSection', {
title: 'About Your Denied Application',
isVisible: () => permitSection.radioButton('applicationOutcome')?.value() === 'denied',
customStyles: { backgroundColor: '#fef2f2', padding: '16px', borderRadius: '8px' }
});
deniedSection.addRow(row => {
row.addRadioButton('denialClarity', {
label: 'Were the reasons for denial clearly explained?',
options: [
{ id: 'very-clear', name: 'Very Clear' },
{ id: 'somewhat-clear', name: 'Somewhat Clear' },
{ id: 'not-clear', name: 'Not Clear' },
{ id: 'no-explanation', name: 'No Explanation Given' }
],
orientation: 'horizontal'
});
});
deniedSection.addRow(row => {
row.addRadioButton('appealInfoProvided', {
label: 'Were you informed about the appeal process?',
options: [
{ id: 'yes', name: 'Yes' },
{ id: 'no', name: 'No' },
{ id: 'not-applicable', name: 'Not Applicable' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 7: Overall Satisfaction
// ============================================
const overallSection = form.addSubform('overallSection', {
title: 'Overall Satisfaction'
});
overallSection.addRow(row => {
row.addEmojiRating('overallExperience', {
label: 'How would you describe your overall experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRatingScale('recommendScore', {
preset: 'nps',
label: 'How likely are you to recommend our permit services to others?',
showSegmentColors: true,
showCategoryLabel: true
});
});
// ============================================
// SECTION 8: Additional Comments
// ============================================
const commentsSection = form.addSubform('commentsSection', {
title: 'Your Suggestions'
});
commentsSection.addSpacer();
commentsSection.addRow(row => {
row.addTextarea('improvements', {
label: () => {
const effort = effortSection.ratingScale('effortScore')?.value();
if (effort !== null && effort !== undefined && effort <= 3) {
return 'What specific changes would make this process easier?';
}
return 'Any suggestions for improving our permit services?';
},
placeholder: 'Your feedback helps us serve you better...',
rows: 4,
autoExpand: true
});
});
commentsSection.addRow(row => {
row.addCheckbox('allowFollowUp', {
label: 'You may contact me about my feedback'
});
});
commentsSection.addRow(row => {
row.addEmail('contactEmail', {
label: 'Email for follow-up',
placeholder: 'your@email.com',
isVisible: () => commentsSection.checkbox('allowFollowUp')?.value() === true,
isRequired: () => commentsSection.checkbox('allowFollowUp')?.value() === true
});
});
// ============================================
// SECTION 9: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Feedback Summary',
isVisible: () => effortSection.ratingScale('effortScore')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const effort = effortSection.ratingScale('effortScore')?.value();
const permitType = permitSection.dropdown('permitType')?.value();
const outcome = permitSection.radioButton('applicationOutcome')?.value();
const nps = overallSection.ratingScale('recommendScore')?.value();
const npsCategory = overallSection.ratingScale('recommendScore')?.npsCategory();
const timeline = timelineSection.starRating('timelineSatisfaction')?.value();
const processingDays = timelineSection.slider('processingDays')?.value();
if (!effort) return '';
const permitLabels: Record<string, string> = {
'building': 'Building Permit',
'renovation': 'Renovation Permit',
'electrical': 'Electrical Permit',
'plumbing': 'Plumbing Permit',
'zoning': 'Zoning Permit',
'business': 'Business License',
'vendor': 'Vendor Permit',
'special-event': 'Special Event Permit',
'parking': 'Parking Permit',
'other': 'Other Permit'
};
const outcomeLabels: Record<string, string> = {
'approved': '✅ Approved',
'approved-conditions': '✅ Approved with Conditions',
'pending': '⏳ Pending',
'denied': '❌ Denied',
'withdrawn': '↩️ Withdrawn'
};
let effortEmoji = '😊';
if (effort <= 2) effortEmoji = '😫';
else if (effort <= 4) effortEmoji = '😐';
else if (effort >= 6) effortEmoji = '😃';
let summary = `📋 Permit Feedback Summary\n`;
summary += `${'═'.repeat(28)}\n\n`;
if (permitType) {
summary += `📄 Permit: ${permitLabels[permitType] || permitType}\n`;
}
if (outcome) {
summary += `📌 Status: ${outcomeLabels[outcome] || outcome}\n`;
}
summary += `\n${effortEmoji} Effort Score: ${effort}/7\n`;
if (processingDays) {
summary += `⏱️ Processing Time: ${processingDays} days\n`;
}
if (timeline) {
summary += `📅 Timeline Satisfaction: ${timeline}/5 stars\n`;
}
if (nps !== null && nps !== undefined) {
summary += `\n📊 Recommendation: ${nps}/10`;
if (npsCategory) {
summary += ` (${npsCategory.charAt(0).toUpperCase() + npsCategory.slice(1)})`;
}
}
return summary;
},
customStyles: () => {
const effort = effortSection.ratingScale('effortScore')?.value() || 4;
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (effort >= 5) {
return { ...baseStyles, backgroundColor: '#dbeafe', borderLeft: '4px solid #3b82f6' };
} else if (effort <= 3) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => effortSection.ratingScale('effortScore')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your input helps us improve government services for all citizens. We appreciate you taking the time to share your experience.'
});
}