feat: homepage newsletter, Q&A callout, episodes page redesign, SEO/sitemap, asset tagging

- Fix "Invalid Date" on student account page (memberSince guard)
- Fix study community author showing full email instead of username
- Add newsletter sign-up section to homepage (between PRISM and Current Series)
- Add newsletter opt-in nudge to study enrollment flow
- Redesign episodes page: latest episode featured card + per-episode Spotify embed
- Add SEO description field to episodes page (visually hidden, admin-editable)
- Expand default sitemap paths to include /about, /contact, /episodes, /resources, /study, /study/titus, /study/colossians
- Add featured Q&A callout section to homepage
- Add Q&A callout to study hub footer
- Tag both untagged image assets in Asset Manager
This commit is contained in:
nmemmert
2026-06-03 10:56:55 -04:00
parent 01abb2c8c6
commit b187b19f18
11 changed files with 382 additions and 15 deletions
+33 -1
View File
@@ -463,6 +463,8 @@ export function StudyLandingPage({ content }: Props) {
const [studyModal, setStudyModal] = useState<'none' | 'enrollments' | 'browseTracks'>('none')
const [enrollBusySlug, setEnrollBusySlug] = useState('')
const [enrollMessage, setEnrollMessage] = useState('')
const [showNewsletterNudge, setShowNewsletterNudge] = useState(false)
const [newsletterNudgeDone, setNewsletterNudgeDone] = useState(false)
const studies = getStudies(content)
const activeStudies = studies.filter(study => study.status !== 'planned')
@@ -495,6 +497,7 @@ export function StudyLandingPage({ content }: Props) {
const slugs = normalizeEnrolledStudySlugs(data.enrolledStudySlugs)
setAuth(prev => ({ ...prev, enrolledStudySlugs: slugs }))
setEnrollMessage(enrolled ? `Unenrolled from ${data.studyTitle ?? studySlug}.` : `Enrolled in ${data.studyTitle ?? studySlug}.`)
if (!enrolled && !auth.subscribeNewsletter) setShowNewsletterNudge(true)
await refreshOverview()
} catch (err) {
setEnrollMessage(err instanceof Error ? err.message : 'Unable to update enrollment.')
@@ -678,6 +681,22 @@ export function StudyLandingPage({ content }: Props) {
</div>
)}
{enrollMessage && <p className="study-note-status" style={{ marginTop: '1rem' }}>{enrollMessage}</p>}
{showNewsletterNudge && !newsletterNudgeDone && (
<div className="study-newsletter-nudge" style={{ marginTop: '1rem', background: '#1a1a12', border: '1px solid rgba(201,168,76,0.25)', borderRadius: '8px', padding: '1rem', fontSize: '0.9rem' }}>
<p style={{ margin: '0 0 0.5rem' }}>Want episode updates and study announcements by email?</p>
<div style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap' }}>
<button type="button" className="btn-primary" style={{ fontSize: '0.85rem', padding: '0.4rem 1rem' }} onClick={async () => {
try {
await fetch('/api/study-account/profile', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ subscribeNewsletter: true }) })
setAuth(prev => ({ ...prev, subscribeNewsletter: true }))
} catch {}
setNewsletterNudgeDone(true)
setShowNewsletterNudge(false)
}}>Yes, subscribe me</button>
<button type="button" className="btn-secondary" style={{ fontSize: '0.85rem', padding: '0.4rem 1rem' }} onClick={() => setShowNewsletterNudge(false)}>No thanks</button>
</div>
</div>
)}
</div>
</div>
)}
@@ -844,6 +863,19 @@ export function StudyLandingPage({ content }: Props) {
</section>
)}
<section className="section-study-module" aria-label="Q&A">
<div className="section-inner" style={{ textAlign: 'center', padding: '3rem 1rem' }}>
<p className="eyebrow">Have a question from the Word?</p>
<h2 style={{ fontSize: '1.5rem', fontWeight: 700, margin: '0.5rem 0 0.75rem', color: '#f0e9cc' }}>Ask Nate a Question</h2>
<p style={{ color: '#8a7f5a', maxWidth: '480px', margin: '0 auto 1.5rem', fontSize: '0.95rem' }}>
Submit a Bible or theology question and it may be answered in a future episode or the Q&amp;A archive.
</p>
<div style={{ display: 'flex', gap: '0.75rem', justifyContent: 'center', flexWrap: 'wrap' }}>
<a href="/questions" className="btn-primary">Browse Q&amp;A Archive </a>
<a href="/questions#ask" className="btn-secondary">Submit a Question</a>
</div>
</div>
</section>
</main>
)
@@ -2299,7 +2331,7 @@ export function StudyAccountPage() {
<p style={{ margin: 0, fontSize: '0.85rem' }}>Total notes saved</p>
</div>
<div style={{ flex: '1 1 150px', background: '#1a1a16', borderRadius: '8px', padding: '1rem', border: '1px solid #2a2518' }}>
<p style={{ fontSize: '1rem', fontWeight: 600, margin: 0 }}>{new Date(overview.stats.memberSince).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</p>
<p style={{ fontSize: '1rem', fontWeight: 600, margin: 0 }}>{overview.stats.memberSince && !isNaN(new Date(overview.stats.memberSince).getTime()) ? new Date(overview.stats.memberSince).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : '—'}</p>
<p style={{ margin: 0, fontSize: '0.85rem' }}>Member since</p>
</div>
</div>