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:
+37
-1
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user