export function refereeOfficiating(form: FormTs) {
// Referee & Official Feedback Form
// Demonstrates: MatrixQuestion, StarRating, RatingScale, EmojiRating, Dropdown, conditional flows
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Referee & Official Feedback',
computedValue: () => 'Help us maintain fair play standards. Your feedback improves officiating quality.',
customStyles: {
backgroundColor: '#1e3a5f',
color: 'white',
padding: '24px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Match Information
// ============================================
const matchInfo = form.addSubform('matchInfo', {
title: 'Match Details'
});
matchInfo.addRow(row => {
row.addDropdown('sportType', {
label: 'Sport',
options: [
{ id: 'soccer', name: 'Soccer/Football' },
{ id: 'basketball', name: 'Basketball' },
{ id: 'volleyball', name: 'Volleyball' },
{ id: 'hockey', name: 'Hockey' },
{ id: 'rugby', name: 'Rugby' },
{ id: 'tennis', name: 'Tennis' },
{ id: 'baseball', name: 'Baseball/Softball' },
{ id: 'handball', name: 'Handball' },
{ id: 'other', name: 'Other' }
],
placeholder: 'Select sport',
isRequired: true
}, '1fr');
row.addDropdown('competitionLevel', {
label: 'Competition Level',
options: [
{ id: 'recreational', name: 'Recreational/Friendly' },
{ id: 'youth', name: 'Youth League' },
{ id: 'amateur', name: 'Amateur/Club Level' },
{ id: 'semi-pro', name: 'Semi-Professional' },
{ id: 'professional', name: 'Professional' },
{ id: 'tournament', name: 'Tournament/Cup' }
],
placeholder: 'Select level',
isRequired: true
}, '1fr');
});
matchInfo.addRow(row => {
row.addDatepicker('matchDate', {
label: 'Match Date',
isRequired: true
}, '1fr');
row.addDropdown('yourRole', {
label: 'Your Role',
options: [
{ id: 'coach', name: 'Coach' },
{ id: 'manager', name: 'Team Manager' },
{ id: 'player', name: 'Player' },
{ id: 'spectator', name: 'Spectator' },
{ id: 'league-official', name: 'League Official' }
],
isRequired: true
}, '1fr');
});
// ============================================
// SECTION 2: Overall Experience
// ============================================
const overallSection = form.addSubform('overallSection', {
title: 'Overall Officiating Experience',
isVisible: () => matchInfo.dropdown('sportType')?.value() !== null && matchInfo.dropdown('sportType')?.value() !== undefined
});
overallSection.addRow(row => {
row.addEmojiRating('overallFeeling', {
label: 'How would you describe the overall officiating experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addRatingScale('satisfactionScore', {
preset: 'satisfaction',
label: 'Overall Satisfaction with Officiating',
alignment: 'center',
showSegmentColors: false,
isRequired: true
});
});
// ============================================
// SECTION 3: Officiating Aspects Matrix
// ============================================
const aspectsSection = form.addSubform('aspectsSection', {
title: 'Officiating Performance',
isVisible: () => overallSection.ratingScale('satisfactionScore')?.value() !== null && overallSection.ratingScale('satisfactionScore')?.value() !== undefined
});
aspectsSection.addRow(row => {
row.addMatrixQuestion('officiatingMatrix', {
label: 'Please rate the following aspects of officiating:',
rows: [
{ id: 'rules', label: 'Rules Knowledge', description: 'Understanding of game rules', isRequired: true },
{ id: 'consistency', label: 'Consistency', description: 'Same calls for both teams', isRequired: true },
{ id: 'fairness', label: 'Fairness & Impartiality', description: 'No bias toward either team', isRequired: true },
{ id: 'positioning', label: 'Positioning', description: 'Being in the right place to make calls', isRequired: true },
{ id: 'communication', label: 'Communication', description: 'Clear signals and explanations', isRequired: true },
{ id: 'game-control', label: 'Game Control', description: 'Managing tempo and player conduct', isRequired: true }
],
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
});
});
// ============================================
// SECTION 4: Key Ratings
// ============================================
const ratingsSection = form.addSubform('ratingsSection', {
title: 'Key Performance Indicators',
isVisible: () => aspectsSection.matrixQuestion('officiatingMatrix')?.areAllRequiredRowsAnswered() === true,
customStyles: { backgroundColor: '#f0f9ff', padding: '20px', borderRadius: '10px' }
});
ratingsSection.addRow(row => {
row.addStarRating('fairnessRating', {
label: 'Fairness',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'center'
}, '1fr');
row.addStarRating('professionalismRating', {
label: 'Professionalism',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'center'
}, '1fr');
row.addStarRating('timelinessRating', {
label: 'Punctuality & Preparation',
maxStars: 5,
size: 'lg',
showCounter: true,
alignment: 'center'
}, '1fr');
});
// ============================================
// SECTION 5: Issues Reported (for low satisfaction)
// ============================================
const issuesSection = form.addSubform('issuesSection', {
title: 'Issues Encountered',
isVisible: () => {
const satisfaction = overallSection.ratingScale('satisfactionScore')?.value();
return satisfaction !== null && satisfaction !== undefined && satisfaction <= 3;
},
customStyles: { backgroundColor: '#fef2f2', padding: '20px', borderRadius: '10px' }
});
issuesSection.addRow(row => {
row.addCheckboxList('reportedIssues', {
label: 'What issues did you observe? (Select all that apply)',
options: [
{ id: 'missed-calls', name: 'Missed obvious calls' },
{ id: 'wrong-calls', name: 'Incorrect calls' },
{ id: 'inconsistent', name: 'Inconsistent application of rules' },
{ id: 'bias', name: 'Perceived bias' },
{ id: 'poor-positioning', name: 'Poor positioning' },
{ id: 'lost-control', name: 'Lost control of the game' },
{ id: 'poor-communication', name: 'Poor communication' },
{ id: 'unprofessional', name: 'Unprofessional behavior' },
{ id: 'late-arrival', name: 'Late arrival or poor preparation' }
],
orientation: 'vertical'
});
});
issuesSection.addSpacer({ height: '15px' });
issuesSection.addRow(row => {
row.addTextarea('issueDetails', {
label: 'Please describe the specific incidents:',
placeholder: 'Provide details about what happened, approximate game time, and how it affected the match...',
rows: 4,
autoExpand: true,
isVisible: () => (issuesSection.checkboxList('reportedIssues')?.value()?.length ?? 0) > 0
});
});
// ============================================
// SECTION 6: Positive Feedback (for high satisfaction)
// ============================================
const praiseSection = form.addSubform('praiseSection', {
title: 'What Went Well',
isVisible: () => {
const satisfaction = overallSection.ratingScale('satisfactionScore')?.value();
return satisfaction !== null && satisfaction !== undefined && satisfaction >= 4;
},
customStyles: { backgroundColor: '#f0fdf4', padding: '20px', borderRadius: '10px' }
});
praiseSection.addRow(row => {
row.addCheckboxList('positiveAspects', {
label: 'What did the official(s) do particularly well?',
options: [
{ id: 'fair', name: 'Fair and impartial calls' },
{ id: 'consistent', name: 'Consistent throughout the match' },
{ id: 'communicated', name: 'Communicated decisions clearly' },
{ id: 'calm', name: 'Remained calm under pressure' },
{ id: 'respected', name: 'Treated players with respect' },
{ id: 'flow', name: 'Let the game flow well' },
{ id: 'difficult-calls', name: 'Made difficult calls correctly' },
{ id: 'professional', name: 'Highly professional demeanor' }
],
orientation: 'vertical'
});
});
praiseSection.addRow(row => {
row.addTextarea('praiseComments', {
label: 'Additional positive feedback:',
placeholder: 'Share specific examples of excellent officiating...',
rows: 3,
autoExpand: true,
isVisible: () => (praiseSection.checkboxList('positiveAspects')?.value()?.length ?? 0) > 0
});
});
// ============================================
// SECTION 7: Final Assessment
// ============================================
const finalSection = form.addSubform('finalSection', {
title: 'Final Assessment',
isVisible: () => {
const matrix = aspectsSection.matrixQuestion('officiatingMatrix');
return matrix?.areAllRequiredRowsAnswered() === true;
}
});
finalSection.addRow(row => {
row.addThumbRating('wouldRecommend', {
label: 'Would you recommend this official for future matches at this level?',
showLabels: true,
upLabel: 'Yes, recommend',
downLabel: 'Needs improvement',
size: 'lg',
alignment: 'center'
});
});
finalSection.addSpacer({ height: '10px' });
finalSection.addRow(row => {
row.addTextarea('additionalComments', {
label: () => {
const recommend = finalSection.thumbRating('wouldRecommend')?.value();
if (recommend === 'up') return 'Any final words of praise or suggestions?';
if (recommend === 'down') return 'What specific improvements would you suggest?';
return 'Additional comments (optional):';
},
placeholder: 'Share any additional observations...',
rows: 3,
autoExpand: true
});
});
// ============================================
// SECTION 8: Optional Contact
// ============================================
const contactSection = form.addSubform('contactSection', {
title: 'Contact Information (Optional)',
isVisible: () => finalSection.thumbRating('wouldRecommend')?.value() !== null && finalSection.thumbRating('wouldRecommend')?.value() !== undefined
});
contactSection.addRow(row => {
row.addCheckbox('allowFollowUp', {
label: 'I am willing to be contacted for follow-up regarding this feedback'
});
});
contactSection.addRow(row => {
row.addEmail('contactEmail', {
label: 'Email address',
placeholder: 'your@email.com',
isVisible: () => contactSection.checkbox('allowFollowUp')?.value() === true,
isRequired: () => contactSection.checkbox('allowFollowUp')?.value() === true
});
});
// ============================================
// SECTION 9: Summary
// ============================================
const summarySection = form.addSubform('summarySection', {
title: 'Feedback Summary',
isVisible: () => finalSection.thumbRating('wouldRecommend')?.value() !== null && finalSection.thumbRating('wouldRecommend')?.value() !== undefined
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const sport = matchInfo.dropdown('sportType')?.value();
const level = matchInfo.dropdown('competitionLevel')?.value();
const feeling = overallSection.emojiRating('overallFeeling')?.value();
const satisfaction = overallSection.ratingScale('satisfactionScore')?.value();
const fairness = ratingsSection.starRating('fairnessRating')?.value();
const professionalism = ratingsSection.starRating('professionalismRating')?.value();
const recommend = finalSection.thumbRating('wouldRecommend')?.value();
if (!satisfaction) return '';
const sportLabels: Record<string, string> = {
'soccer': 'Soccer/Football', 'basketball': 'Basketball', 'volleyball': 'Volleyball',
'hockey': 'Hockey', 'rugby': 'Rugby', 'tennis': 'Tennis',
'baseball': 'Baseball/Softball', 'handball': 'Handball', 'other': 'Other'
};
const levelLabels: Record<string, string> = {
'recreational': 'Recreational', 'youth': 'Youth League', 'amateur': 'Amateur',
'semi-pro': 'Semi-Pro', 'professional': 'Professional', 'tournament': 'Tournament'
};
const feelingEmojis: Record<string, string> = {
'very-bad': '😡', 'bad': '😕', 'neutral': '😐', 'good': '🙂', 'excellent': '😍'
};
let emoji = satisfaction >= 4 ? '⚽' : satisfaction <= 2 ? '🚩' : '📋';
let summary = `${emoji} Officiating Feedback Summary\n`;
summary += `${'═'.repeat(30)}\n\n`;
if (sport) summary += `🏆 Sport: ${sportLabels[sport] || sport}\n`;
if (level) summary += `📊 Level: ${levelLabels[level] || level}\n`;
summary += `\n`;
summary += `📈 Satisfaction: ${satisfaction}/5\n`;
if (feeling) summary += `${feelingEmojis[feeling] || ''} Overall Feeling: ${feeling}\n`;
if (fairness) summary += `⚖️ Fairness: ${fairness}/5 stars\n`;
if (professionalism) summary += `👔 Professionalism: ${professionalism}/5 stars\n`;
summary += `\n${recommend === 'up' ? '✅ Recommended for future matches' : '⚠️ Needs improvement'}`;
return summary;
},
customStyles: () => {
const recommend = finalSection.thumbRating('wouldRecommend')?.value();
const baseStyles = {
padding: '20px',
borderRadius: '10px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (recommend === 'up') {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #059669' };
} else if (recommend === 'down') {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
return { ...baseStyles, backgroundColor: '#f1f5f9', borderLeft: '4px solid #64748b' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Feedback',
isVisible: () => finalSection.thumbRating('wouldRecommend')?.value() !== null && finalSection.thumbRating('wouldRecommend')?.value() !== undefined
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your input helps maintain high officiating standards and ensures fair play for everyone. We review all feedback to continuously improve the match experience.'
});
}