Files
study-bible-project/src/components/SessionSchedule.jsx
T
nmemmertandClaude Sonnet 5.5 0f1a181d87 Rename the Export stage to Finish
It now holds files, sharing, handouts, slides, importing notes, schedule,
co-teachers and earlier versions, not just exports.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 13:17:08 -04:00

65 lines
3.7 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Finish → Schedule: the study's sessions (one per chunk) as a calendar file, starting on a date you pick.
import { useState } from 'react';
import { formatChunkReference } from '../utils/bibleUtils.jsx';
import { buildIcs, downloadIcs, sessionDates } from '../utils/icsFile.js';
const field = 'rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-2.5 py-1.5 text-sm text-[var(--r-text)] focus:outline-none focus:ring-2 focus:ring-[var(--r-line)]';
const isoDay = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
// The study's sessions in order: { title, description } for each chunk.
export function sessionsOf(project) {
const list = [];
(project?.chapters ?? []).forEach((chapter, chapterIndex) => {
for (const chunk of chapter.chunks ?? []) {
const ref = formatChunkReference(project, chapterIndex, chunk, '–');
const n = list.length + 1;
const label = `Session ${(chunk.episodeNumber ?? '').toString().trim() || n}`;
const name = (project.series ?? '').trim() || project.title;
list.push({ title: `${name} — ${label}: ${(chunk.episodeTitle ?? '').trim() || ref}`, description: `${project.title}\n${ref}` });
}
});
return list;
}
export default function SessionSchedule({ project }) {
const sessions = sessionsOf(project);
const [start, setStart] = useState(() => isoDay(new Date()));
const [every, setEvery] = useState('7');
const [time, setTime] = useState('');
const [minutes, setMinutes] = useState('60');
if (sessions.length === 0) return null;
const download = () => {
const [y, m, d] = start.split('-').map(Number);
const dates = sessionDates(new Date(y, m - 1, d), sessions.length, Number(every));
const events = sessions.map((s, i) => ({ ...s, date: dates[i], time: time || undefined, minutes: Number(minutes) || 60 }));
downloadIcs(buildIcs(events, { name: (project.series ?? '').trim() || project.title }), `${(project.title || 'study').replace(/[^\w]+/g, '-').toLowerCase()}-schedule.ics`);
};
return (
<section className="rounded-2xl border border-[var(--r-line)] bg-[var(--r-bar)] p-5" aria-label="Schedule">
<h2 className="text-sm font-medium">Schedule</h2>
<p className="mb-3 mt-1 text-xs text-[var(--r-sub)]">Put the {sessions.length} session{sessions.length === 1 ? '' : 's'} on your calendar: one per chunk, in order, using each chunk’s session number and title.</p>
<div className="flex flex-wrap items-end gap-3">
<label className="text-xs text-[var(--r-sub)]">First session
<input type="date" value={start} onChange={(e) => setStart(e.target.value)} className={`${field} mt-1 block`} />
</label>
<label className="text-xs text-[var(--r-sub)]">Then every
<select value={every} onChange={(e) => setEvery(e.target.value)} className={`${field} mt-1 block`}>
<option value="1">day</option><option value="7">week</option><option value="14">2 weeks</option><option value="30">30 days</option>
</select>
</label>
<label className="text-xs text-[var(--r-sub)]">At (optional)
<input type="time" value={time} onChange={(e) => setTime(e.target.value)} className={`${field} mt-1 block`} />
</label>
{time && (
<label className="text-xs text-[var(--r-sub)]">Minutes
<input type="number" min="5" max="480" value={minutes} onChange={(e) => setMinutes(e.target.value)} className={`${field} mt-1 block w-20`} />
</label>
)}
<button type="button" onClick={download} disabled={!start} className="rounded-xl border border-[var(--r-line)] px-4 py-2 text-sm hover:bg-black/5 disabled:opacity-50">Download calendar file (.ics)</button>
</div>
</section>
);
}