Analytics: unique visitors this week + probed-URLs list; Scripture Index: most-studied strip + print; admin warning for unparseable passage references; v2.44.0

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-15 11:38:26 -04:00
1 parent 6a5d525d6e
commit e9d9dd0d50
13 files changed
+143 -9

No files matched your search

+23 -2
View File
@@ -2735,7 +2735,7 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
// Merge entries that share the same reference (a study lesson and an episode can both
// cover the same passage) into one row, then group rows by book in canonical Bible order.
const { bookGroups, otherRows } = useMemo(() => {
const { bookGroups, otherRows, topRows } = useMemo(() => {
const rowMap = new Map<string, ScriptureRow>()
const order: string[] = []
for (const entry of entries) {
@@ -2789,7 +2789,12 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
rows: groupsByBook.get(id)!.sort((a, b) => a.chapter - b.chapter || a.verseStart - b.verseStart),
}))
other.sort((a, b) => a.displayRef.localeCompare(b.displayRef))
return { bookGroups: groups, otherRows: other }
// Passages covered by the most lessons/episodes (needs at least two sources to be worth highlighting)
const top = [...rowMap.values()]
.filter(r => r.bookId && r.sources.length >= 2)
.sort((a, b) => b.sources.length - a.sources.length || order.indexOf(a.key) - order.indexOf(b.key))
.slice(0, 6)
return { bookGroups: groups, otherRows: other, topRows: top }
}, [entries])
// Apply the search box and filters
@@ -2886,6 +2891,7 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
<PageBanner eyebrow="Reference" title="Scripture Index" desc="Every passage covered across our studies and episodes, organized by Scripture reference. Find the lesson or episode for any verse." />
<main className="si-main">
<div className="section-inner">
<h1 className="si-print-title">Scripture Index — {siteTitle}</h1>
{loading ? (
<p className="si-note">Loading…</p>
) : !hasContent ? (
@@ -2915,6 +2921,20 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
</div>
</div>
{!filtering && topRows.length > 0 && (
<div className="si-top" aria-label="Most-studied passages">
<h2 className="si-top-title">Most-studied passages</h2>
<div className="si-top-list">
{topRows.map(r => (
<button key={r.key} type="button" className="si-top-item" onClick={() => setQuery(r.displayRef)}>
<span className="si-top-ref">{r.displayRef}</span>
<span className="si-top-count">{r.sources.length} sources</span>
</button>
))}
</div>
</div>
)}
{bookGroups.length > 1 && (
<div className="si-books" role="group" aria-label="Books covered">
{bookGroups.map(g => (
@@ -2938,6 +2958,7 @@ function ScriptureIndexPage({ content }: { content: SiteContent }) {
)}
<button type="button" className="si-link" onClick={() => setAll(true)}>Expand all</button>
<button type="button" className="si-link" onClick={() => setAll(false)}>Collapse all</button>
<button type="button" className="si-link" onClick={() => { setAll(true); window.setTimeout(() => window.print(), 150) }}>Print index</button>
</span>
</div>