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:
+33
-1
@@ -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&A archive.
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: '0.75rem', justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
<a href="/questions" className="btn-primary">Browse Q&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>
|
||||
|
||||
Reference in New Issue
Block a user