Add a study-picker discussion hub and require sign-in to view it; v2.2.0
Publish Container / build (push) Successful in 16m31s
Publish Container / build (push) Successful in 16m31s
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
c92bb2189a
commit
3d3c7175a1
3 files changed
+111
-6
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "siteforge",
|
||||
"private": true,
|
||||
"version": "2.1.1",
|
||||
"version": "2.2.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
+3
-5
@@ -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 }) {
|
||||
<div className="nav-dropdown-menu">
|
||||
<div className="nav-dropdown-menu-inner">
|
||||
<NavLink to="/study" className={({ isActive }) => `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>All Studies</NavLink>
|
||||
{communityStudy && (
|
||||
<NavLink to={`/study/${communityStudy.slug}/community`} className={({ isActive }) => `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>Discussion</NavLink>
|
||||
)}
|
||||
<NavLink to="/study/discussion" className={({ isActive }) => `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>Discussion</NavLink>
|
||||
<NavLink to="/scripture" className={({ isActive }) => `nav-dropdown-item${isActive ? ' nav-dropdown-item--active' : ''}`} onClick={() => { setMenuOpen(false); setStudiesOpen(false) }}>Scripture Index</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3216,6 +3213,7 @@ export default function App() {
|
||||
<Route path="/study/reset-password" element={<StudyRouteFrame content={content} child={<StudyResetPasswordPage />} />} />
|
||||
<Route path="/study/verify-email" element={<StudyRouteFrame content={content} child={<StudyVerifyEmailPage />} />} />
|
||||
<Route path="/study/account" element={<StudyRouteFrame content={content} child={<StudyAccountPage />} />} />
|
||||
<Route path="/study/discussion" element={<StudyRouteFrame content={content} child={<StudyDiscussionHubPage content={content} />} />} />
|
||||
<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} />} />} />
|
||||
<Route path="/study/:studySlug/notes" element={<StudyRouteFrame content={content} child={<ColossiansStudyNotesPage content={content} />} />} />
|
||||
|
||||
@@ -3994,6 +3994,92 @@ export function StudyQuizPage({ content }: Props) {
|
||||
)
|
||||
}
|
||||
|
||||
export function StudyDiscussionHubPage({ content }: Props) {
|
||||
const studies = getStudies(content)
|
||||
const [auth, setAuth] = useState<StudyAuthState>({ checked: false, authenticated: false, username: '', enrolledStudySlugs: [] })
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
readJson<StudyAuthStatusResponse>('/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 (
|
||||
<main className="thanks-page" aria-label="Loading">
|
||||
<div className="thanks-card"><p>Loading...</p></div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
if (!auth.authenticated) {
|
||||
return (
|
||||
<main className="thanks-page" aria-label="Sign in required">
|
||||
<div className="thanks-card">
|
||||
<p className="eyebrow">Study Discussion</p>
|
||||
<h1>Sign In Required</h1>
|
||||
<p>Create a free student account to see and join the conversation with other students.</p>
|
||||
<Link to="/study/signup" className="btn-primary" style={{ marginTop: '1rem', display: 'inline-block' }}>Sign In</Link>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="study-index-page" aria-label="Study discussion">
|
||||
<section className="section-study-course-hero">
|
||||
<div className="section-inner study-course-hero-inner">
|
||||
<p className="eyebrow">Student Community</p>
|
||||
<h1>Study Discussion</h1>
|
||||
<p className="study-course-hero-copy">Pick a study track to see what other students are saying, ask questions, and share what God is teaching you.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section-inner" style={{ paddingTop: '1rem', paddingBottom: '4rem' }}>
|
||||
{studies.length === 0 ? (
|
||||
<p className="study-detail-copy">No study tracks are available yet.</p>
|
||||
) : (
|
||||
<div className="study-module-list">
|
||||
{studies.map(study => {
|
||||
const enrolled = isEnrolledInStudy(auth, study.slug)
|
||||
return (
|
||||
<article key={study.slug} className="study-module-row">
|
||||
<div className="study-module-row-left">
|
||||
<p className="study-module-lesson">{enrolled ? 'Enrolled' : 'Study track'}</p>
|
||||
<h3>{study.title}</h3>
|
||||
<p>{study.description || 'Join the discussion for this study track.'}</p>
|
||||
</div>
|
||||
<div className="study-module-row-right" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||||
<Link to={`/study/${study.slug}/community`} className="btn-primary">
|
||||
{enrolled ? 'Open Discussion' : 'View Discussion'}
|
||||
</Link>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<main className="thanks-page" aria-label="Loading">
|
||||
<div className="thanks-card"><p>Loading...</p></div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
if (!auth.authenticated) {
|
||||
return (
|
||||
<main className="thanks-page" aria-label="Sign in required">
|
||||
<div className="thanks-card">
|
||||
<p className="eyebrow">Study Discussion</p>
|
||||
<h1>Sign In Required</h1>
|
||||
<p>Create a free student account to see and join the conversation.</p>
|
||||
<Link to="/study/signup" className="btn-primary" style={{ marginTop: '1rem', display: 'inline-block' }}>Sign In</Link>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
if (!study) {
|
||||
return (
|
||||
<main className="study-index-page" aria-label="Community">
|
||||
|
||||
Reference in new issue
Block a user