Files
study-bible-project/src/components/VersionHistory.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

68 lines
4.0 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 → Earlier versions: the copies the server kept of this study as you worked. Look at what is different
// from now, and put one back. The version you replace is kept, so putting one back can be undone too.
import { useCallback, useEffect, useState } from 'react';
import { getStudyVersion, listStudyVersions } from '../syncService.js';
import { describeDiff, diffProjects } from '../utils/projectDiff.js';
const btn = 'rounded-lg border border-[var(--r-line)] px-3 py-1.5 text-xs hover:bg-black/5 disabled:opacity-50';
export default function VersionHistory({ project, onRestore }) {
const [state, setState] = useState({ status: 'loading', versions: [] });
const [open, setOpen] = useState(null); // { id, lines } the version being looked at
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
const load = useCallback(async () => {
if (!project?.id) return;
const r = await listStudyVersions(project.id);
setState(r.ok ? { status: 'ready', versions: r.data.versions } : { status: 'unavailable', versions: [] });
}, [project?.id]);
useEffect(() => { load(); }, [load]);
const preview = async (v) => {
if (open?.id === v.id) { setOpen(null); return; }
setError('');
const r = await getStudyVersion(project.id, v.id);
if (!r.ok) { setError(r.error ?? 'Could not open that version.'); return; }
setOpen({ id: v.id, lines: describeDiff(diffProjects(project, r.data)) });
};
const restore = async (v) => {
if (!window.confirm(`Put back the version from ${new Date(v.savedAt).toLocaleString()}? What you have now is kept in this list, so you can undo it.`)) return;
setBusy(true); setError('');
const ok = await onRestore(v.id);
setBusy(false);
if (ok) { setOpen(null); load(); } else setError('Could not restore that version. Check your connection and try again.');
};
return (
<section className="rounded-2xl border border-[var(--r-line)] bg-[var(--r-bar)] p-5" aria-label="Earlier versions">
<h2 className="text-sm font-medium">Earlier versions</h2>
<p className="mb-3 mt-1 text-xs text-[var(--r-sub)]">As you work, your account keeps earlier copies of this study (about one for every ten minutes of editing, the latest 30). Use one to undo a bad merge or a deletion.</p>
{state.status === 'loading' && <p className="text-xs text-[var(--r-sub)]">Loading…</p>}
{state.status === 'unavailable' && <p className="text-xs text-[var(--r-sub)]">Earlier versions need your account and a connection, and the study has to have synced once.</p>}
{state.status === 'ready' && state.versions.length === 0 && <p className="text-xs text-[var(--r-sub)]">Nothing kept yet. The first earlier version appears after you’ve changed the study a couple of times.</p>}
{state.versions.length > 0 && (
<ul className="divide-y divide-[var(--r-line)] text-sm">
{state.versions.map((v) => (
<li key={v.id} className="py-2">
<div className="flex flex-wrap items-center justify-between gap-2">
<span><span className="font-medium">{new Date(v.savedAt).toLocaleString()}</span> <span className="text-xs text-[var(--r-sub)]">{v.chunks} chunk{v.chunks === 1 ? '' : 's'} · {v.words} words</span></span>
<span className="flex gap-2">
<button type="button" onClick={() => preview(v)} aria-expanded={open?.id === v.id} className={btn}>{open?.id === v.id ? 'Hide' : 'What’s different'}</button>
<button type="button" onClick={() => restore(v)} disabled={busy} className={btn}>Put this back</button>
</span>
</div>
{open?.id === v.id && (
<ul className="mt-2 list-disc space-y-0.5 rounded-lg bg-[var(--r-bg)] py-2 pl-7 pr-3 text-xs text-[var(--r-sub)]">
{open.lines.map((line) => <li key={line}>{line}</li>)}
</ul>
)}
</li>
))}
</ul>
)}
{error && <p role="alert" className="mt-2 text-xs text-[var(--r-danger,#b42318)]">{error}</p>}
</section>
);
}