Rename Downloads→Resources throughout, fix audit bugs, add /subscribe to footer; v1.1.49
Publish Container / docker (push) Successful in 14m2s
Publish Container / docker (push) Successful in 14m2s
- Rename all user-facing "Downloads" text to "Resources" (nav, page eyebrows, buttons, admin labels, page title, content defaults)
- Add usePageMeta to ContactPage, EpisodeDetailPage, DownloadDetailPage, StartHerePage for correct social share previews
- Wire contactEyebrow and aboutShowEyebrow fields to the UI (were editable in admin but never rendered)
- Wrap StartHerePage in full site chrome (header, search, footer); fix Step 2 link from /#qa to /questions
- Fix admin "View All Contacts" link (was 404); now navigates to Contact admin panel
- Remove broken "Previous Studies" jump nav entry (no matching anchor)
- Remove /studys typo route
- Make dashboard "Top Resource Downloads" stat dynamic instead of hardcoded to titus-study key
- Add Newsletter link (/subscribe) to footer legal nav
- Fix server.js startup log ("Portfolio app" → "Siteforge")
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
0d2e68ae11
commit
a072076818
7 files changed
+123
-65
No files matched your search
+70
-40
@@ -428,7 +428,7 @@ function SiteHeader({ content }: { content: SiteContent }) {
|
||||
<NavLink to="/questions" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Q&A</NavLink>
|
||||
<NavLink to="/study" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Studies</NavLink>
|
||||
<NavLink to="/study/account" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>My Account</NavLink>
|
||||
<NavLink to="/resources" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Downloads</NavLink>
|
||||
<NavLink to="/resources" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Resources</NavLink>
|
||||
<NavLink to="/about" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>About</NavLink>
|
||||
<NavLink to="/contact" className={({ isActive }) => `header-nav-link${isActive ? ' header-nav-link--active' : ''}`} onClick={() => setMenuOpen(false)}>Contact</NavLink>
|
||||
<a
|
||||
@@ -503,6 +503,8 @@ function SiteFooter({ content }: { content: SiteContent }) {
|
||||
<nav className="footer-links footer-links--legal" aria-label="Legal links">
|
||||
<Link to="/finished">Finished Books</Link>
|
||||
<span aria-hidden="true">·</span>
|
||||
<Link to="/subscribe">Newsletter</Link>
|
||||
<span aria-hidden="true">·</span>
|
||||
<Link to="/privacy">Privacy Policy</Link>
|
||||
<span aria-hidden="true">·</span>
|
||||
<Link to="/terms">Terms</Link>
|
||||
@@ -601,6 +603,7 @@ function AboutSection({ content }: { content: SiteContent }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="about-text">
|
||||
{content.aboutShowEyebrow && <p className="eyebrow">{content.aboutShowEyebrow}</p>}
|
||||
<h2>{content.aboutShowHeading}</h2>
|
||||
<p>{content.aboutShowP1}</p>
|
||||
<p>{content.aboutShowP2}</p>
|
||||
@@ -841,7 +844,7 @@ function DownloadLibrarySection({ content }: { content: SiteContent }) {
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="download-empty-state">No downloads tagged "{activeTag}".</p>
|
||||
<p className="download-empty-state">No resources tagged "{activeTag}".</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
@@ -853,15 +856,20 @@ function DownloadLibrarySection({ content }: { content: SiteContent }) {
|
||||
function DownloadDetailPage({ content }: { content: SiteContent }) {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const resource = resolveDownloadPageResource(content, id)
|
||||
const siteTitle = content.seo?.title || DEFAULTS.seo.title
|
||||
usePageMeta(
|
||||
resource ? `${resource.label} | ${siteTitle}` : `Resource | ${siteTitle}`,
|
||||
resource?.summary || 'Free study guides and resources from Verse by Verse with Nate.',
|
||||
)
|
||||
|
||||
if (!resource) {
|
||||
return (
|
||||
<main className="thanks-page" aria-label="Download not found">
|
||||
<div className="thanks-card">
|
||||
<p className="eyebrow">Downloads</p>
|
||||
<p className="eyebrow">Resources</p>
|
||||
<h1>Download not found</h1>
|
||||
<p>The download you requested is not available right now.</p>
|
||||
<Link to="/resources" className="btn-primary">Back to Downloads</Link>
|
||||
<Link to="/resources" className="btn-primary">Back to Resources</Link>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
@@ -870,7 +878,7 @@ function DownloadDetailPage({ content }: { content: SiteContent }) {
|
||||
return (
|
||||
<main className="thanks-page download-detail-page" aria-label={resource.label}>
|
||||
<div className="thanks-card download-detail-card">
|
||||
<p className="eyebrow">Downloads</p>
|
||||
<p className="eyebrow">Resources</p>
|
||||
<h1>{resource.label}</h1>
|
||||
<p>{resource.summary}</p>
|
||||
|
||||
@@ -901,7 +909,7 @@ function DownloadDetailPage({ content }: { content: SiteContent }) {
|
||||
{resource.appleBooksLabel || 'Get it on Apple Books'}
|
||||
</a>
|
||||
)}
|
||||
<Link to="/resources" className="btn-secondary">Back to Downloads</Link>
|
||||
<Link to="/resources" className="btn-secondary">Back to Resources</Link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1289,7 +1297,7 @@ function LandingPage({ content }: { content: SiteContent }) {
|
||||
<SpotifyIcon />
|
||||
{content.seriesListenBtnLabel || 'Listen to Series'}
|
||||
</a>
|
||||
<Link to="/resources" className="btn-secondary">{content.seriesDownloadsBtnLabel || 'View Downloads'}</Link>
|
||||
<Link to="/resources" className="btn-secondary">{content.seriesDownloadsBtnLabel || 'View Resources'}</Link>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
@@ -1422,6 +1430,12 @@ function EpisodeDetailPage({ content }: { content: SiteContent }) {
|
||||
}
|
||||
}, [episode])
|
||||
|
||||
const siteTitle = content.seo?.title || DEFAULTS.seo.title
|
||||
usePageMeta(
|
||||
episode ? `${episode.title} | ${siteTitle}` : `Episode | ${siteTitle}`,
|
||||
episode?.summary || 'Expository Bible teaching from Verse by Verse with Nate.',
|
||||
)
|
||||
|
||||
if (!episode) {
|
||||
return (
|
||||
<div className="site">
|
||||
@@ -1894,14 +1908,14 @@ function BooksAndResourcesSection({ content }: { content: SiteContent }) {
|
||||
function ResourcesPage({ content }: { content: SiteContent }) {
|
||||
const siteTitle = content.seo?.title || DEFAULTS.seo.title
|
||||
usePageMeta(
|
||||
`Resources & Downloads | ${siteTitle}`,
|
||||
`Resources | ${siteTitle}`,
|
||||
'Study guides, sermon notes, and free resources from Verse by Verse with Nate.',
|
||||
)
|
||||
return (
|
||||
<div className="site">
|
||||
<SiteHeader content={content} />
|
||||
<SiteSearchBar content={content} />
|
||||
<PageBanner eyebrow="Downloads" title="Study Guides & Resources" desc="Free guides, worksheets, and curated resources to help you go deeper in Scripture." />
|
||||
<PageBanner eyebrow="Resources" title="Study Guides & Resources" desc="Free guides, worksheets, and curated resources to help you go deeper in Scripture." />
|
||||
<DownloadLibrarySection content={content} />
|
||||
<CustomBlocksSection content={content} page="downloads" />
|
||||
<BooksAndResourcesSection content={content} />
|
||||
@@ -1933,12 +1947,18 @@ function AboutPage({ content }: { content: SiteContent }) {
|
||||
}
|
||||
|
||||
function ContactPage({ content }: { content: SiteContent }) {
|
||||
const siteTitle = content.seo?.title || DEFAULTS.seo.title
|
||||
usePageMeta(
|
||||
`Contact | ${siteTitle}`,
|
||||
'Get in touch with Nate — submit a Bible question, testimony, or topic request.',
|
||||
)
|
||||
return (
|
||||
<div className="site">
|
||||
<SiteHeader content={content} />
|
||||
<SiteSearchBar content={content} />
|
||||
<PageBanner eyebrow="Contact" title={content.contactHeading || 'Get in Touch'} />
|
||||
<PageBanner eyebrow={content.contactEyebrow || 'Contact'} title={content.contactHeading || 'Get in Touch'} />
|
||||
<ContactSection content={content} />
|
||||
<KofiSupportSection content={content} />
|
||||
<CustomBlocksSection content={content} page="contact" />
|
||||
<SiteFooter content={content} />
|
||||
<AnalyticsConsentBanner content={content} />
|
||||
@@ -1947,38 +1967,49 @@ function ContactPage({ content }: { content: SiteContent }) {
|
||||
}
|
||||
|
||||
function StartHerePage({ content }: { content: SiteContent }) {
|
||||
const siteTitle = content.seo?.title || DEFAULTS.seo.title
|
||||
usePageMeta(
|
||||
`Start Here | ${siteTitle}`,
|
||||
content.startHereIntro || 'A beginner\'s guide to Verse by Verse with Nate.',
|
||||
)
|
||||
return (
|
||||
<main className="thanks-page" aria-label="Start Here">
|
||||
<div className="thanks-card" style={{ maxWidth: '980px', width: '100%' }}>
|
||||
<p className="eyebrow">Verse by Verse with Nate</p>
|
||||
<h1>{content.startHereHeading}</h1>
|
||||
<p>
|
||||
{content.startHereIntro}
|
||||
</p>
|
||||
<div className="site">
|
||||
<SiteHeader content={content} />
|
||||
<SiteSearchBar content={content} />
|
||||
<main className="thanks-page" aria-label="Start Here">
|
||||
<div className="thanks-card" style={{ maxWidth: '980px', width: '100%' }}>
|
||||
<p className="eyebrow">Verse by Verse with Nate</p>
|
||||
<h1>{content.startHereHeading}</h1>
|
||||
<p>
|
||||
{content.startHereIntro}
|
||||
</p>
|
||||
|
||||
<div className="start-grid" style={{ marginTop: '1.25rem' }}>
|
||||
<article className="start-card">
|
||||
<h3>{content.startHereStep1Title}</h3>
|
||||
<p>{content.startHereStep1Body}</p>
|
||||
<Link to="/episodes" className="btn-secondary">{content.startHereStep1Cta}</Link>
|
||||
</article>
|
||||
<article className="start-card">
|
||||
<h3>{content.startHereStep2Title}</h3>
|
||||
<p>{content.startHereStep2Body}</p>
|
||||
<a href="/#qa" className="btn-secondary">{content.startHereStep2Cta}</a>
|
||||
</article>
|
||||
<article className="start-card">
|
||||
<h3>{content.startHereStep3Title}</h3>
|
||||
<p>{content.startHereStep3Body}</p>
|
||||
<Link to="/contact" className="btn-primary">{content.startHereStep3Cta}</Link>
|
||||
</article>
|
||||
</div>
|
||||
<div className="start-grid" style={{ marginTop: '1.25rem' }}>
|
||||
<article className="start-card">
|
||||
<h3>{content.startHereStep1Title}</h3>
|
||||
<p>{content.startHereStep1Body}</p>
|
||||
<Link to="/episodes" className="btn-secondary">{content.startHereStep1Cta}</Link>
|
||||
</article>
|
||||
<article className="start-card">
|
||||
<h3>{content.startHereStep2Title}</h3>
|
||||
<p>{content.startHereStep2Body}</p>
|
||||
<Link to="/questions" className="btn-secondary">{content.startHereStep2Cta}</Link>
|
||||
</article>
|
||||
<article className="start-card">
|
||||
<h3>{content.startHereStep3Title}</h3>
|
||||
<p>{content.startHereStep3Body}</p>
|
||||
<Link to="/contact" className="btn-primary">{content.startHereStep3Cta}</Link>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '1.4rem' }}>
|
||||
<Link to="/" className="btn-secondary">Back to Site</Link>
|
||||
<div style={{ marginTop: '1.4rem' }}>
|
||||
<Link to="/" className="btn-secondary">Back to Site</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
<SiteFooter content={content} />
|
||||
<AnalyticsConsentBanner content={content} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2664,8 +2695,7 @@ export default function App() {
|
||||
<Route path="/" element={<LandingPage content={content} />} />
|
||||
<Route path="/start-here" element={<StartHerePage content={content} />} />
|
||||
<Route path="/study" element={<StudyRouteFrame content={content} child={<StudyLandingPage content={content} />} />} />
|
||||
<Route path="/studys" element={<StudyRouteFrame content={content} child={<StudyLandingPage content={content} />} />} />
|
||||
<Route path="/study/signup" element={<StudyRouteFrame content={content} child={<StudySignupPage />} />} />
|
||||
<Route path="/study/signup" element={<StudyRouteFrame content={content} child={<StudySignupPage />} />} />
|
||||
<Route path="/study/account" element={<StudyRouteFrame content={content} child={<StudyAccountPage />} />} />
|
||||
<Route path="/study/:studySlug" element={<StudyRouteFrame content={content} child={<ColossiansStudyIndexPage content={content} />} />} />
|
||||
<Route path="/study/:studySlug/community" element={<StudyRouteFrame content={content} child={<StudyCommunityPage content={content} />} />} />
|
||||
|
||||
Reference in new issue
Block a user