Two concurrent series lanes, Complete studies, self-paced finished books; v2.9.0
Publish Container / build (push) Successful in 17m50s

- Study status 'complete' (manual or auto when all lessons released); stays enrollable and self-paced (no sequential lock, Resume button, Finished Studies hub section)
- Studies get lane (A/B) and expected start date; planned studies show "Starts <date>"; hub shows next lesson date instead of hardcoded pace
- Second concurrent series (homepage card, admin panel) with its own scheduled launch; transitions are per lane and flip the ending study to Complete and the incoming study to Active
- Finished books link to their study: "Study This Book" on playlist page, audio-only link from hub (playlist stays public, no account)
- Admin: bulk release scheduler (every N days), same-day collision warning, linked-study selects
- Weekly digest no longer dumps a finished study's backlog on late enrollees
- CI: build and tag the container as :beta on pushes to the beta branch

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-08 10:14:02 -04:00
1 parent 2d8e4be5ea
commit ddcaec0ddf
12 files changed
+628 -105

No files matched your search

+54 -22
View File
@@ -1314,28 +1314,55 @@ function LandingPage({ content }: { content: SiteContent }) {
<span className="ornament">✦</span>
</h2>
<div className="series-grid">
<article className="series-card">
<img
src={content.seriesImageUrl}
alt={content.seriesTitle}
className="series-art"
/>
<div className="series-info">
<p className="series-label">{content.seriesLabel}</p>
<h3>{content.seriesTitle}</h3>
<p>{content.seriesDescription}</p>
<a
href={content.seriesListenUrl || content.platformSpotifyUrl || '/spotify'}
target="_blank"
rel="noreferrer"
className="btn-primary"
>
<SpotifyIcon />
{content.seriesListenBtnLabel || 'Listen to Series'}
</a>
<Link to="/resources" className="btn-secondary">{content.seriesDownloadsBtnLabel || 'View Resources'}</Link>
</div>
</article>
{[
{
key: 'primary',
imageUrl: content.seriesImageUrl,
title: content.seriesTitle,
label: content.seriesLabel,
description: content.seriesDescription,
listenUrl: content.seriesListenUrl,
listenBtnLabel: content.seriesListenBtnLabel,
studySlug: content.seriesStudySlug,
},
...(content.secondSeries?.title?.trim()
? [{
key: 'second',
imageUrl: content.secondSeries.imageUrl,
title: content.secondSeries.title,
label: content.secondSeries.label,
description: content.secondSeries.description,
listenUrl: content.secondSeries.listenUrl,
listenBtnLabel: content.secondSeries.listenBtnLabel,
studySlug: content.secondSeries.studySlug,
}]
: []),
].map(series => (
<article className="series-card" key={series.key}>
<img
src={series.imageUrl}
alt={series.title}
className="series-art"
/>
<div className="series-info">
<p className="series-label">{series.label}</p>
<h3>{series.title}</h3>
<p>{series.description}</p>
<a
href={series.listenUrl || content.platformSpotifyUrl || '/spotify'}
target="_blank"
rel="noreferrer"
className="btn-primary"
>
<SpotifyIcon />
{series.listenBtnLabel || content.seriesListenBtnLabel || 'Listen to Series'}
</a>
{series.studySlug
? <Link to={`/study/${series.studySlug}`} className="btn-secondary">Study Along</Link>
: <Link to="/resources" className="btn-secondary">{content.seriesDownloadsBtnLabel || 'View Resources'}</Link>}
</div>
</article>
))}
</div>
</div>
</section>
@@ -1708,6 +1735,11 @@ function FinishedSeriesPage({ content }: { content: SiteContent }) {
⬇ Download Album
</a>
)}
{book.studySlug && (
<Link to={`/study/${book.studySlug}`} className="finished-series-btn-download">
📖 Study This Book
</Link>
)}
</div>
{book.downloadQrUrl && book.downloadUrl && (
<div className="finished-series-qr">