import { useState, useEffect } from 'react' import { Link, NavLink, Routes, Route, useLocation, useNavigate, useParams } from 'react-router-dom' import AdminPage from './AdminPage' import QASection from './components/QASection' import ContactForm from './components/ContactForm' import { FacebookIcon, SpotifyIcon, YouTubeIcon, AmazonMusicIcon } from './icons' import type { SiteContent } from './content' import { DEFAULTS } from './content' import './App.css' const SPOTIFY_SHOW_URL = '/spotify' const SPOTIFY_EMBED_URL = 'https://open.spotify.com/embed/show/0Gq1TzoJOdReSZ1gYQi8Xl?utm_source=generator&theme=0' const SPOTIFY_CREATOR_URL = 'https://creators.spotify.com/pod/profile/nmemmert/' const APPLE_PODCASTS_URL = '/apple' const YOUTUBE_URL = 'https://www.youtube.com/@blackzebraem5558' const AMAZON_MUSIC_URL = '/amazon' const FACEBOOK_URL = 'https://facebook.com/versebyversewithnate' const CONSENT_KEY = 'vbn_analytics_consent_choice' function StudyDownloadForm() { const [fields, setFields] = useState({ firstName: '', lastName: '', email: '' }) const [subscribe, setSubscribe] = useState(true) const [honey, setHoney] = useState('') const [status, setStatus] = useState<'idle' | 'submitting' | 'error' | 'success'>('idle') const [errorMsg, setErrorMsg] = useState('') const [successMsg, setSuccessMsg] = useState('') function handleChange(e: React.ChangeEvent) { setFields(f => ({ ...f, [e.target.name]: e.target.value })) } async function handleSubmit(e: React.FormEvent) { e.preventDefault() setStatus('submitting') setErrorMsg('') setSuccessMsg('') try { const res = await fetch('/api/study-downloads/titus', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...fields, subscribe, _honey: honey }), }) const data = await res.json().catch(() => ({})) as { message?: string; downloadUrl?: string } if (!res.ok || !data.downloadUrl) { setErrorMsg(data.message ?? 'Could not process your request. Please try again.') setStatus('error') return } setStatus('success') setSuccessMsg('Your download should start now. If not, use the link below.') window.location.assign(data.downloadUrl) } catch { setErrorMsg('Could not connect. Please try again later.') setStatus('error') } } return (
setHoney(e.target.value)} />
{status === 'error' &&

{errorMsg}

} {status === 'success' &&

{successMsg}

}
) } function ResourceDownloadForm({ resourceId, buttonText }: { resourceId: string; buttonText: string }) { const [fields, setFields] = useState({ firstName: '', lastName: '', email: '' }) const [subscribe, setSubscribe] = useState(true) const [honey, setHoney] = useState('') const [status, setStatus] = useState<'idle' | 'submitting' | 'error' | 'success'>('idle') const [errorMsg, setErrorMsg] = useState('') const [successMsg, setSuccessMsg] = useState('') function handleChange(e: React.ChangeEvent) { setFields(f => ({ ...f, [e.target.name]: e.target.value })) } async function handleSubmit(e: React.FormEvent) { e.preventDefault() setStatus('submitting') setErrorMsg('') setSuccessMsg('') try { const res = await fetch('/api/resource-download', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ resourceId, ...fields, subscribe, _honey: honey }), }) const data = await res.json().catch(() => ({})) as { message?: string; downloadUrl?: string } if (!res.ok || !data.downloadUrl) { setErrorMsg(data.message ?? 'Could not process your request. Please try again.') setStatus('error') return } setStatus('success') setSuccessMsg('Your download should start now. If not, use the link below.') window.location.assign(data.downloadUrl) } catch { setErrorMsg('Could not connect. Please try again later.') setStatus('error') } } return (
setHoney(e.target.value)} />
{status === 'error' &&

{errorMsg}

} {status === 'success' &&

{successMsg}

}
) } function AnalyticsConsentBanner() { const [choice, setChoice] = useState<'unknown' | 'accepted' | 'declined'>(() => { const saved = localStorage.getItem(CONSENT_KEY) if (saved === 'accepted' || saved === 'declined') return saved return 'unknown' }) async function sendChoice(consent: boolean) { await fetch('/api/analytics-consent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ consent }), }) } async function accept() { setChoice('accepted') localStorage.setItem(CONSENT_KEY, 'accepted') try { await sendChoice(true) } catch { // Keep local preference even if network fails. } } async function decline() { setChoice('declined') localStorage.setItem(CONSENT_KEY, 'declined') try { await sendChoice(false) } catch { // Keep local preference even if network fails. } } if (choice !== 'unknown') return null return (

We use optional analytics cookies to measure visits and location trends for site improvement.

) } interface Episode { title: string pubDate: string link: string description: string duration: string episode: string } function formatPubDate(raw: string): string { try { return new Date(raw).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }) } catch { return raw } } function LatestEpisodesList() { const [episodes, setEpisodes] = useState([]) const [loading, setLoading] = useState(true) const [failed, setFailed] = useState(false) useEffect(() => { fetch('/api/episodes') .then(r => (r.ok ? r.json() : Promise.reject(new Error('fetch failed')))) .then((data: { episodes: Episode[] }) => { if (data.episodes.length === 0) setFailed(true) else setEpisodes(data.episodes) setLoading(false) }) .catch(() => { setFailed(true) setLoading(false) }) }, []) if (loading) { return (
{[1, 2, 3].map(i => ) } if (failed) { return (