export function movingServiceSurvey(form: FormTs) {
// Moving Service Feedback Form - Comprehensive moving company review
// Demonstrates: MatrixQuestion, StarRating, Slider, EmojiRating, RatingScale (NPS), conditional damage section
// ============================================
// STATE
// ============================================
const hasDamage = form.state<boolean>(false);
const overallRating = form.state<number | null>(null);
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Moving Service Feedback',
computedValue: () => "Help us improve by sharing your moving experience.",
customStyles: {
background: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// SECTION 1: Move Details
// ============================================
const detailsSection = form.addSubform('details', {
title: 'About Your Move'
});
detailsSection.addRow(row => {
row.addRadioButton('moveType', {
label: 'What type of move was this?',
options: [
{ id: 'local', name: 'Local (same city/area)' },
{ id: 'long', name: 'Long distance (different city)' },
{ id: 'interstate', name: 'Interstate/Cross-country' },
{ id: 'international', name: 'International' }
],
orientation: 'horizontal',
isRequired: true
});
});
detailsSection.addRow(row => {
row.addDropdown('homeSize', {
label: 'Size of home moved:',
options: [
{ id: 'studio', name: 'Studio/1 bedroom' },
{ id: '2bed', name: '2-3 bedrooms' },
{ id: '4bed', name: '4+ bedrooms' },
{ id: 'office', name: 'Office/Commercial' }
],
placeholder: 'Select size...'
}, '1fr');
row.addDatepicker('moveDate', {
label: 'Move date:'
}, '1fr');
});
detailsSection.addRow(row => {
row.addCheckboxList('servicesUsed', {
label: 'Which services did you use?',
options: [
{ id: 'packing', name: 'Packing service' },
{ id: 'unpacking', name: 'Unpacking service' },
{ id: 'storage', name: 'Storage' },
{ id: 'specialty', name: 'Specialty items (piano, art, etc.)' },
{ id: 'furniture', name: 'Furniture disassembly/assembly' },
{ id: 'basic', name: 'Basic loading/unloading only' }
],
orientation: 'vertical'
});
});
// ============================================
// SECTION 2: Pre-Move Experience
// ============================================
const preMoveSection = form.addSubform('preMove', {
title: 'Before the Move'
});
preMoveSection.addRow(row => {
row.addStarRating('quoteProcess', {
label: 'Rate the quote/estimate process',
maxStars: 5,
size: 'lg',
alignment: 'center'
});
});
preMoveSection.addRow(row => {
row.addSlider('quoteAccuracy', {
label: 'How accurate was the quote compared to final cost?',
min: 1,
max: 5,
step: 1,
defaultValue: 3,
showValue: false
});
});
preMoveSection.addRow(row => {
row.addTextPanel('quoteLabel', {
computedValue: () => {
const val = preMoveSection.slider('quoteAccuracy')?.value() ?? 3;
const labels = ['', 'Final cost much higher', 'Somewhat higher', 'About right', 'Somewhat lower', 'Final cost lower'];
return labels[val] || '';
},
customStyles: () => {
const val = preMoveSection.slider('quoteAccuracy')?.value() ?? 3;
const baseStyles = { textAlign: 'center', padding: '8px', borderRadius: '6px', fontWeight: '500' };
if (val >= 4) return { ...baseStyles, backgroundColor: '#d1fae5', color: '#065f46' };
if (val === 3) return { ...baseStyles, backgroundColor: '#e0f2fe', color: '#0369a1' };
return { ...baseStyles, backgroundColor: '#fee2e2', color: '#991b1b' };
}
});
});
preMoveSection.addRow(row => {
row.addStarRating('communication', {
label: 'Rate pre-move communication',
maxStars: 5,
size: 'lg',
alignment: 'center',
tooltip: 'Responsiveness, clarity, and helpfulness'
});
});
// ============================================
// SECTION 3: Moving Crew Evaluation
// ============================================
const crewSection = form.addSubform('crew', {
title: 'Moving Crew Performance'
});
crewSection.addRow(row => {
row.addMatrixQuestion('crewRatings', {
label: 'Rate your moving crew:',
rows: [
{ id: 'punctuality', label: 'Punctuality (arrived on time)', isRequired: true },
{ id: 'professionalism', label: 'Professionalism & attitude' },
{ id: 'efficiency', label: 'Efficiency & speed' },
{ id: 'care', label: 'Care with belongings' },
{ id: 'communication', label: 'Communication during move' },
{ id: 'cleanup', label: 'Cleanup after loading/unloading' }
],
columns: [
{ id: 'poor', label: 'Poor' },
{ id: 'fair', label: 'Fair' },
{ id: 'good', label: 'Good' },
{ id: 'excellent', label: 'Excellent' }
],
striped: true,
fullWidth: true
});
});
crewSection.addRow(row => {
row.addStarRating('crewOverall', {
label: 'Overall crew rating',
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true
});
});
crewSection.addSpacer({ height: '16px' });
crewSection.addRow(row => {
row.addTextarea('crewComments', {
label: 'Any specific feedback about the crew?',
placeholder: 'Shoutouts for exceptional crew members, or areas for improvement...',
rows: 2,
autoExpand: true
});
});
// ============================================
// SECTION 4: Item Condition
// ============================================
const conditionSection = form.addSubform('condition', {
title: 'Condition of Your Belongings'
});
conditionSection.addRow(row => {
row.addRadioButton('damageReport', {
label: 'Were any items damaged during the move?',
options: [
{ id: 'none', name: 'No damage - everything arrived in great condition' },
{ id: 'minor', name: 'Minor damage (scratches, small dents)' },
{ id: 'moderate', name: 'Moderate damage (noticeable but repairable)' },
{ id: 'major', name: 'Major damage (items broken or destroyed)' }
],
orientation: 'vertical',
onValueChange: (val) => hasDamage.set(val !== 'none' && val !== null)
});
});
// ============================================
// SECTION 5: Damage Details (Conditional)
// ============================================
const damageSection = form.addSubform('damageDetails', {
title: 'Damage Details',
isVisible: () => hasDamage(),
customStyles: {
backgroundColor: '#fef3c7',
padding: '16px',
borderRadius: '8px',
border: '1px solid #f59e0b'
}
});
damageSection.addRow(row => {
row.addCheckboxList('damagedItems', {
label: 'What types of items were damaged?',
options: [
{ id: 'furniture', name: 'Furniture' },
{ id: 'electronics', name: 'Electronics' },
{ id: 'fragile', name: 'Fragile items (glass, ceramics)' },
{ id: 'boxes', name: 'Boxes/packed items' },
{ id: 'appliances', name: 'Appliances' },
{ id: 'property', name: 'Property (walls, floors, doors)' }
],
orientation: 'vertical'
});
});
damageSection.addRow(row => {
row.addTextarea('damageDescription', {
label: 'Please describe the damage:',
placeholder: 'Provide details about damaged items for our records...',
rows: 3,
autoExpand: true
});
});
damageSection.addRow(row => {
row.addRadioButton('claimFiled', {
label: 'Have you filed a damage claim?',
options: [
{ id: 'yes', name: 'Yes, already filed' },
{ id: 'planning', name: 'Planning to file' },
{ id: 'no', name: 'No, not filing a claim' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 6: Timing & Schedule
// ============================================
const timingSection = form.addSubform('timing', {
title: 'Timing & Schedule'
});
timingSection.addRow(row => {
row.addRadioButton('arrivalTime', {
label: 'Did the crew arrive on time?',
options: [
{ id: 'early', name: 'Early' },
{ id: 'ontime', name: 'On time' },
{ id: 'late15', name: 'Up to 15 min late' },
{ id: 'late30', name: '15-30 min late' },
{ id: 'late60', name: 'More than 30 min late' }
],
orientation: 'horizontal'
});
});
timingSection.addRow(row => {
row.addRadioButton('completionTime', {
label: 'How did actual completion time compare to estimate?',
options: [
{ id: 'faster', name: 'Faster than expected' },
{ id: 'asexpected', name: 'As expected' },
{ id: 'somewhat', name: 'Somewhat longer' },
{ id: 'much', name: 'Much longer than expected' }
],
orientation: 'horizontal'
});
});
// ============================================
// SECTION 7: Overall Satisfaction
// ============================================
const overallSection = form.addSubform('overall', {
title: 'Overall Experience'
});
overallSection.addRow(row => {
row.addEmojiRating('moveExperience', {
label: 'How do you feel about your overall moving experience?',
preset: 'satisfaction',
size: 'lg',
showLabels: true,
alignment: 'center'
});
});
overallSection.addRow(row => {
row.addStarRating('overallSatisfaction', {
label: 'Rate your overall satisfaction with our moving service',
maxStars: 5,
size: 'lg',
alignment: 'center',
showConfettiOnMax: true,
onValueChange: (val) => overallRating.set(val ?? null)
});
});
overallSection.addRow(row => {
row.addRatingScale('npsScore', {
label: 'How likely are you to recommend us to friends or family?',
preset: 'nps',
size: 'md',
alignment: 'center',
showCategoryLabel: true,
showSegmentColors: true
});
});
overallSection.addRow(row => {
row.addThumbRating('useAgain', {
label: 'Would you use our services for your next move?',
size: 'lg',
showLabels: true,
upLabel: 'Yes, definitely',
downLabel: 'Probably not',
alignment: 'center'
});
});
// ============================================
// SECTION 8: Additional Feedback
// ============================================
const feedbackSection = form.addSubform('feedback', {
title: 'Additional Feedback'
});
feedbackSection.addRow(row => {
row.addTextarea('whatWorked', {
label: 'What did we do well?',
placeholder: 'Share what you appreciated about our service...',
rows: 2,
autoExpand: true
});
});
feedbackSection.addRow(row => {
row.addTextarea('improvements', {
label: 'How could we improve?',
placeholder: 'Your suggestions help us serve future customers better...',
rows: 2,
autoExpand: true
});
});
// ============================================
// SUMMARY SECTION
// ============================================
const summarySection = form.addSubform('summary', {
title: 'Your Feedback Summary',
isVisible: () => overallRating() !== null
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const moveType = detailsSection.radioButton('moveType')?.value();
const crewRating = crewSection.starRating('crewOverall')?.value();
const overall = overallRating();
const nps = overallSection.ratingScale('npsScore')?.value();
const npsCategory = overallSection.ratingScale('npsScore')?.npsCategory();
const damage = conditionSection.radioButton('damageReport')?.value();
const useAgain = overallSection.thumbRating('useAgain')?.value();
const moveLabels: Record<string, string> = {
'local': 'Local Move',
'long': 'Long Distance',
'interstate': 'Interstate',
'international': 'International'
};
let summary = `Moving Feedback Summary\n${'═'.repeat(28)}\n\n`;
summary += `Move Type: ${moveLabels[moveType || ''] || 'Not specified'}\n`;
if (crewRating) {
summary += `Crew Rating: ${'★'.repeat(crewRating)}${'☆'.repeat(5 - crewRating)}\n`;
}
if (overall) {
summary += `Overall: ${'★'.repeat(overall)}${'☆'.repeat(5 - overall)}\n`;
}
if (nps !== null && nps !== undefined) {
summary += `NPS Score: ${nps}/10 (${npsCategory?.charAt(0).toUpperCase()}${npsCategory?.slice(1)})\n`;
}
if (damage) {
const damageLabels: Record<string, string> = {
'none': 'No damage',
'minor': 'Minor damage',
'moderate': 'Moderate damage',
'major': 'Major damage'
};
summary += `\nItem Condition: ${damageLabels[damage]}`;
}
if (useAgain) {
summary += `\n\nWould use again: ${useAgain === 'up' ? 'Yes' : 'No'}`;
}
return summary;
},
customStyles: () => {
const overall = overallRating();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '13px'
};
if (overall && overall >= 4) {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (overall && overall <= 2) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
}
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Moving Feedback'
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You!',
message: 'Your feedback helps us deliver better moving experiences. We know moving is stressful, and we appreciate you taking the time to share your experience with us.'
});
}