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

62 lines
3.6 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 → Import notes: bring notes from a Word or Markdown file into this study, matched to its chunks by passage.
import { useState } from 'react';
import { useApp } from '../context/AppContext.js';
import { useToast } from './Toasts.jsx';
import { applyImportedNotes, parseNotesText } from '../utils/importNotes.js';
export default function ImportNotes() {
const { project, updateProject } = useApp();
const { toast } = useToast();
const [file, setFile] = useState('');
const [sections, setSections] = useState(null); // null until a file is read
const [own, setOwn] = useState(true);
const [error, setError] = useState('');
const choose = async (e) => {
const f = e.target.files?.[0];
e.target.value = '';
setError(''); setSections(null);
if (!f) return;
try {
let text;
if (/\.docx$/i.test(f.name)) {
const { default: mammoth } = await import('mammoth');
text = (await mammoth.extractRawText({ arrayBuffer: await f.arrayBuffer() })).value;
} else text = await f.text();
const found = parseNotesText(text);
setFile(f.name);
setSections(found);
if (found.length === 0) setError('No passage headings with Observe / Interpret / Apply notes were found in that file. Start each section with a heading like “John 3:16–18”.');
} catch { setError('That file couldn’t be read.'); }
};
const preview = sections ? applyImportedNotes(project, sections, { own }) : null;
const add = () => {
updateProject((current) => applyImportedNotes(current, sections, { own }).project);
toast(`Added notes to ${preview.matched.length} chunk${preview.matched.length === 1 ? '' : 's'}`);
setSections(null);
};
return (
<section className="rounded-2xl border border-[var(--r-line)] bg-[var(--r-bar)] p-5" aria-label="Import notes">
<h2 className="text-sm font-medium">Import notes</h2>
<p className="mb-3 mt-1 text-xs text-[var(--r-sub)]">Bring in notes from a Word (.docx), Markdown or text file. Start each section with a passage heading (“John 3:16–18”), then label the notes “Observe:”, “Interpret:”, “Apply:”, “Background:” or “Questions:”. Notes you’ve already written are kept; imported text is added below them.</p>
<label className="inline-block cursor-pointer rounded-xl border border-[var(--r-line)] px-4 py-2 text-sm hover:bg-black/5">Choose a file
<input type="file" accept=".docx,.md,.markdown,.txt,text/markdown,text/plain" className="hidden" onChange={choose} />
</label>
{error && <p role="alert" className="mt-2 text-xs text-[var(--r-danger,#b42318)]">{error}</p>}
{preview && sections.length > 0 && (
<div className="mt-3 space-y-2 text-sm">
<p className="text-xs text-[var(--r-sub)]">{file}: {sections.length} section{sections.length === 1 ? '' : 's'} found.</p>
<ul className="space-y-1 text-xs">
{preview.matched.map((m) => <li key={m.heading}>✓ {m.heading}: {m.fields.join(', ')}</li>)}
{preview.unmatched.map((h) => <li key={h} className="text-[var(--r-sub)]">No chunk for {h}, skipped</li>)}
</ul>
<label className="flex items-center gap-2 text-xs text-[var(--r-sub)]"><input type="checkbox" checked={own} onChange={(e) => setOwn(e.target.checked)} className="accent-[var(--r-accent)]" /> These notes are my own writing (untick if they came from AI or another source)</label>
<button type="button" onClick={add} disabled={preview.matched.length === 0} className="rounded-xl bg-[var(--r-text)] px-4 py-2 text-sm font-medium text-[var(--r-bg)] disabled:opacity-40">Add to this study</button>
</div>
)}
</section>
);
}