Enforce study lesson release locks and schedule biweekly Monday dates

This commit is contained in:
nmemmert
2026-05-12 15:09:38 -04:00
parent 82e9443db8
commit d74151d91d
5 changed files with 154 additions and 83 deletions
+38 -7
View File
@@ -78,6 +78,13 @@ function isComingSoon(section: StudySection): boolean {
return releaseDate > now
}
function isSectionReleased(section: StudySection): boolean {
if (!section.releasedAt) return true
const releaseDate = new Date(section.releasedAt)
if (Number.isNaN(releaseDate.getTime())) return true
return releaseDate <= new Date()
}
function getReleaseDateDisplay(section: StudySection): string {
if (!section.releasedAt) return ''
const date = new Date(section.releasedAt)
@@ -102,6 +109,7 @@ export function StudyLandingPage({ content }: Props) {
const activeStudies = studies.filter(study => study.status !== 'planned')
const plannedStudies = studies.filter(study => study.status === 'planned')
const firstActiveStudy = activeStudies[0]
const firstActiveStudyFirstReleasedSection = firstActiveStudy?.sections.find(isSectionReleased)
return (
<main className="study-index-page" aria-label="Study hub">
@@ -117,7 +125,7 @@ export function StudyLandingPage({ content }: Props) {
<span>Audio + commentary</span>
</div>
<div className="study-course-hero-actions">
{firstActiveStudy && <Link to={`/study/${firstActiveStudy.slug}`} className="btn-primary">Start Learning</Link>}
{firstActiveStudyFirstReleasedSection && <Link to={`/study/${firstActiveStudy.slug}/${firstActiveStudyFirstReleasedSection.id}`} className="btn-primary">Start Learning</Link>}
<Link to="#study-tracks" className="btn-secondary">Browse Tracks</Link>
<Link to="/study/signup" className="btn-primary">Create Account or Login</Link>
</div>
@@ -158,6 +166,9 @@ export function StudyLandingPage({ content }: Props) {
</div>
<div className="study-module-list">
{activeStudies.map(study => (
(() => {
const releasedCount = study.sections.filter(isSectionReleased).length
return (
<Link key={study.id} to={`/study/${study.slug}`} className="study-module-row">
<div className="study-module-row-left">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '0.5rem', flexWrap: 'wrap' }}>
@@ -170,11 +181,13 @@ export function StudyLandingPage({ content }: Props) {
</div>
<div className="study-module-row-right">
<p className="study-module-focus">Track Snapshot</p>
<p>{study.sections.length > 0 ? `${study.sections.length} lessons available` : 'Lessons will be published soon.'}</p>
<p>{study.sections.length > 0 ? 'Estimated pace: 1-2 lessons per week' : 'Pacing details coming with first lesson release.'}</p>
<p>{releasedCount > 0 ? `${releasedCount} lessons available now` : 'Lessons will be published soon.'}</p>
<p>{study.sections.length > 0 ? 'Estimated pace: every other Monday' : 'Pacing details coming with first lesson release.'}</p>
</div>
</Link>
))}
)
})()
))}
</div>
</div>
</section>
@@ -357,6 +370,8 @@ export function ColossiansStudyIndexPage({ content }: Props) {
const sections = study.sections
const chapterSummaries = getChapterSummaries(study)
const releasedSections = sections.filter(isSectionReleased)
const firstReleasedSection = releasedSections[0]
const populatedChapterCount = chapterSummaries.filter(chapter => chapter.lessonCount > 0).length
return (
@@ -367,14 +382,14 @@ export function ColossiansStudyIndexPage({ content }: Props) {
<h1>{study.title}</h1>
<p className="study-course-hero-copy">{study.description}</p>
<div className="study-course-meta">
<span>{sections.length} lessons</span>
<span>{releasedSections.length} lessons available now</span>
<span>{chapterSummaries.length} chapters</span>
<span>{populatedChapterCount} chapters with lessons</span>
<span>Text + commentary + discussion</span>
<span>Student notes enabled</span>
</div>
<div className="study-course-hero-actions">
{sections.length > 0 && <Link to={`/study/${study.slug}/${sections[0].id}`} className="btn-primary">Start Class</Link>}
{firstReleasedSection && <Link to={`/study/${study.slug}/${firstReleasedSection.id}`} className="btn-primary">Start Class</Link>}
<Link to={`/study/${study.slug}/notes`} className="btn-secondary">My Notes</Link>
<Link to="/study" className="btn-secondary">Back to Studies</Link>
</div>
@@ -446,6 +461,7 @@ export function ColossiansStudySectionPage({ content }: Props) {
const lessonNumber = getLessonNumber(sections, sectionId)
const previousSection = sectionIndex > 0 ? sections[sectionIndex - 1] : null
const nextSection = sectionIndex >= 0 && sectionIndex < sections.length - 1 ? sections[sectionIndex + 1] : null
const sectionIsReleased = section ? isSectionReleased(section) : false
const [auth, setAuth] = useState<StudyAuthState>({ checked: false, authenticated: false, username: '' })
const [usernameInput, setUsernameInput] = useState('')
@@ -587,6 +603,19 @@ export function ColossiansStudySectionPage({ content }: Props) {
)
}
if (!sectionIsReleased) {
return (
<main className="thanks-page" aria-label="Lesson not yet available">
<div className="thanks-card">
<p className="eyebrow">{study.title}</p>
<h1>Lesson Coming Soon</h1>
<p>This lesson will be available on {getReleaseDateDisplay(section)}.</p>
<Link to={`/study/${study.slug}`} className="btn-primary">Back to Lessons</Link>
</div>
</main>
)
}
return (
<main className="study-section-page" aria-label={section.title}>
<section className="section-study-classroom">
@@ -697,8 +726,10 @@ export function ColossiansStudySectionPage({ content }: Props) {
{previousSection ? (
<Link to={`/study/${study.slug}/${previousSection.id}`} className="btn-secondary">Previous Lesson</Link>
) : <span className="study-nav-placeholder" />}
{nextSection ? (
{nextSection && isSectionReleased(nextSection) ? (
<Link to={`/study/${study.slug}/${nextSection.id}`} className="btn-secondary">Next Lesson</Link>
) : nextSection ? (
<span className="study-nav-placeholder">Available {getReleaseDateDisplay(nextSection)}</span>
) : <span className="study-nav-placeholder" />}
</div>
</article>