From 3d3c7175a1dc27d8ce03230180a47e3e7e8af43f Mon Sep 17 00:00:00 2001 From: nmemmert Date: Thu, 24 Sep 2026 10:19:20 -0400 Subject: [PATCH] Add a study-picker discussion hub and require sign-in to view it; v2.2.0 The "Discussion" nav link previously jumped straight into the featured study's community board. Adds /study/discussion as a proper landing page that lists every study track so students pick which one to discuss, then links into /study/:slug/community from there. Both pages now require an authenticated session to view at all (previously the per-study community page rendered its shell to logged-out visitors, only gating the post feed itself). Co-Authored-By: Claude Sonnet 5 --- package.json | 2 +- src/App.tsx | 8 ++-- src/studyPages.tsx | 107 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 639f108..2360e62 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "siteforge", "private": true, - "version": "2.1.1", + "version": "2.2.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.tsx b/src/App.tsx index 9538b4f..e41e504 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ import { Link, NavLink, Routes, Route, useLocation, useNavigate, useParams } fro import AdminPage from './AdminPage' import QASection from './components/QASection' import ContactForm from './components/ContactForm' -import { ColossiansStudyIndexPage, ColossiansStudyNotesPage, ColossiansStudySectionPage, StudyLandingPage, StudySignupPage, StudyAccountPage, StudyCommunityPage, StudyQuizPage, StudyResetPasswordPage, StudyVerifyEmailPage } from './studyPages' +import { ColossiansStudyIndexPage, ColossiansStudyNotesPage, ColossiansStudySectionPage, StudyLandingPage, StudySignupPage, StudyAccountPage, StudyCommunityPage, StudyDiscussionHubPage, StudyQuizPage, StudyResetPasswordPage, StudyVerifyEmailPage } from './studyPages' import { SpotifyIcon } from './icons' import type { SiteContent, StudyProgram, Testimonial } from './content' import { DEFAULTS } from './content' @@ -386,7 +386,6 @@ function SiteHeader({ content }: { content: SiteContent }) { const location = useLocation() const episodesActive = location.pathname.startsWith('/episodes') || location.pathname.startsWith('/finished') || location.pathname.startsWith('/nuggets') const studiesActive = location.pathname.startsWith('/study') || location.pathname === '/scripture' - const communityStudy = getHomepageFeaturedStudy(content) useEffect(() => { function handleOutsideClick(e: MouseEvent) { @@ -445,9 +444,7 @@ function SiteHeader({ content }: { content: SiteContent }) {
`nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>All Studies - {communityStudy && ( - `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>Discussion - )} + `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>Discussion `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>Scripture Index
@@ -3216,6 +3213,7 @@ export default function App() { } />} /> } />} /> } />} /> + } />} /> } />} /> } />} /> } />} /> diff --git a/src/studyPages.tsx b/src/studyPages.tsx index e8acf8f..6515545 100644 --- a/src/studyPages.tsx +++ b/src/studyPages.tsx @@ -3994,6 +3994,92 @@ export function StudyQuizPage({ content }: Props) { ) } +export function StudyDiscussionHubPage({ content }: Props) { + const studies = getStudies(content) + const [auth, setAuth] = useState({ checked: false, authenticated: false, username: '', enrolledStudySlugs: [] }) + + useEffect(() => { + let cancelled = false + readJson('/api/study-auth/status') + .then(data => { + if (cancelled) return + setAuth({ + checked: true, + authenticated: Boolean(data.authenticated), + username: data.username ?? '', + enrolledStudySlugs: normalizeEnrolledStudySlugs(data.enrolledStudySlugs), + }) + }) + .catch(() => { + if (cancelled) return + setAuth({ checked: true, authenticated: false, username: '', enrolledStudySlugs: [] }) + }) + return () => { + cancelled = true + } + }, []) + + usePageMeta('Study Discussion | Bible Study', 'Pick a study track and join the conversation with other students.') + + if (!auth.checked) { + return ( +
+

Loading...

+
+ ) + } + + if (!auth.authenticated) { + return ( +
+
+

Study Discussion

+

Sign In Required

+

Create a free student account to see and join the conversation with other students.

+ Sign In +
+
+ ) + } + + return ( +
+
+
+

Student Community

+

Study Discussion

+

Pick a study track to see what other students are saying, ask questions, and share what God is teaching you.

+
+
+
+ {studies.length === 0 ? ( +

No study tracks are available yet.

+ ) : ( +
+ {studies.map(study => { + const enrolled = isEnrolledInStudy(auth, study.slug) + return ( +
+
+

{enrolled ? 'Enrolled' : 'Study track'}

+

{study.title}

+

{study.description || 'Join the discussion for this study track.'}

+
+
+ + {enrolled ? 'Open Discussion' : 'View Discussion'} + +
+
+ ) + })} +
+ )} +
+
+ ) +} + export function StudyCommunityPage({ content }: Props) { const { studySlug } = useParams<{ studySlug: string }>() const location = useLocation() @@ -4012,6 +4098,27 @@ export function StudyCommunityPage({ content }: Props) { .catch(() => setAuth(prev => ({ ...prev, checked: true }))) }, []) + if (!auth.checked) { + return ( +
+

Loading...

+
+ ) + } + + if (!auth.authenticated) { + return ( +
+
+

Study Discussion

+

Sign In Required

+

Create a free student account to see and join the conversation.

+ Sign In +
+
+ ) + } + if (!study) { return (