import React, { useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Badge, Button, Modal, toast } from "@pubscript/ui"; import { importManuscriptFile, SUPPORTED_IMPORT_EXTENSIONS, type ImportedManuscript, } from "@pubscript/exporters"; import { useProjectStore } from "../../store/project-store.js"; export interface ImportDialogProps { open: boolean; onClose: () => void; defaultMode?: "new-project" | "append-chapters"; } export function ImportDialog({ open, onClose, defaultMode = "new-project", }: ImportDialogProps) { const navigate = useNavigate(); const currentProject = useProjectStore((s) => s.project); const importManuscript = useProjectStore((s) => s.importManuscript); const fileInputRef = useRef(null); const [selectedFile, setSelectedFile] = useState(null); const [rawBytes, setRawBytes] = useState(null); const [parsed, setParsed] = useState(null); const [mode, setMode] = useState<"new-project" | "append-chapters">(defaultMode); const [customTitle, setCustomTitle] = useState(""); const [splitChapters, setSplitChapters] = useState(true); const [parsing, setParsing] = useState(false); const [importing, setImporting] = useState(false); const [error, setError] = useState(null); const runParse = async (fileName: string, bytes: Uint8Array, split: boolean) => { setParsing(true); setError(null); try { const res = await importManuscriptFile(fileName, bytes, { splitChapters: split, }); setParsed(res); setCustomTitle((prev) => prev || res.title); } catch (err) { setParsed(null); setError(err instanceof Error ? err.message : "Failed to parse manuscript file."); } finally { setParsing(false); } }; const handleFileChange = async (file: File | null) => { if (!file) return; setSelectedFile(file); setCustomTitle(""); const buf = await file.arrayBuffer(); const bytes = new Uint8Array(buf); setRawBytes(bytes); await runParse(file.name, bytes, splitChapters); }; const handleToggleSplit = async (nextSplit: boolean) => { setSplitChapters(nextSplit); if (selectedFile && rawBytes) { await runParse(selectedFile.name, rawBytes, nextSplit); } }; const handleConfirmImport = async () => { if (!parsed) return; setImporting(true); setError(null); try { await importManuscript(parsed, { mode: currentProject ? mode : "new-project", title: customTitle.trim() || parsed.title, }); toast.success( mode === "append-chapters" && currentProject ? `Imported ${parsed.chapters.length} chapter(s) (${parsed.totalWords.toLocaleString()} words) into "${currentProject.title}"` : `Created project "${customTitle.trim() || parsed.title}" (${parsed.chapters.length} chapter(s), ${parsed.totalWords.toLocaleString()} words)`, ); setSelectedFile(null); setRawBytes(null); setParsed(null); setCustomTitle(""); onClose(); navigate("/"); } catch (err) { setError(err instanceof Error ? err.message : "Failed to import manuscript."); } finally { setImporting(false); } }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); const f = e.dataTransfer.files?.[0]; if (f) void handleFileChange(f); }; return ( } >
e.preventDefault()} onDrop={handleDrop} onClick={() => fileInputRef.current?.click()} style={{ border: "2px dashed var(--ps-border)", borderRadius: 10, padding: "20px 16px", textAlign: "center", background: "var(--ps-bg)", cursor: "pointer", }} > void handleFileChange(e.target.files?.[0] ?? null)} />
📥
{selectedFile ? `${selectedFile.name} (${(selectedFile.size / 1024).toFixed(1)} KB)` : "Click to choose a file or drag & drop here"}
Compatible formats: .docx (Word / OnlyOffice), .odt{" "} (OpenOffice / LibreOffice / OnlyOffice), .epub, .md,{" "} .txt, .html, .rtf,{" "} .json
{error && (
{error}
)} {parsing && (
Running PubScript format conversion pipeline…
)} {parsed && !parsing && (
{parsed.formatLabel} {parsed.chapters.length} chapter(s) · {parsed.totalWords.toLocaleString()} words
{parsed.author && ( Author: {parsed.author} )}
{currentProject && (
)} {mode === "new-project" && (
setCustomTitle(e.target.value)} placeholder="Manuscript Title" />
)} {parsed.format !== "epub" && parsed.format !== "json" && ( )}
Converted Chapters Preview ({parsed.chapters.length})
{parsed.chapters.map((ch, idx) => (
{idx + 1}. {ch.title} {ch.wordCount.toLocaleString()} words
))}
)}
); }