export function projectFeedback(form: FormTs) {
// Project Completion Review - B2B/Agency project feedback
// Demonstrates: MatrixQuestion, StarRating, RatingScale, EmojiRating, SuggestionChips, Pages, dynamic labels
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Project Completion Review',
computedValue: () => 'Help us improve our services with your feedback',
customStyles: {
backgroundColor: '#059669',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// MULTI-PAGE STRUCTURE
// ============================================
const pages = form.addPages('projectPages', {
heightMode: 'current-page'
});
// ============================================
// PAGE 1: Project Overview & Deliverables
// ============================================
const page1 = pages.addPage('deliverables');
page1.addRow(row => {
row.addTextPanel('page1Title', {
label: 'Page 1 of 3: Project & Deliverables',
customStyles: {
backgroundColor: '#d1fae5',
padding: '12px 16px',
borderRadius: '8px',
fontWeight: 'bold'
}
});
});
// Project info
const projectInfo = page1.addSubform('projectInfo', {
title: 'Project Information',
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' }
});
projectInfo.addRow(row => {
row.addTextbox('projectName', {
label: 'Project Name',
placeholder: 'e.g., Website Redesign, Marketing Campaign',
isRequired: true
}, '1fr');
row.addDropdown('projectType', {
label: 'Project Type',
options: [
{ id: 'development', name: 'Development/Engineering' },
{ id: 'design', name: 'Design/Creative' },
{ id: 'marketing', name: 'Marketing/Campaign' },
{ id: 'consulting', name: 'Consulting/Advisory' },
{ id: 'implementation', name: 'Implementation/Setup' },
{ id: 'strategy', name: 'Strategy/Planning' },
{ id: 'research', name: 'Research/Analysis' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select type'
}, '1fr');
});
// Overall impression
const overallSection = page1.addSubform('overallSection', {
title: 'Overall Satisfaction',
customStyles: () => {
const rating = overallSection.starRating('overallSatisfaction')?.value();
if (rating !== null && rating !== undefined) {
if (rating >= 4) return { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px' };
if (rating >= 3) return { backgroundColor: '#fef3c7', padding: '16px', borderRadius: '8px' };
return { backgroundColor: '#fee2e2', padding: '16px', borderRadius: '8px' };
}
return { padding: '16px', borderRadius: '8px', border: '1px dashed #059669' };
}
});
overallSection.addRow(row => {
row.addStarRating('overallSatisfaction', {
label: 'Overall project satisfaction',
maxStars: 5,
size: 'xl',
alignment: 'center',
showConfettiOnMax: true
});
});
overallSection.addRow(row => {
row.addEmojiRating('projectMood', {
label: 'How do you feel about the completed project?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
// Deliverables rating
const deliverablesSection = page1.addSubform('deliverablesSection', {
title: 'Deliverable Quality',
isVisible: () => overallSection.starRating('overallSatisfaction')?.value() !== null
});
deliverablesSection.addRow(row => {
row.addMatrixQuestion('deliverableRatings', {
label: 'Rate the quality of deliverables:',
rows: [
{ id: 'quality', label: 'Overall Quality', description: 'Meets professional standards', isRequired: true },
{ id: 'requirements', label: 'Requirements Met', description: 'Matched what was agreed', isRequired: true },
{ id: 'attention', label: 'Attention to Detail', description: 'Thoroughness and polish', isRequired: false },
{ id: 'innovation', label: 'Innovation/Creativity', description: 'Fresh ideas and solutions', isRequired: false },
{ id: 'documentation', label: 'Documentation', description: 'Clarity of handover materials', isRequired: false }
],
columns: [
{ id: 'na', label: 'N/A' },
{ 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
});
});
page1.addRow(row => {
row.addEmpty('1fr');
row.addButton('next1', {
label: 'Next: Process & Communication',
onClick: () => pages.goToPage('process')
});
});
// ============================================
// PAGE 2: Process & Communication
// ============================================
const page2 = pages.addPage('process');
page2.addRow(row => {
row.addTextPanel('page2Title', {
label: 'Page 2 of 3: Process & Communication',
customStyles: {
backgroundColor: '#d1fae5',
padding: '12px 16px',
borderRadius: '8px',
fontWeight: 'bold'
}
});
});
// Timeline section
const timelineSection = page2.addSubform('timelineSection', {
title: 'Timeline & Milestones',
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' }
});
timelineSection.addRow(row => {
row.addRatingScale('onTime', {
preset: 'likert-5',
label: 'The project was delivered on time',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
timelineSection.addRow(row => {
row.addRadioButton('timelineActual', {
label: 'How did the actual timeline compare to the estimate?',
options: [
{ id: 'early', name: 'Delivered early' },
{ id: 'on-time', name: 'On schedule' },
{ id: 'slightly-late', name: 'Slightly delayed (acceptable)' },
{ id: 'late', name: 'Significantly delayed' }
],
orientation: 'horizontal'
});
});
// Delays follow-up
timelineSection.addRow(row => {
row.addRadioButton('delayHandling', {
label: 'How well were delays communicated and managed?',
options: [
{ id: 'excellent', name: 'Proactively communicated, well managed' },
{ id: 'acceptable', name: 'Communicated when asked, reasonably managed' },
{ id: 'poor', name: 'Poor communication about delays' }
],
orientation: 'vertical',
isVisible: () => {
const timeline = timelineSection.radioButton('timelineActual')?.value();
return timeline === 'slightly-late' || timeline === 'late';
}
});
});
// Communication section
const commSection = page2.addSubform('commSection', {
title: 'Communication & Collaboration'
});
commSection.addRow(row => {
row.addMatrixQuestion('communicationRatings', {
label: 'Rate communication throughout the project:',
rows: [
{ id: 'responsiveness', label: 'Responsiveness', description: 'Timely replies to questions', isRequired: true },
{ id: 'clarity', label: 'Clarity', description: 'Clear and understandable communication', isRequired: true },
{ id: 'proactive', label: 'Proactive Updates', description: 'Kept you informed without asking', isRequired: false },
{ id: 'listening', label: 'Listening', description: 'Understood your needs', isRequired: true },
{ id: 'availability', label: 'Availability', description: 'Easy to reach when needed', 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
});
});
commSection.addRow(row => {
row.addStarRating('overallCommunication', {
label: 'Overall communication rating',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
// Team section
const teamSection = page2.addSubform('teamSection', {
title: 'Team & Expertise'
});
teamSection.addRow(row => {
row.addStarRating('teamExpertise', {
label: 'Team knowledge and expertise',
maxStars: 5,
size: 'lg',
alignment: 'center'
}, '1fr');
row.addStarRating('teamProfessionalism', {
label: 'Team professionalism',
maxStars: 5,
size: 'lg',
alignment: 'center'
}, '1fr');
});
page2.addRow(row => {
row.addButton('back2', {
label: 'Back',
onClick: () => pages.goToPage('deliverables')
});
row.addEmpty('1fr');
row.addButton('next2', {
label: 'Next: Value & Future',
onClick: () => pages.goToPage('value')
});
});
// ============================================
// PAGE 3: Value & Future Partnership
// ============================================
const page3 = pages.addPage('value');
page3.addRow(row => {
row.addTextPanel('page3Title', {
label: 'Page 3 of 3: Value & Future Partnership',
customStyles: {
backgroundColor: '#d1fae5',
padding: '12px 16px',
borderRadius: '8px',
fontWeight: 'bold'
}
});
});
// Value section
const valueSection = page3.addSubform('valueSection', {
title: 'Value Delivered',
customStyles: { padding: '16px', borderRadius: '8px', border: '1px solid #e2e8f0' }
});
valueSection.addRow(row => {
row.addRatingScale('valueForInvestment', {
preset: 'likert-5',
label: 'The project delivered good value for the investment',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
valueSection.addRow(row => {
row.addRatingScale('metGoals', {
preset: 'likert-5',
label: 'The project met our business goals/objectives',
lowLabel: 'Strongly Disagree',
highLabel: 'Strongly Agree',
alignment: 'center'
});
});
valueSection.addRow(row => {
row.addRadioButton('budgetAdherence', {
label: 'How did the final cost compare to the estimate?',
options: [
{ id: 'under', name: 'Under budget' },
{ id: 'on', name: 'On budget' },
{ id: 'slightly-over', name: 'Slightly over (10-20%)' },
{ id: 'over', name: 'Significantly over (>20%)' }
],
orientation: 'horizontal'
});
});
// Strengths and improvements
const strengthsSection = page3.addSubform('strengthsSection', {
title: 'What We Did Well'
});
strengthsSection.addRow(row => {
row.addSuggestionChips('projectStrengths', {
label: 'What stood out positively? (select up to 4)',
suggestions: [
{ id: 'quality', name: 'Quality of work' },
{ id: 'communication', name: 'Communication' },
{ id: 'timeline', name: 'Met deadlines' },
{ id: 'flexibility', name: 'Flexibility' },
{ id: 'expertise', name: 'Expertise' },
{ id: 'creative', name: 'Creative solutions' },
{ id: 'responsive', name: 'Responsiveness' },
{ id: 'proactive', name: 'Proactive approach' },
{ id: 'budget', name: 'Budget management' },
{ id: 'professional', name: 'Professionalism' }
],
max: 4,
alignment: 'center'
});
});
const improvementsSection = page3.addSubform('improvementsSection', {
title: 'Areas for Improvement',
isVisible: () => {
const overall = overallSection.starRating('overallSatisfaction')?.value();
return overall !== null && overall !== undefined && overall <= 4;
}
});
improvementsSection.addRow(row => {
row.addSuggestionChips('improvementAreas', {
label: 'What could we improve? (select all that apply)',
suggestions: [
{ id: 'communication', name: 'Communication' },
{ id: 'timeline', name: 'Timeline management' },
{ id: 'scope', name: 'Scope management' },
{ id: 'quality', name: 'Quality control' },
{ id: 'documentation', name: 'Documentation' },
{ id: 'responsiveness', name: 'Responsiveness' },
{ id: 'budget', name: 'Budget adherence' },
{ id: 'expectations', name: 'Setting expectations' }
],
alignment: 'center'
});
});
// NPS and future work
const futureSection = page3.addSubform('futureSection', {
title: 'Future Partnership',
customStyles: () => {
const nps = futureSection.ratingScale('projectNPS')?.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' };
}
});
futureSection.addRow(row => {
row.addRatingScale('projectNPS', {
preset: 'nps',
label: 'How likely are you to recommend us to colleagues?',
showSegmentColors: true,
showCategoryLabel: true,
showConfettiOnPromoter: true,
alignment: 'center'
});
});
futureSection.addRow(row => {
row.addThumbRating('workAgain', {
label: 'Would you work with us again on future projects?',
showLabels: true,
upLabel: 'Yes, definitely',
downLabel: 'Probably not',
alignment: 'center',
size: 'lg'
});
});
// Testimonial permission
const testimonialSection = page3.addSubform('testimonialSection', {
isVisible: () => {
const nps = futureSection.ratingScale('projectNPS')?.npsCategory();
return nps === 'promoter';
},
customStyles: { backgroundColor: '#d1fae5', padding: '16px', borderRadius: '8px', marginTop: '12px' }
});
testimonialSection.addRow(row => {
row.addCheckbox('testimonialPermission', {
label: 'You may use my feedback as a testimonial (with approval)'
});
});
testimonialSection.addRow(row => {
row.addTextarea('testimonialQuote', {
label: 'Would you like to share a brief testimonial?',
placeholder: 'Share your experience in a few sentences...',
rows: 2,
autoExpand: true,
isVisible: () => testimonialSection.checkbox('testimonialPermission')?.value() === true
});
});
// Final comments
const finalSection = page3.addSubform('finalSection', {
title: 'Final Thoughts'
});
finalSection.addSpacer();
finalSection.addRow(row => {
row.addTextarea('finalComments', {
label: 'Any other feedback or suggestions?',
placeholder: 'Share anything else about your experience...',
rows: 3,
autoExpand: true
});
});
// Summary
const summarySection = page3.addSubform('summarySection', {
title: 'Feedback Summary'
});
summarySection.addRow(row => {
row.addTextPanel('summary', {
computedValue: () => {
const projectName = projectInfo.textbox('projectName')?.value();
const overall = overallSection.starRating('overallSatisfaction')?.value();
const communication = commSection.starRating('overallCommunication')?.value();
const nps = futureSection.ratingScale('projectNPS')?.npsCategory();
const workAgain = futureSection.thumbRating('workAgain')?.value();
const timeline = timelineSection.radioButton('timelineActual')?.value();
const budget = valueSection.radioButton('budgetAdherence')?.value();
const strengths = strengthsSection.suggestionChips('projectStrengths')?.value() || [];
if (!overall) return '';
const timelineLabels: Record<string, string> = {
'early': '✅ Early',
'on-time': '✅ On time',
'slightly-late': '⚠️ Slightly late',
'late': '❌ Delayed'
};
const budgetLabels: Record<string, string> = {
'under': '✅ Under',
'on': '✅ On budget',
'slightly-over': '⚠️ Slightly over',
'over': '❌ Over budget'
};
let emoji = overall >= 4 ? '🎉' : overall >= 3 ? '🤔' : '😟';
let summary = `${emoji} PROJECT REVIEW\n`;
summary += `${'═'.repeat(28)}\n\n`;
if (projectName) {
summary += `Project: ${projectName}\n`;
}
summary += `\nOverall: ${'★'.repeat(overall)}${'☆'.repeat(5 - overall)} (${overall}/5)\n`;
if (communication) {
summary += `Communication: ${'★'.repeat(communication)}${'☆'.repeat(5 - communication)}\n`;
}
if (timeline) {
summary += `\nTimeline: ${timelineLabels[timeline]}`;
}
if (budget) {
summary += `\nBudget: ${budgetLabels[budget]}`;
}
if (strengths.length > 0) {
summary += `\n\n✨ Strengths: ${strengths.length} highlighted`;
}
if (nps) {
summary += `\n\nNPS: ${nps.charAt(0).toUpperCase() + nps.slice(1)}`;
}
if (workAgain) {
summary += `\nWork again: ${workAgain === 'up' ? '👍 Yes' : '👎 No'}`;
}
return summary;
},
customStyles: () => {
const nps = futureSection.ratingScale('projectNPS')?.npsCategory();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px'
};
if (nps === 'promoter') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (nps === 'passive') {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
} else if (nps === 'detractor') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#ecfdf5', borderLeft: '4px solid #059669' };
}
});
});
page3.addRow(row => {
row.addButton('back3', {
label: 'Back',
onClick: () => pages.goToPage('process')
});
row.addEmpty('1fr');
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Review',
isVisible: () => {
const currentPage = pages.currentPageIndex();
return currentPage === 2; // Show only on last page (0-indexed)
}
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your detailed review helps us continuously improve our services. We truly value our partnership and look forward to working together on future projects.'
});
}