Scripture Index: verse of the week (admin override or weekly rotation through covered passages); v2.45.0
Publish Container / build (push) Successful in 17m33s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-15 11:50:19 -04:00
1 parent e9d9dd0d50
commit a1adfa0ef1
6 files changed
+75 -4

No files matched your search

+50
View File
@@ -2838,6 +2838,38 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
return promise
}
// Verse of the week: the admin's pick if set, otherwise rotate through covered passages by calendar week
const [weekIndex] = useState(() => Math.floor(Date.now() / (7 * 24 * 60 * 60 * 1000)))
const verseOfWeek = useMemo(() => {
const override = content.verseOfTheWeek?.trim()
const parsed = override ? parseSIRef(override) : null
if (parsed) {
const key = `${parsed.bookId}-${parsed.chapter}-${parsed.verseStart}-${parsed.endChapter}-${parsed.verseEnd}`
const existing = bookGroups.flatMap(g => g.rows).find(r => r.key === key)
return existing ?? {
key,
displayRef: `${SI_BOOK_NAME[parsed.bookId]} ${parsed.chapter}:${parsed.verseStart}${parsed.verseEnd !== parsed.verseStart ? `-${parsed.verseEnd}` : ''}`,
bookId: parsed.bookId, chapter: parsed.chapter, endChapter: parsed.endChapter,
verseStart: parsed.verseStart, verseEnd: parsed.verseEnd, sources: [],
} as ScriptureRow
}
const pool = bookGroups.flatMap(g => g.rows)
if (pool.length === 0) return null
return pool[weekIndex % pool.length]
}, [bookGroups, content.verseOfTheWeek, weekIndex])
const [votText, setVotText] = useState<string | null>(null)
const votKey = verseOfWeek?.key
useEffect(() => {
if (!verseOfWeek || !verseOfWeek.bookId) return
let cancelled = false
const verseEnd = verseOfWeek.endChapter !== verseOfWeek.chapter ? Number.MAX_SAFE_INTEGER : verseOfWeek.verseEnd
fetchChapter(verseOfWeek.bookId, verseOfWeek.chapter)
.then(ch => { if (!cancelled) setVotText(extractVerseRange(ch, verseOfWeek.verseStart, verseEnd) || null) })
.catch(() => { if (!cancelled) setVotText(null) })
return () => { cancelled = true }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [votKey])
function toggleVerse(row: ScriptureRow) {
const open = shownVerses.has(row.key)
setShownVerses(prev => {
@@ -2921,6 +2953,24 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
</div>
</div>
{!filtering && verseOfWeek && (
<div className="si-vow">
<p className="si-vow-eyebrow">Verse of the week</p>
<h2 className="si-vow-ref">{verseOfWeek.displayRef}</h2>
{votText && <p className="si-vow-text">“{votText}”</p>}
{verseOfWeek.sources.length > 0 && (
<div className="si-sources">
{verseOfWeek.sources.slice(0, 3).map((src, i) => (
<Link key={i} to={src.url} className={`si-chip si-chip--${src.type}`}>
<span className="si-chip-kind">{src.type === 'episode' ? 'Episode' : 'Study'}</span>
<span className="si-chip-label">{src.label}</span>
</Link>
))}
</div>
)}
</div>
)}
{!filtering && topRows.length > 0 && (
<div className="si-top" aria-label="Most-studied passages">
<h2 className="si-top-title">Most-studied passages</h2>