export function meetingFeedback(form: FormTs) {
// Meeting Effectiveness Feedback - Quick post-meeting survey
// Demonstrates: ThumbRating, EmojiRating, RatingScale, Slider, StarRating, dynamic styling
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Quick Meeting Feedback',
computedValue: () => 'Help us make meetings more valuable - takes 30 seconds',
customStyles: {
backgroundColor: '#8b5cf6',
color: 'white',
padding: '20px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Quick Value Assessment
// ============================================
const valueSection = form.addSubform('valueSection', {
title: 'Was This Meeting Valuable?',
customStyles: () => {
const thumb = valueSection.thumbRating('meetingValue')?.value();
if (thumb === 'up') return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (thumb === 'down') return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
return { padding: '16px', borderRadius: '8px', border: '1px dashed #8b5cf6' };
}
});
valueSection.addRow(row => {
row.addThumbRating('meetingValue', {
label: 'Was this meeting a good use of your time?',
showLabels: true,
upLabel: 'Yes, valuable',
downLabel: 'Not really',
alignment: 'center',
size: 'lg'
});
});
// Follow-up based on value rating
valueSection.addRow(row => {
row.addRadioButton('alternativeFormat', {
label: 'Could this have been handled differently?',
options: [
{ id: 'perfect', name: 'No, meeting was necessary' },
{ id: 'shorter', name: 'Yes, but shorter' },
{ id: 'email', name: 'Could have been an email/message' },
{ id: 'async', name: 'Async document/video would work' },
{ id: 'skip', name: 'Could have been skipped entirely' }
],
orientation: 'vertical',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() === 'down'
});
});
// ============================================
// SECTION 2: Meeting Quality Metrics
// ============================================
const qualitySection = form.addSubform('qualitySection', {
title: 'Meeting Quality',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() !== null
});
qualitySection.addRow(row => {
row.addEmojiRating('engagement', {
label: 'How engaged did you feel during this meeting?',
preset: 'mood',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
qualitySection.addRow(row => {
row.addRatingScale('hadAgenda', {
preset: 'likert-5',
label: 'The meeting had a clear agenda and purpose',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center',
size: 'sm'
});
});
qualitySection.addRow(row => {
row.addRatingScale('stayedOnTopic', {
preset: 'likert-5',
label: 'The discussion stayed focused on relevant topics',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center',
size: 'sm'
});
});
// ============================================
// SECTION 3: Time Assessment
// ============================================
const timeSection = form.addSubform('timeSection', {
title: 'Time Usage',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() !== null
});
timeSection.addRow(row => {
row.addSlider('actualDuration', {
label: 'How long was the meeting? (minutes)',
min: 5,
max: 120,
step: 5,
showValue: true,
unit: 'min',
defaultValue: 30
}, '1fr');
row.addSlider('optimalDuration', {
label: 'How long should it have been? (minutes)',
min: 0,
max: 120,
step: 5,
showValue: true,
unit: 'min',
defaultValue: 30
}, '1fr');
});
timeSection.addRow(row => {
row.addTextPanel('timeDiff', {
label: 'Time Efficiency',
computedValue: () => {
const actual = timeSection.slider('actualDuration')?.value();
const optimal = timeSection.slider('optimalDuration')?.value();
if (actual === null || actual === undefined || optimal === null || optimal === undefined) {
return '';
}
const diff = actual - optimal;
if (diff === 0) {
return '✅ Perfect timing!';
} else if (diff > 0) {
return `⏰ ${diff} minutes could be saved`;
} else {
return `📝 Could use ${-diff} more minutes`;
}
},
customStyles: () => {
const actual = timeSection.slider('actualDuration')?.value();
const optimal = timeSection.slider('optimalDuration')?.value();
if (actual === null || actual === undefined || optimal === null || optimal === undefined) {
return { display: 'none' };
}
const diff = actual - optimal;
if (diff === 0) {
return { backgroundColor: '#d1fae5', padding: '12px', borderRadius: '8px', textAlign: 'center', fontWeight: 'bold' };
} else if (diff > 0) {
return { backgroundColor: '#fef3c7', padding: '12px', borderRadius: '8px', textAlign: 'center', fontWeight: 'bold' };
}
return { backgroundColor: '#e0e7ff', padding: '12px', borderRadius: '8px', textAlign: 'center', fontWeight: 'bold' };
}
});
});
// ============================================
// SECTION 4: Participation & Outcomes
// ============================================
const outcomesSection = form.addSubform('outcomesSection', {
title: 'Participation & Outcomes',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() !== null
});
outcomesSection.addRow(row => {
row.addThumbRating('hadVoice', {
label: 'Did you have a chance to contribute?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
alignment: 'center',
size: 'md'
}, '1fr');
row.addThumbRating('necessaryAttendee', {
label: 'Did you need to be in this meeting?',
showLabels: true,
upLabel: 'Yes',
downLabel: 'No',
alignment: 'center',
size: 'md'
}, '1fr');
});
outcomesSection.addRow(row => {
row.addThumbRating('clearActions', {
label: 'Were action items and next steps clear?',
showLabels: true,
upLabel: 'Yes, clear',
downLabel: 'No, unclear',
alignment: 'center',
size: 'lg'
});
});
// Follow-up if actions unclear
outcomesSection.addRow(row => {
row.addTextarea('actionSuggestions', {
label: 'What was missing from the action items?',
placeholder: 'What decisions or next steps need clarification?',
rows: 2,
autoExpand: true,
isVisible: () => outcomesSection.thumbRating('clearActions')?.value() === 'down'
});
});
// ============================================
// SECTION 5: Quick Improvement Suggestions
// ============================================
const improvementSection = form.addSubform('improvementSection', {
title: 'Quick Improvements',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() === 'down'
});
improvementSection.addRow(row => {
row.addSuggestionChips('improvements', {
label: 'What would make this meeting better? (select up to 3)',
suggestions: [
{ id: 'agenda', name: 'Send agenda beforehand' },
{ id: 'shorter', name: 'Keep it shorter' },
{ id: 'fewer', name: 'Fewer attendees' },
{ id: 'focus', name: 'Stay on topic' },
{ id: 'prep', name: 'Better preparation' },
{ id: 'decisions', name: 'Make decisions' },
{ id: 'actions', name: 'Clear action items' },
{ id: 'cancel', name: 'Cancel if not needed' }
],
max: 3,
alignment: 'center'
});
});
// ============================================
// SECTION 6: Overall Meeting Rating
// ============================================
const ratingSection = form.addSubform('ratingSection', {
title: 'Overall Rating',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() !== null
});
ratingSection.addRow(row => {
row.addStarRating('overallRating', {
label: 'Rate this meeting overall',
maxStars: 5,
size: 'xl',
alignment: 'center',
showConfettiOnMax: true
});
});
ratingSection.addSpacer();
ratingSection.addRow(row => {
row.addTextarea('additionalFeedback', {
label: 'Any other feedback?',
placeholder: 'Optional: share anything else about the meeting...',
rows: 2,
autoExpand: true
});
});
// ============================================
// SECTION 7: Summary
// ============================================
const summarySection = form.addSubform('summarySection', {
title: 'Feedback Summary',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summary', {
computedValue: () => {
const value = valueSection.thumbRating('meetingValue')?.value();
const engagement = qualitySection.emojiRating('engagement')?.value();
const actual = timeSection.slider('actualDuration')?.value();
const optimal = timeSection.slider('optimalDuration')?.value();
const clearActions = outcomesSection.thumbRating('clearActions')?.value();
const overall = ratingSection.starRating('overallRating')?.value();
const improvements = improvementSection.suggestionChips('improvements')?.value() || [];
if (!value) return '';
const engagementLabels: Record<string, string> = {
'sad': '😢 Disengaged',
'down': '😔 Low energy',
'neutral': '😐 Neutral',
'happy': '😊 Engaged',
'excited': '🤩 Very engaged'
};
let emoji = value === 'up' ? '✅' : '❌';
let summary = `${emoji} MEETING FEEDBACK\n`;
summary += `${'═'.repeat(25)}\n\n`;
summary += `Valuable: ${value === 'up' ? 'Yes' : 'No'}\n`;
if (engagement) {
summary += `Engagement: ${engagementLabels[engagement] || engagement}\n`;
}
if (actual !== null && actual !== undefined && optimal !== null && optimal !== undefined) {
const diff = actual - optimal;
summary += `\nDuration: ${actual}min`;
if (diff !== 0) {
summary += ` (${diff > 0 ? '+' : ''}${diff}min vs optimal)`;
}
summary += '\n';
}
if (clearActions) {
summary += `Actions clear: ${clearActions === 'up' ? 'Yes' : 'No'}\n`;
}
if (improvements.length > 0) {
summary += `\n💡 Improvements: ${improvements.length} suggested`;
}
if (overall) {
summary += `\n\nOverall: ${'★'.repeat(overall)}${'☆'.repeat(5 - overall)}`;
}
return summary;
},
customStyles: () => {
const value = valueSection.thumbRating('meetingValue')?.value();
const overall = ratingSection.starRating('overallRating')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px'
};
if (value === 'up' || (overall && overall >= 4)) {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (value === 'down' || (overall && overall <= 2)) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#f3e8ff', borderLeft: '4px solid #8b5cf6' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => valueSection.thumbRating('meetingValue')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Thanks for the Quick Feedback!',
message: 'Your input helps us make meetings more productive. We review this feedback regularly to improve our meeting culture.'
});
}