export function subcontractorPerformanceSurvey(form: FormTs) {
// Subcontractor Performance Evaluation - Construction contractor assessment
// Demonstrates: MatrixQuestion, StarRating, PriceDisplay, Datepicker, Integer, Slider, computed values
// State for budget calculation
const originalBudget = form.state<number>(0);
const finalCost = form.state<number>(0);
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Subcontractor Performance Evaluation',
computedValue: () => 'Complete this evaluation to assess subcontractor performance. Objective ratings help maintain quality standards and inform future hiring decisions.',
customStyles: {
backgroundColor: '#b45309',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Project Information
// ============================================
const projectSection = form.addSubform('projectInfo', {
title: 'Project Information'
});
projectSection.addRow(row => {
row.addTextbox('projectName', {
label: 'Project name',
placeholder: 'e.g., Downtown Office Building',
isRequired: true
}, '1fr');
row.addTextbox('projectNumber', {
label: 'Project number/ID',
placeholder: 'e.g., PRJ-2024-001'
}, '1fr');
});
projectSection.addRow(row => {
row.addTextbox('companyName', {
label: 'Subcontractor company name',
placeholder: 'Enter company name',
isRequired: true
}, '1fr');
row.addDropdown('tradeType', {
label: 'Trade/specialty',
options: [
{ id: 'electrical', name: 'Electrical' },
{ id: 'plumbing', name: 'Plumbing' },
{ id: 'hvac', name: 'HVAC' },
{ id: 'framing', name: 'Framing/Carpentry' },
{ id: 'drywall', name: 'Drywall' },
{ id: 'roofing', name: 'Roofing' },
{ id: 'concrete', name: 'Concrete' },
{ id: 'masonry', name: 'Masonry' },
{ id: 'painting', name: 'Painting' },
{ id: 'flooring', name: 'Flooring' },
{ id: 'landscaping', name: 'Landscaping' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select trade...',
isRequired: true
}, '1fr');
});
projectSection.addRow(row => {
row.addDatepicker('startDate', {
label: 'Work start date',
isRequired: true
}, '1fr');
row.addDatepicker('endDate', {
label: 'Work completion date',
isRequired: true
}, '1fr');
});
// ============================================
// SECTION 2: Work Quality Assessment
// ============================================
const qualitySection = form.addSubform('qualityAssessment', {
title: 'Work Quality Assessment',
isVisible: () => projectSection.dropdown('tradeType')?.value() !== null
});
qualitySection.addRow(row => {
row.addMatrixQuestion('qualityMetrics', {
label: 'Rate the quality of work:',
rows: [
{ id: 'craftsmanship', label: 'Craftsmanship/workmanship', isRequired: true },
{ id: 'specifications', label: 'Adherence to specifications', isRequired: true },
{ id: 'materials', label: 'Material quality used', isRequired: true },
{ id: 'cleanliness', label: 'Jobsite cleanliness', isRequired: false },
{ id: 'punchlist', label: 'Punch list responsiveness', isRequired: false }
],
columns: [
{ id: '1', label: 'Poor' },
{ id: '2', label: 'Below Avg' },
{ id: '3', label: 'Average' },
{ id: '4', label: 'Good' },
{ id: '5', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
qualitySection.addRow(row => {
row.addStarRating('overallQuality', {
label: 'Overall work quality rating',
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true
});
});
// ============================================
// SECTION 3: Schedule & Timeliness
// ============================================
const scheduleSection = form.addSubform('scheduleAssessment', {
title: 'Schedule & Timeliness',
isVisible: () => projectSection.dropdown('tradeType')?.value() !== null
});
scheduleSection.addRow(row => {
row.addRadioButton('scheduleAdherence', {
label: 'Did the subcontractor complete work on schedule?',
options: [
{ id: 'early', name: 'Completed early' },
{ id: 'on-time', name: 'Completed on time' },
{ id: 'minor-delay', name: 'Minor delays (1-3 days)' },
{ id: 'significant-delay', name: 'Significant delays (4+ days)' }
],
orientation: 'vertical',
isRequired: true
});
});
scheduleSection.addRow(row => {
row.addInteger('delayDays', {
label: 'Number of days delayed',
min: 0,
max: 365,
placeholder: 'Enter days',
isVisible: () => {
const adherence = scheduleSection.radioButton('scheduleAdherence')?.value();
return adherence === 'minor-delay' || adherence === 'significant-delay';
},
isRequired: () => {
const adherence = scheduleSection.radioButton('scheduleAdherence')?.value();
return adherence === 'minor-delay' || adherence === 'significant-delay';
}
}, '200px');
});
scheduleSection.addRow(row => {
row.addCheckboxList('delayReasons', {
label: 'Reasons for delay (if applicable)',
options: [
{ id: 'weather', name: 'Weather conditions' },
{ id: 'materials', name: 'Material availability' },
{ id: 'labor', name: 'Labor shortage' },
{ id: 'coordination', name: 'Coordination with other trades' },
{ id: 'changes', name: 'Design changes/RFIs' },
{ id: 'subcontractor', name: 'Subcontractor performance' },
{ id: 'other', name: 'Other factors' }
],
orientation: 'vertical',
isVisible: () => {
const adherence = scheduleSection.radioButton('scheduleAdherence')?.value();
return adherence === 'minor-delay' || adherence === 'significant-delay';
}
});
});
scheduleSection.addRow(row => {
row.addStarRating('scheduleRating', {
label: 'Overall schedule adherence rating',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
// ============================================
// SECTION 4: Budget & Cost Management
// ============================================
const budgetSection = form.addSubform('budgetAssessment', {
title: 'Budget & Cost Management',
isVisible: () => projectSection.dropdown('tradeType')?.value() !== null
});
budgetSection.addRow(row => {
row.addMoney('originalBudget', {
label: 'Original contract value',
currency: '$',
placeholder: 'Enter amount',
onValueChange: (value) => originalBudget.set(value ?? 0)
}, '1fr');
row.addMoney('finalCost', {
label: 'Final cost (including change orders)',
currency: '$',
placeholder: 'Enter amount',
onValueChange: (value) => finalCost.set(value ?? 0)
}, '1fr');
});
budgetSection.addRow(row => {
row.addPriceDisplay('budgetVariance', {
label: 'Budget variance',
computedValue: () => {
const original = originalBudget();
const final = finalCost();
if (original === 0 || final === 0) return null;
return final - original;
},
currency: '$',
prefix: () => {
const variance = finalCost() - originalBudget();
if (variance > 0) return 'Over budget: +';
if (variance < 0) return 'Under budget: ';
return 'On budget: ';
},
variant: () => {
const variance = finalCost() - originalBudget();
if (variance > 0) return 'default';
if (variance < 0) return 'success';
return 'highlight';
},
isVisible: () => originalBudget() > 0 && finalCost() > 0
});
});
budgetSection.addRow(row => {
row.addRadioButton('changeOrderHandling', {
label: 'How were change orders handled?',
options: [
{ id: 'excellent', name: 'Proactive, fair pricing, minimal disputes' },
{ id: 'good', name: 'Responsive, reasonable pricing' },
{ id: 'average', name: 'Adequate, some negotiation needed' },
{ id: 'poor', name: 'Slow response, inflated pricing' },
{ id: 'na', name: 'No change orders' }
],
orientation: 'vertical'
});
});
// ============================================
// SECTION 5: Safety & Compliance
// ============================================
const safetySection = form.addSubform('safetyAssessment', {
title: 'Safety & Compliance',
isVisible: () => projectSection.dropdown('tradeType')?.value() !== null
});
safetySection.addRow(row => {
row.addMatrixQuestion('safetyMetrics', {
label: 'Rate safety performance:',
rows: [
{ id: 'ppe', label: 'PPE compliance', isRequired: true },
{ id: 'practices', label: 'Safe work practices', isRequired: true },
{ id: 'housekeeping', label: 'Housekeeping/organization', isRequired: false },
{ id: 'communication', label: 'Safety communication', isRequired: false },
{ id: 'documentation', label: 'Safety documentation', isRequired: false }
],
columns: [
{ id: '1', label: 'Poor' },
{ id: '2', label: 'Below Avg' },
{ id: '3', label: 'Average' },
{ id: '4', label: 'Good' },
{ id: '5', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
safetySection.addRow(row => {
row.addRadioButton('safetyIncidents', {
label: 'Were there any safety incidents?',
options: [
{ id: 'none', name: 'No incidents' },
{ id: 'near-miss', name: 'Near-miss(es) only' },
{ id: 'minor', name: 'Minor incident(s)' },
{ id: 'recordable', name: 'OSHA recordable incident(s)' }
],
orientation: 'horizontal',
isRequired: true
});
});
safetySection.addRow(row => {
row.addTextarea('incidentDetails', {
label: 'Describe safety incidents (if any):',
placeholder: 'Provide details of any safety incidents or near-misses...',
rows: 3,
isVisible: () => {
const incidents = safetySection.radioButton('safetyIncidents')?.value();
return incidents !== 'none' && incidents !== null;
}
});
});
// ============================================
// SECTION 6: Communication & Professionalism
// ============================================
const commSection = form.addSubform('communicationAssessment', {
title: 'Communication & Professionalism',
isVisible: () => projectSection.dropdown('tradeType')?.value() !== null
});
commSection.addRow(row => {
row.addStarRating('communication', {
label: 'Communication & responsiveness',
maxStars: 5,
size: 'lg',
alignment: 'left'
}, '1fr');
row.addStarRating('professionalism', {
label: 'Professionalism & attitude',
maxStars: 5,
size: 'lg',
alignment: 'left'
}, '1fr');
});
commSection.addRow(row => {
row.addStarRating('coordination', {
label: 'Coordination with other trades',
maxStars: 5,
size: 'lg',
alignment: 'left'
}, '1fr');
row.addStarRating('problemSolving', {
label: 'Problem-solving ability',
maxStars: 5,
size: 'lg',
alignment: 'left'
}, '1fr');
});
// ============================================
// SECTION 7: Overall Evaluation
// ============================================
const overallSection = form.addSubform('overallEvaluation', {
title: 'Overall Evaluation',
isVisible: () => qualitySection.starRating('overallQuality')?.value() !== null
});
overallSection.addRow(row => {
row.addRatingScale('overallScore', {
preset: 'nps',
label: 'How likely are you to hire this subcontractor again?',
showCategoryLabel: true,
showSegmentColors: true,
showConfettiOnPromoter: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRadioButton('recommendation', {
label: 'Would you recommend this subcontractor to other project managers?',
options: [
{ id: 'highly', name: 'Highly recommend' },
{ id: 'recommend', name: 'Recommend with reservations' },
{ id: 'neutral', name: 'Neutral' },
{ id: 'not-recommend', name: 'Would not recommend' }
],
orientation: 'vertical'
});
});
overallSection.addSpacer();
overallSection.addRow(row => {
row.addTextarea('additionalComments', {
label: () => {
const nps = overallSection.ratingScale('overallScore')?.npsCategory();
if (nps === 'detractor') {
return 'What could the subcontractor have done better?';
} else if (nps === 'promoter') {
return 'What made this subcontractor stand out?';
}
return 'Additional comments or feedback:';
},
placeholder: 'Provide any additional feedback about this subcontractor...',
rows: 4,
autoExpand: true
});
});
// ============================================
// SECTION 8: Summary
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Evaluation Summary',
isVisible: () => overallSection.ratingScale('overallScore')?.value() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const companyName = projectSection.textbox('companyName')?.value();
const projectName = projectSection.textbox('projectName')?.value();
const trade = projectSection.dropdown('tradeType')?.value();
const qualityRating = qualitySection.starRating('overallQuality')?.value();
const scheduleRating = scheduleSection.starRating('scheduleRating')?.value();
const overallScore = overallSection.ratingScale('overallScore')?.value();
const recommendation = overallSection.radioButton('recommendation')?.value();
const variance = finalCost() - originalBudget();
if (!companyName || overallScore === null || overallScore === undefined) return '';
const tradeNames: Record<string, string> = {
'electrical': 'Electrical', 'plumbing': 'Plumbing', 'hvac': 'HVAC',
'framing': 'Framing', 'drywall': 'Drywall', 'roofing': 'Roofing',
'concrete': 'Concrete', 'masonry': 'Masonry', 'painting': 'Painting',
'flooring': 'Flooring', 'landscaping': 'Landscaping', 'other': 'Other'
};
const recommendLabels: Record<string, string> = {
'highly': 'Highly Recommended',
'recommend': 'Recommended',
'neutral': 'Neutral',
'not-recommend': 'Not Recommended'
};
let summary = 'Performance Summary\n';
summary += '═'.repeat(25) + '\n\n';
summary += `Subcontractor: ${companyName}\n`;
summary += `Project: ${projectName || 'N/A'}\n`;
summary += `Trade: ${tradeNames[trade ?? ''] || trade}\n\n`;
if (qualityRating) {
summary += `Quality: ${'★'.repeat(qualityRating)}${'☆'.repeat(5 - qualityRating)}\n`;
}
if (scheduleRating) {
summary += `Schedule: ${'★'.repeat(scheduleRating)}${'☆'.repeat(5 - scheduleRating)}\n`;
}
if (originalBudget() > 0 && finalCost() > 0) {
const variancePercent = ((variance / originalBudget()) * 100).toFixed(1);
summary += `Budget: ${variance >= 0 ? '+' : ''}$${variance.toLocaleString()} (${variance >= 0 ? '+' : ''}${variancePercent}%)\n`;
}
summary += `\nRehire Score: ${overallScore}/10`;
const category = overallSection.ratingScale('overallScore')?.npsCategory();
if (category) {
summary += ` (${category})`;
}
if (recommendation) {
summary += `\nVerdict: ${recommendLabels[recommendation] || recommendation}`;
}
return summary;
},
customStyles: () => {
const category = overallSection.ratingScale('overallScore')?.npsCategory();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (category === 'promoter') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (category === 'passive') {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
} else if (category === 'detractor') {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#f3f4f6', borderLeft: '4px solid #9ca3af' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Evaluation',
isVisible: () => projectSection.dropdown('tradeType')?.value() !== null
});
form.configureCompletionScreen({
type: 'text',
title: 'Evaluation Submitted',
message: 'Thank you for completing this subcontractor performance evaluation. Your feedback helps maintain quality standards and supports informed vendor selection for future projects.'
});
}