Add Fetch from BSB button to admin lesson editor; v1.1.36

Adds a one-click "Fetch from BSB" button in the lesson passage text field
that calls the BSB API and overwrites the field with verse-numbered text.
Also fixes reference parsing to tolerate leading book abbreviations like
t:1-2 or col:1-2 by stripping letters before the chapter:verse pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-08-10 10:23:48 -04:00
parent 9e602e29ec
commit 2ba9c9f7f4
4 changed files with 34 additions and 5 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
{
"users": {},
"updatedAt": "2026-08-10T14:07:00.476Z"
"updatedAt": "2026-08-10T14:22:31.192Z"
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "siteforge",
"private": true,
"version": "1.1.35",
"version": "1.1.36",
"type": "module",
"scripts": {
"dev": "vite",
+2 -1
View File
@@ -18,7 +18,8 @@ const BOLLS_BOOK_MAP = {
}
function parseReference(reference) {
const match = reference.match(/^(\d+):(\d+)(?:-(\d+))?$/)
const stripped = reference.replace(/^[a-z]+/i, '')
const match = stripped.match(/^(\d+):(\d+)(?:-(\d+))?$/)
if (!match) return null
return {
chapter: parseInt(match[1], 10),
+30 -2
View File
@@ -21,6 +21,28 @@ interface SortableLessonSectionProps {
function SortableLessonSection({ section, study, updateStudySection, removeStudySection, rssEpisodes }: SortableLessonSectionProps) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: section.id })
const [bsbFetching, setBsbFetching] = useState(false)
const [bsbMsg, setBsbMsg] = useState('')
async function fetchBsbPassage() {
const bookKey = (study.bibleBook?.trim() || study.slug).toLowerCase()
const reference = section.reference?.trim()
if (!bookKey || !reference) { setBsbMsg('Set the passage reference first.'); return }
setBsbFetching(true)
setBsbMsg('')
try {
const params = new URLSearchParams({ book: bookKey, reference })
const res = await fetch(`/api/bible-passage?${params}`)
const data = await res.json() as { text?: string; message?: string }
if (!res.ok || !data.text) { setBsbMsg(data.message ?? 'Passage not found.'); return }
updateStudySection(study.id, section.id, 'passageText', data.text)
setBsbMsg('Passage filled from BSB.')
} catch {
setBsbMsg('Could not reach the BSB API.')
} finally {
setBsbFetching(false)
}
}
const style = {
transform: CSS.Transform.toString(transform),
transition,
@@ -94,8 +116,14 @@ function SortableLessonSection({ section, study, updateStudySection, removeStudy
<textarea id={`study-section-announcement-${study.id}-${section.id}`} rows={3} value={section.announcement ?? ''} placeholder="A short instructor announcement for this lesson." onChange={e => updateStudySection(study.id, section.id, 'announcement', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`study-section-passage-${study.id}-${section.id}`}>Passage Text</label>
<textarea id={`study-section-passage-${study.id}-${section.id}`} rows={4} value={section.passageText} placeholder="Paste the passage text here." onChange={e => updateStudySection(study.id, section.id, 'passageText', e.target.value)} />
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap', marginBottom: '0.35rem' }}>
<label htmlFor={`study-section-passage-${study.id}-${section.id}`} style={{ margin: 0 }}>Passage Text</label>
<button type="button" className="btn-admin-secondary" disabled={bsbFetching} onClick={fetchBsbPassage} style={{ fontSize: '0.8rem', padding: '0.2rem 0.75rem' }}>
{bsbFetching ? 'Fetching…' : 'Fetch from BSB'}
</button>
{bsbMsg && <span style={{ fontSize: '0.8rem', color: bsbMsg.includes('filled') ? '#7abf7a' : '#c97070' }}>{bsbMsg}</span>}
</div>
<textarea id={`study-section-passage-${study.id}-${section.id}`} rows={4} value={section.passageText} placeholder="Paste the passage text here, or use Fetch from BSB." onChange={e => updateStudySection(study.id, section.id, 'passageText', e.target.value)} />
</div>
<div className="admin-field">
<label htmlFor={`study-section-commentary-${study.id}-${section.id}`}>Commentary</label>