export function betaReaderFeedbackSurvey(form: FormTs) {
// Beta Reader Manuscript Feedback - Multi-page survey
// Demonstrates: Pages (multi-page wizard), MatrixQuestion, StarRating, EmojiRating, Slider, SuggestionChips
// ============================================
// HEADER
// ============================================
form.addRow(row => {
row.addTextPanel('header', {
label: 'Beta Reader Feedback Form',
computedValue: () => 'Thank you for being a beta reader! Your insights help shape this story.',
customStyles: {
backgroundColor: '#7c3aed',
color: 'white',
padding: '28px',
borderRadius: '12px',
textAlign: 'center'
}
});
});
// ============================================
// MULTI-PAGE WIZARD
// ============================================
const pages = form.addPages('feedbackPages', { heightMode: 'current-page' });
// ============================================
// PAGE 1: Reader Profile & Overall Impressions
// ============================================
const page1 = pages.addPage('overallImpressions');
page1.addRow(row => {
row.addTextPanel('page1Title', {
label: 'Page 1 of 4: Overall Impressions',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#7c3aed',
marginBottom: '8px'
}
});
});
const readerSection = page1.addSubform('reader', {
title: 'About You as a Reader'
});
readerSection.addRow(row => {
row.addCheckboxList('readingGenres', {
label: 'What genres do you typically read? (Select all that apply)',
options: [
{ id: 'fantasy', name: 'Fantasy' },
{ id: 'scifi', name: 'Science Fiction' },
{ id: 'romance', name: 'Romance' },
{ id: 'mystery', name: 'Mystery/Thriller' },
{ id: 'literary', name: 'Literary Fiction' },
{ id: 'ya', name: 'Young Adult' },
{ id: 'horror', name: 'Horror' },
{ id: 'historical', name: 'Historical Fiction' }
],
orientation: 'vertical'
});
});
readerSection.addRow(row => {
row.addRadioButton('completionStatus', {
label: 'Did you finish reading the manuscript?',
options: [
{ id: 'finished', name: 'Yes, I finished it' },
{ id: 'most', name: 'Read most of it (75%+)' },
{ id: 'half', name: 'Read about half' },
{ id: 'quarter', name: 'Read less than half' },
{ id: 'stopped', name: 'Stopped early' }
],
orientation: 'vertical',
isRequired: true
});
});
readerSection.addRow(row => {
row.addTextarea('stoppedReason', {
label: 'What made you stop reading?',
placeholder: 'This feedback is valuable for the author...',
rows: 3,
isVisible: () => {
const status = readerSection.radioButton('completionStatus')?.value();
return status === 'quarter' || status === 'stopped';
}
});
});
const firstImpression = page1.addSubform('firstImpression', {
title: 'First Impressions'
});
firstImpression.addRow(row => {
row.addEmojiRating('overallReaction', {
label: 'What was your overall emotional reaction to the manuscript?',
preset: 'mood',
size: 'lg',
alignment: 'center'
});
});
firstImpression.addRow(row => {
row.addStarRating('overallRating', {
label: 'Overall rating (as a complete work)',
maxStars: 5,
size: 'lg',
alignment: 'center',
showCounter: true
});
});
firstImpression.addRow(row => {
row.addThumbRating('wouldBuy', {
label: 'Would you buy this book if it were published?',
showLabels: true,
upLabel: 'Yes!',
downLabel: 'Probably not',
size: 'lg',
alignment: 'center'
});
});
page1.addRow(row => {
row.addButton('nextPage1', {
label: 'Next: Story Elements →',
onClick: () => pages.goToPage('storyElements')
});
});
// ============================================
// PAGE 2: Story Elements
// ============================================
const page2 = pages.addPage('storyElements');
page2.addRow(row => {
row.addTextPanel('page2Title', {
label: 'Page 2 of 4: Story Elements',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#7c3aed',
marginBottom: '8px'
}
});
});
const plotSection = page2.addSubform('plot', {
title: 'Plot & Structure'
});
plotSection.addRow(row => {
row.addMatrixQuestion('plotElements', {
label: 'Rate these aspects of the plot:',
rows: [
{ id: 'hook', label: 'Opening hook/first chapter', isRequired: true },
{ id: 'conflict', label: 'Central conflict clarity', isRequired: true },
{ id: 'stakes', label: 'Stakes (what characters could lose)', isRequired: true },
{ id: 'twists', label: 'Plot twists/surprises', isRequired: false },
{ id: 'resolution', label: 'Ending/resolution', isRequired: true }
],
columns: [
{ id: 'excellent', label: 'Excellent' },
{ id: 'good', label: 'Good' },
{ id: 'needs-work', label: 'Needs Work' },
{ id: 'weak', label: 'Weak' }
],
striped: true,
fullWidth: true
});
});
plotSection.addRow(row => {
row.addSlider('pacingRating', {
label: 'How was the overall pacing?',
min: 1,
max: 5,
step: 1,
defaultValue: 3,
showValue: true
}, '1fr');
});
plotSection.addRow(row => {
row.addTextPanel('pacingLabel', {
computedValue: () => {
const pacing = plotSection.slider('pacingRating')?.value();
const labels: Record<number, string> = {
1: '⏪ Too slow - dragged in places',
2: '🐢 Slightly slow',
3: '⚖️ Just right',
4: '🐇 Slightly rushed',
5: '⏩ Too fast - needed more development'
};
return pacing ? labels[pacing] || '' : '';
},
customStyles: {
textAlign: 'center',
fontStyle: 'italic',
color: '#6b7280'
}
});
});
plotSection.addSpacer();
plotSection.addRow(row => {
row.addTextarea('plotFeedback', {
label: 'Additional comments on plot and structure:',
placeholder: 'What worked? What confused you? Any plot holes?',
rows: 4,
autoExpand: true
});
});
page2.addRow(row => {
row.addButton('prevPage2', {
label: '← Previous',
onClick: () => pages.goToPage('overallImpressions')
});
row.addButton('nextPage2', {
label: 'Next: Characters & Writing →',
onClick: () => pages.goToPage('charactersWriting')
});
});
// ============================================
// PAGE 3: Characters & Writing Style
// ============================================
const page3 = pages.addPage('charactersWriting');
page3.addRow(row => {
row.addTextPanel('page3Title', {
label: 'Page 3 of 4: Characters & Writing',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#7c3aed',
marginBottom: '8px'
}
});
});
const charSection = page3.addSubform('characters', {
title: 'Characters'
});
charSection.addRow(row => {
row.addMatrixQuestion('charRatings', {
label: 'Rate these aspects of the characters:',
rows: [
{ id: 'protagonist', label: 'Main character(s) likability', isRequired: true },
{ id: 'development', label: 'Character development/arcs', isRequired: true },
{ id: 'motivation', label: 'Believable motivations', isRequired: true },
{ id: 'dialogue', label: 'Distinct voices in dialogue', isRequired: true },
{ id: 'relationships', label: 'Character relationships', isRequired: false },
{ id: 'antagonist', label: 'Villain/antagonist', isRequired: false }
],
columns: [
{ id: 'excellent', label: 'Excellent' },
{ id: 'good', label: 'Good' },
{ id: 'needs-work', label: 'Needs Work' },
{ id: 'na', label: 'N/A' }
],
striped: true,
fullWidth: true
});
});
charSection.addRow(row => {
row.addTextbox('favoriteChar', {
label: 'Who was your favorite character and why?',
placeholder: 'Character name and reason...'
}, '1fr');
});
charSection.addRow(row => {
row.addTextbox('leastFavoriteChar', {
label: 'Which character did you struggle with (if any)?',
placeholder: 'Character name and reason...'
}, '1fr');
});
const writingSection = page3.addSubform('writing', {
title: 'Writing Style'
});
writingSection.addRow(row => {
row.addStarRating('proseQuality', {
label: 'Overall quality of the prose',
maxStars: 5,
size: 'lg',
alignment: 'center',
showCounter: true
}, '1fr');
row.addStarRating('dialogueQuality', {
label: 'Quality of dialogue',
maxStars: 5,
size: 'lg',
alignment: 'center',
showCounter: true
}, '1fr');
});
writingSection.addRow(row => {
row.addCheckboxList('writingIssues', {
label: 'Did you notice any recurring issues? (Select all that apply)',
options: [
{ id: 'none', name: 'No major issues' },
{ id: 'info-dump', name: 'Info-dumping/exposition' },
{ id: 'telling', name: 'Too much telling, not enough showing' },
{ id: 'pov', name: 'POV inconsistencies' },
{ id: 'tense', name: 'Tense shifts' },
{ id: 'repetitive', name: 'Repetitive words/phrases' },
{ id: 'passive', name: 'Too much passive voice' },
{ id: 'dialogue-tags', name: 'Clunky dialogue tags' }
],
orientation: 'vertical'
});
});
writingSection.addSpacer();
writingSection.addRow(row => {
row.addTextarea('writingFeedback', {
label: 'Comments on writing style:',
placeholder: 'What did you love about the writing? What could be improved?',
rows: 4,
autoExpand: true
});
});
page3.addRow(row => {
row.addButton('prevPage3', {
label: '← Previous',
onClick: () => pages.goToPage('storyElements')
});
row.addButton('nextPage3', {
label: 'Next: Final Thoughts →',
onClick: () => pages.goToPage('finalThoughts')
});
});
// ============================================
// PAGE 4: Final Thoughts & Summary
// ============================================
const page4 = pages.addPage('finalThoughts');
page4.addRow(row => {
row.addTextPanel('page4Title', {
label: 'Page 4 of 4: Final Thoughts',
customStyles: {
fontSize: '18px',
fontWeight: 'bold',
color: '#7c3aed',
marginBottom: '8px'
}
});
});
const strengthsSection = page4.addSubform('strengths', {
title: 'Highlights & Suggestions'
});
strengthsSection.addRow(row => {
row.addSuggestionChips('bestParts', {
label: 'What were the strongest elements? (Select up to 3)',
suggestions: [
{ id: 'characters', name: 'Characters' },
{ id: 'plot', name: 'Plot' },
{ id: 'world', name: 'World-building' },
{ id: 'dialogue', name: 'Dialogue' },
{ id: 'prose', name: 'Prose style' },
{ id: 'pacing', name: 'Pacing' },
{ id: 'emotion', name: 'Emotional impact' },
{ id: 'hook', name: 'Opening hook' }
],
max: 3,
alignment: 'center'
});
});
strengthsSection.addRow(row => {
row.addSuggestionChips('needsWork', {
label: 'What needs the most work? (Select up to 3)',
suggestions: [
{ id: 'characters', name: 'Characters' },
{ id: 'plot', name: 'Plot' },
{ id: 'world', name: 'World-building' },
{ id: 'dialogue', name: 'Dialogue' },
{ id: 'prose', name: 'Prose style' },
{ id: 'pacing', name: 'Pacing' },
{ id: 'ending', name: 'Ending' },
{ id: 'beginning', name: 'Beginning' }
],
max: 3,
alignment: 'center'
});
});
strengthsSection.addSpacer();
strengthsSection.addRow(row => {
row.addTextarea('mostMemorable', {
label: 'What scene or moment was most memorable to you?',
placeholder: 'Describe the scene that stuck with you...',
rows: 3
});
});
strengthsSection.addRow(row => {
row.addTextarea('finalAdvice', {
label: 'If you could give the author one piece of advice, what would it be?',
placeholder: 'Your most important suggestion...',
rows: 3
});
});
const marketSection = page4.addSubform('market', {
title: 'Market Fit'
});
marketSection.addRow(row => {
row.addDropdown('targetAudience', {
label: 'Who do you think the target audience is?',
options: [
{ id: 'ya', name: 'Young Adult (13-18)' },
{ id: 'na', name: 'New Adult (18-25)' },
{ id: 'adult', name: 'Adult (general)' },
{ id: 'mature', name: 'Adult (mature themes)' },
{ id: 'mg', name: 'Middle Grade (8-12)' },
{ id: 'unsure', name: 'Not sure' }
]
}, '1fr');
row.addTextbox('compTitles', {
label: 'What published books is this similar to?',
placeholder: 'e.g., "Hunger Games meets The Night Circus"'
}, '1fr');
});
const summarySection = page4.addSubform('summary', {
title: 'Feedback Summary'
});
summarySection.addRow(row => {
row.addTextPanel('summaryContent', {
computedValue: () => {
const overallRating = firstImpression.starRating('overallRating')?.value();
const wouldBuy = firstImpression.thumbRating('wouldBuy')?.value();
const reaction = firstImpression.emojiRating('overallReaction')?.value();
const pacing = plotSection.slider('pacingRating')?.value();
const proseQuality = writingSection.starRating('proseQuality')?.value();
const bestParts = strengthsSection.suggestionChips('bestParts')?.value() || [];
const needsWork = strengthsSection.suggestionChips('needsWork')?.value() || [];
let summary = `📚 Beta Reader Summary\n`;
summary += `${'═'.repeat(25)}\n\n`;
if (overallRating) {
const stars = '⭐'.repeat(overallRating) + '☆'.repeat(5 - overallRating);
summary += `Overall: ${stars}\n`;
}
if (reaction) {
const moodLabels: Record<string, string> = {
'sad': '😢 Sad/Frustrated',
'down': '😔 Disappointed',
'neutral': '😐 Mixed feelings',
'happy': '😊 Enjoyed it',
'excited': '🤩 Loved it!'
};
summary += `Reaction: ${moodLabels[reaction] || reaction}\n`;
}
if (wouldBuy) {
summary += `Would buy: ${wouldBuy === 'up' ? '✅ Yes' : '❌ No'}\n`;
}
if (pacing) {
const pacingLabels: Record<number, string> = {
1: 'Too slow', 2: 'Slightly slow', 3: 'Perfect',
4: 'Slightly fast', 5: 'Too fast'
};
summary += `Pacing: ${pacingLabels[pacing]}\n`;
}
if (proseQuality) {
summary += `Prose quality: ${proseQuality}/5\n`;
}
if (bestParts.length > 0) {
summary += `\n✨ Strengths: ${bestParts.join(', ')}`;
}
if (needsWork.length > 0) {
summary += `\n📝 Needs work: ${needsWork.join(', ')}`;
}
return summary;
},
customStyles: () => {
const rating = firstImpression.starRating('overallRating')?.value();
const baseStyles = {
padding: '16px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontFamily: 'monospace',
fontSize: '14px'
};
if (rating && rating >= 4) {
return { ...baseStyles, backgroundColor: '#d1fae5', borderLeft: '4px solid #10b981' };
} else if (rating && rating === 3) {
return { ...baseStyles, backgroundColor: '#fef3c7', borderLeft: '4px solid #f59e0b' };
} else if (rating && rating < 3) {
return { ...baseStyles, backgroundColor: '#fee2e2', borderLeft: '4px solid #ef4444' };
}
return { ...baseStyles, backgroundColor: '#f5f3ff', borderLeft: '4px solid #7c3aed' };
}
});
});
page4.addRow(row => {
row.addButton('prevPage4', {
label: '← Previous',
onClick: () => pages.goToPage('charactersWriting')
});
});
// ============================================
// FORM CONFIGURATION
// ============================================
form.configureSubmitButton({
label: 'Submit Beta Reader Feedback'
});
form.configureCompletionScreen({
type: 'text',
title: 'Thank You for Your Feedback!',
message: 'Your detailed insights are invaluable to the author. Beta readers like you help make stories shine. Happy reading!'
});
}