Add testimonials section and Q&A submit CTA; v1.0.8

- Testimonials: new Testimonial type, CMS field, admin UI under About,
  and TestimonialsSection rendered on homepage and About page (hidden when empty)
- Q&A: "Submit a Question →" CTA at bottom of Q&A page linking to /contact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-07-22 12:37:46 -04:00
parent a7c5906c7b
commit b60604aa80
6 changed files with 158 additions and 3 deletions
+37 -1
View File
@@ -702,7 +702,7 @@ interface PodcastChecklistData {
episodes: PodcastChecklistEpisode[]
}
type StringField = Exclude<keyof SiteContent, 'customLinks' | 'customBlocks' | 'redirects' | 'podcastFeaturedLinks' | 'finishedBooks' | 'seo' | 'legal' | 'whereToNextCards' | 'colossiansStudySections' | 'studies'>
type StringField = Exclude<keyof SiteContent, 'customLinks' | 'customBlocks' | 'redirects' | 'podcastFeaturedLinks' | 'finishedBooks' | 'testimonials' | 'seo' | 'legal' | 'whereToNextCards' | 'colossiansStudySections' | 'studies'>
type AdminView =
| 'dashboard' | 'homepage' | 'start-here' | 'about' | 'contact'
@@ -3688,6 +3688,42 @@ export default function AdminPage({ content, onSave, onLogout }: Props) {
</div>
))}
{renderSaveStatus()}
<div className="admin-panel-subhead" id="about-testimonials">
<h3>Listener Testimonials</h3>
<p>Quotes shown on the About page and homepage. Add Apple Podcast reviews, listener emails, or any kind words.</p>
</div>
{(form.testimonials ?? []).length === 0 && (
<p className="admin-stats-note">No testimonials yet. Add one below.</p>
)}
{(form.testimonials ?? []).map(item => (
<AdminCollapsibleCard
key={item.id}
title={item.name || 'Unnamed listener'}
subtitle={item.source || 'No source'}
>
<div className="admin-array-row">
<div className="admin-array-fields">
<div className="admin-field">
<label htmlFor={`testimonial-quote-${item.id}`}>Quote</label>
<textarea id={`testimonial-quote-${item.id}`} rows={3} placeholder="What they said…" value={item.quote} onChange={e => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).map(t => t.id === item.id ? { ...t, quote: e.target.value } : t) }))} />
</div>
<div className="admin-field">
<label htmlFor={`testimonial-name-${item.id}`}>Name</label>
<input id={`testimonial-name-${item.id}`} type="text" placeholder="e.g. Sarah M." value={item.name} onChange={e => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).map(t => t.id === item.id ? { ...t, name: e.target.value } : t) }))} />
</div>
<div className="admin-field">
<label htmlFor={`testimonial-source-${item.id}`}>Source (optional)</label>
<input id={`testimonial-source-${item.id}`} type="text" placeholder="e.g. Apple Podcasts" value={item.source ?? ''} onChange={e => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).map(t => t.id === item.id ? { ...t, source: e.target.value } : t) }))} />
</div>
</div>
<button type="button" className="btn-admin-remove" onClick={() => setForm(f => ({ ...f, testimonials: (f.testimonials ?? []).filter(t => t.id !== item.id) }))}>Remove</button>
</div>
</AdminCollapsibleCard>
))}
<button type="button" className="btn-admin-add" onClick={() => setForm(f => ({ ...f, testimonials: [...(f.testimonials ?? []), { id: Date.now().toString(36), quote: '', name: '', source: '' }] }))}>
+ Add Testimonial
</button>
</section>
)}