// Form for creating or editing a custom reading plan (title, days, cover photo, // books and chapter ranges). Themed with the reader's --r-* colour variables. import { useRef, useState } from 'react'; import { bookOptions, CHAPTER_COUNTS } from '../../utils/bibleUtils.jsx'; import { bookChapterCount, clampPlanDays, ALL_BOOKS, OT_BOOKS, NT_BOOKS, GOSPELS, PENTATEUCH, WISDOM_BOOKS, } from '../../utils/readingPlans.js'; // Downscales a picked image file to a reasonable cover-photo size and returns // a data URL — no server upload endpoint needed, it's stored right on the plan. export function resizeImageToDataUrl(file, maxWidth = 960, quality = 0.82) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onerror = () => reject(new Error('Could not read file.')); reader.onload = () => { const img = new Image(); img.onerror = () => reject(new Error('Could not decode image.')); img.onload = () => { const scale = Math.min(1, maxWidth / img.width); const canvas = document.createElement('canvas'); canvas.width = Math.round(img.width * scale); canvas.height = Math.round(img.height * scale); canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height); resolve(canvas.toDataURL('image/jpeg', quality)); }; img.src = reader.result; }; reader.readAsDataURL(file); }); } const BOOK_PRESETS = [ ['All', ALL_BOOKS], ['None', []], ['Old Testament', OT_BOOKS], ['New Testament', NT_BOOKS], ['Gospels', GOSPELS], ['Pentateuch', PENTATEUCH], ['Psalms', ['PSA']], ['Proverbs', ['PRO']], ['Wisdom', WISDOM_BOOKS], ]; export default function CustomPlanBuilder({ initialValues, onSubmit, onCancel }) { const [title, setTitle] = useState(initialValues?.title ?? ''); const [description, setDescription] = useState(initialValues?.description ?? ''); const [days, setDays] = useState(initialValues?.days ?? 30); const [books, setBooks] = useState(initialValues?.books ?? []); const [chapterRanges, setChapterRanges] = useState(initialValues?.chapterRanges ?? {}); const [image, setImage] = useState(initialValues?.image ?? null); const [imageError, setImageError] = useState(''); const fileInputRef = useRef(null); const isEditing = !!initialValues; const toggleBook = (abbrev) => { const isOn = books.includes(abbrev); setBooks((prev) => (isOn ? prev.filter((b) => b !== abbrev) : [...prev, abbrev])); if (isOn) { setChapterRanges((prev) => { if (!(abbrev in prev)) return prev; const next = { ...prev }; delete next[abbrev]; return next; }); } }; const setChapterRange = (abbrev, key, value) => { const total = CHAPTER_COUNTS[abbrev] ?? 1; const clamped = Math.min(total, Math.max(1, Math.round(value) || 1)); setChapterRanges((prev) => ({ ...prev, [abbrev]: { start: prev[abbrev]?.start ?? 1, end: prev[abbrev]?.end ?? total, [key]: clamped }, })); }; const applyPreset = (list) => { setBooks(list); setChapterRanges({}); }; const pickImage = async (e) => { const file = e.target.files?.[0]; e.target.value = ''; if (!file) return; try { setImageError(''); setImage(await resizeImageToDataUrl(file)); } catch { setImageError("Couldn't use that image."); } }; const totalChapters = books.reduce((sum, b) => sum + bookChapterCount(b, chapterRanges), 0); const canCreate = title.trim().length > 0 && totalChapters > 0 && days >= 1; return (

{isEditing ? 'Edit reading plan' : 'New reading plan'}