export function townHallFeedback(form: FormTs) {
// Town Hall Meeting Feedback - Government/Municipal meeting evaluation
// Demonstrates: RatingScale (Likert), MatrixQuestion, ThumbRating, Slider, Dropdown, EmojiRating, SuggestionChips
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Town Hall Meeting Feedback',
computedValue: () => 'Your voice matters. Help us improve our community meetings.',
customStyles: {
backgroundColor: '#1e40af',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Attendance Information
// ============================================
const attendanceSection = form.addSubform('attendanceSection', {
title: 'Attendance Information',
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #dbeafe' }
});
attendanceSection.addRow(row => {
row.addDropdown('attendanceType', {
label: 'How did you attend the meeting?',
options: [
{ id: 'in-person', name: 'In-person at the venue' },
{ id: 'virtual', name: 'Online/Virtual' },
{ id: 'recording', name: 'Watched the recording later' }
],
placeholder: 'Select attendance type',
isRequired: true
}, '1fr');
row.addDropdown('residentType', {
label: 'Your relationship to the community',
options: [
{ id: 'resident', name: 'Resident' },
{ id: 'business', name: 'Business owner' },
{ id: 'worker', name: 'Work in the area' },
{ id: 'interested', name: 'Interested citizen' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select'
}, '1fr');
});
attendanceSection.addRow(row => {
row.addRadioButton('firstTime', {
label: 'Is this your first town hall meeting?',
options: [
{ id: 'yes', name: 'Yes, first time' },
{ id: 'few', name: 'Attended a few before' },
{ id: 'regular', name: 'Regular attendee' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 2: Overall Experience
// ============================================
const overallSection = form.addSubform('overallSection', {
title: 'Overall Experience',
customStyles: () => {
const rating = overallSection.starRating('overallRating')?.value();
if (rating !== null && rating !== undefined) {
if (rating >= 4) return { backgroundColor: '#dbeafe', padding: '16px', borderRadius: '8px', borderLeft: '4px solid #1e40af' };
if (rating >= 3) return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px', borderLeft: '4px solid #f59e0b' };
return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px', borderLeft: '4px solid #ef4444' };
}
return { padding: '16px', borderRadius: '8px', border: '1px dashed #93c5fd' };
}
});
overallSection.addRow(row => {
row.addStarRating('overallRating', {
label: 'How would you rate the overall town hall meeting?',
maxStars: 5,
size: 'xl',
alignment: 'center',
filledColor: '#1e40af',
showConfettiOnMax: true
});
});
overallSection.addRow(row => {
row.addEmojiRating('meetingMood', {
label: 'How did you feel about the meeting?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
// ============================================
// SECTION 3: Meeting Components Matrix
// ============================================
const componentsSection = form.addSubform('componentsSection', {
title: 'Meeting Quality',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null,
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #dbeafe' }
});
componentsSection.addRow(row => {
row.addMatrixQuestion('meetingAspects', {
label: 'Please rate the following aspects of the meeting:',
rows: [
{ id: 'organization', label: 'Meeting Organization', description: 'Start time, agenda, flow', isRequired: true },
{ id: 'speakers', label: 'Presenters & Speakers', description: 'Clarity and knowledge', isRequired: true },
{ id: 'topics', label: 'Topics Covered', description: 'Relevance to community needs', isRequired: true },
{ id: 'participation', label: 'Public Participation', description: 'Opportunities to ask questions', isRequired: false },
{ id: 'accessibility', label: 'Accessibility', description: 'Easy to attend and understand', isRequired: false }
],
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 4: Topic Relevance
// ============================================
const topicsSection = form.addSubform('topicsSection', {
title: 'Topic Relevance',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
topicsSection.addRow(row => {
row.addSlider('topicRelevance', {
label: 'How relevant were the topics to your concerns? (0-100%)',
min: 0,
max: 100,
step: 10,
showValue: true,
unit: '%',
defaultValue: 50
});
});
topicsSection.addRow(row => {
row.addRatingScale('informationQuality', {
preset: 'likert-5',
label: 'The information presented was clear and understandable',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
topicsSection.addRow(row => {
row.addRatingScale('questionsAnswered', {
preset: 'likert-5',
label: 'My questions and concerns were addressed',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
// ============================================
// SECTION 5: Virtual Experience (conditional)
// ============================================
const virtualSection = form.addSubform('virtualSection', {
title: 'Virtual Participation Experience',
isVisible: () => attendanceSection.dropdown('attendanceType')?.value() === 'virtual',
customStyles: { backgroundColor: '#eff6ff', padding: '16px', borderRadius: '8px' }
});
virtualSection.addRow(row => {
row.addMatrixQuestion('virtualAspects', {
label: 'Rate the virtual meeting experience:',
rows: [
{ id: 'audio', label: 'Audio Quality', isRequired: true },
{ id: 'video', label: 'Video Quality', isRequired: true },
{ id: 'chat', label: 'Chat/Q&A Features', isRequired: false },
{ id: 'ease', label: 'Ease of Joining', isRequired: false }
],
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 6: In-Person Experience (conditional)
// ============================================
const inPersonSection = form.addSubform('inPersonSection', {
title: 'Venue Experience',
isVisible: () => attendanceSection.dropdown('attendanceType')?.value() === 'in-person',
customStyles: { backgroundColor: '#eff6ff', padding: '16px', borderRadius: '8px' }
});
inPersonSection.addRow(row => {
row.addStarRating('venueRating', {
label: 'How would you rate the venue?',
maxStars: 5,
size: 'lg',
alignment: 'center',
filledColor: '#1e40af'
}, '1fr');
row.addStarRating('parkingRating', {
label: 'Parking/Transportation access',
maxStars: 5,
size: 'lg',
alignment: 'center',
filledColor: '#1e40af'
}, '1fr');
});
inPersonSection.addRow(row => {
row.addRatingScale('venueAccessibility', {
preset: 'likert-5',
label: 'The venue was accessible to all attendees',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
// ============================================
// SECTION 7: Civic Engagement
// ============================================
const engagementSection = form.addSubform('engagementSection', {
title: 'Civic Engagement',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
engagementSection.addRow(row => {
row.addRatingScale('voiceHeard', {
preset: 'likert-5',
label: 'I felt my voice was heard',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
engagementSection.addRow(row => {
row.addRatingScale('trustIncreased', {
preset: 'likert-5',
label: 'This meeting increased my trust in local government',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
engagementSection.addRow(row => {
row.addThumbRating('attendAgain', {
label: 'Would you attend future town halls?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
alignment: 'center',
size: 'lg'
}, '1fr');
row.addThumbRating('recommend', {
label: 'Would you encourage neighbors to attend?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
alignment: 'center',
size: 'lg'
}, '1fr');
});
// ============================================
// SECTION 8: Future Topics
// ============================================
const futureTopicsSection = form.addSubform('futureTopicsSection', {
title: 'Future Topics',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
futureTopicsSection.addRow(row => {
row.addSuggestionChips('wantedTopics', {
label: 'What topics would you like addressed in future meetings? (select up to 5)',
suggestions: [
{ id: 'infrastructure', name: 'Roads & Infrastructure' },
{ id: 'safety', name: 'Public Safety' },
{ id: 'zoning', name: 'Zoning & Development' },
{ id: 'environment', name: 'Environment & Parks' },
{ id: 'budget', name: 'Budget & Taxes' },
{ id: 'education', name: 'Schools & Education' },
{ id: 'housing', name: 'Housing & Affordability' },
{ id: 'transit', name: 'Public Transit' },
{ id: 'utilities', name: 'Utilities & Services' },
{ id: 'business', name: 'Local Business' }
],
max: 5,
alignment: 'center'
});
});
futureTopicsSection.addSpacer();
futureTopicsSection.addRow(row => {
row.addTextarea('specificTopics', {
label: 'Any specific issues you would like addressed?',
placeholder: 'Describe the topic or concern you would like to see discussed...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 9: General Comments
// ============================================
const commentsSection = form.addSubform('commentsSection', {
title: 'Additional Comments',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
commentsSection.addRow(row => {
row.addTextarea('suggestions', {
label: () => {
const rating = overallSection.starRating('overallRating')?.value();
if (rating !== null && rating !== undefined && rating >= 4) {
return 'What did you appreciate most about this meeting?';
}
return 'How can we improve future town hall meetings?';
},
placeholder: 'Share your thoughts...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 10: Summary
// ============================================
const summarySection = form.addSubform('summarySection', {
title: 'Feedback Summary',
isVisible: () => overallSection.starRating('overallRating')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summary', {
computedValue: () => {
const overall = overallSection.starRating('overallRating')?.value();
const mood = overallSection.emojiRating('meetingMood')?.value();
const attendance = attendanceSection.dropdown('attendanceType')?.value();
const topicRelevance = topicsSection.slider('topicRelevance')?.value();
const voiceHeard = engagementSection.ratingScale('voiceHeard')?.value();
const attendAgain = engagementSection.thumbRating('attendAgain')?.value();
const recommend = engagementSection.thumbRating('recommend')?.value();
const topics = futureTopicsSection.suggestionChips('wantedTopics')?.value() || [];
if (overall === null || overall === undefined) return '';
const moodLabels: Record<string, string> = {
'very-bad': 'Very Dissatisfied',
'bad': 'Dissatisfied',
'neutral': 'Neutral',
'good': 'Satisfied',
'excellent': 'Very Satisfied'
};
const attendanceLabels: Record<string, string> = {
'in-person': 'In-person',
'virtual': 'Virtual',
'recording': 'Recording'
};
let emoji = '';
if (overall >= 4) emoji = '🏛️';
else if (overall >= 3) emoji = '👍';
else emoji = '📝';
let summary = `${emoji} TOWN HALL FEEDBACK\n`;
summary += `${'═'.repeat(28)}\n\n`;
if (attendance) {
summary += `Attended: ${attendanceLabels[attendance]}\n`;
}
summary += `Overall: ${'★'.repeat(overall)}${'☆'.repeat(5 - overall)} (${overall}/5)\n`;
if (mood) {
summary += `Feeling: ${moodLabels[mood] || mood}\n`;
}
if (topicRelevance !== null && topicRelevance !== undefined) {
summary += `\nTopic Relevance: ${topicRelevance}%\n`;
}
if (voiceHeard !== null && voiceHeard !== undefined) {
const voiceLabels = ['', 'Not at all', 'Slightly', 'Somewhat', 'Mostly', 'Completely'];
summary += `Voice Heard: ${voiceLabels[voiceHeard] || voiceHeard}\n`;
}
if (topics.length > 0) {
summary += `\n📋 Topics Requested: ${topics.length}`;
}
if (attendAgain !== null || recommend !== null) {
summary += '\n\n';
if (attendAgain) {
summary += `Attend again: ${attendAgain === 'up' ? '✅ Yes' : '❌ No'}\n`;
}
if (recommend) {
summary += `Recommend: ${recommend === 'up' ? '✅ Yes' : '❌ No'}`;
}
}
return summary;
},
customStyles: () => {
const overall = overallSection.starRating('overallRating')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px'
};
if (overall !== null && overall !== undefined) {
if (overall >= 4) {
return { ...baseStyles, backgroundColor: '#dbeafe', borderLeft: '4px solid #1e40af' };
} else if (overall >= 3) {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
} else {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
}
return { ...baseStyles, backgroundColor: '#f8fafc', borderLeft: '4px solid #1e40af' };
}
});
});
// ============================================
// 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 input helps us make our town hall meetings more effective and responsive to community needs. We value your participation in local government.'
});
}