tahamajs/IE / Projects /front /src /components /authoring /ReferenceManagerPanel.tsx
tahamajs's picture
download
raw
7.02 kB
// src/components/authoring/ReferenceManagerPanel.tsx
import { useState } from "react";
import { Search, Plus, Upload, Trash2, ExternalLink, Copy, Loader2, Check, BookOpen, Database } from "lucide-react";
interface ReferenceManagerPanelProps {
references: any[];
loading: boolean;
search: string;
setSearch: (v: string) => void;
sourceFilter: string;
setSourceFilter: (v: string) => void;
importing: boolean;
importFromDOI: (doi: string) => void;
importBibTeX: (bibtex: string) => void;
connectZotero: (apiKey: string, userId: string) => void;
connectMendeley: (token: string) => void;
removeReference: (id: string) => void;
generateBibliography: (style: string) => string;
}
export function ReferenceManagerPanel({ references, loading, search, setSearch, sourceFilter, setSourceFilter, importing, importFromDOI, importBibTeX, connectZotero, connectMendeley, removeReference, generateBibliography }: ReferenceManagerPanelProps) {
const [tab, setTab] = useState<"browse" | "import">("browse");
const [doi, setDoi] = useState("");
const [bibtex, setBibtex] = useState("");
const [zoteroKey, setZoteroKey] = useState("");
const [zoteroUser, setZoteroUser] = useState("");
const [mendeleyToken, setMendeleyToken] = useState("");
return (
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4 h-full">
{/* Sidebar */}
<div className="space-y-3">
<div className="flex gap-1">
<button onClick={() => setTab("browse")} className={`flex-1 py-1.5 text-xs rounded-lg ${tab === "browse" ? "bg-primary/20 text-primary" : "hover:bg-white/10"}`}>Browse</button>
<button onClick={() => setTab("import")} className={`flex-1 py-1.5 text-xs rounded-lg ${tab === "import" ? "bg-primary/20 text-primary" : "hover:bg-white/10"}`}>Import</button>
</div>
{tab === "browse" && (
<>
<div className="relative">
<Search className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400" />
<input type="text" value={search} onChange={e => setSearch(e.target.value)} placeholder="Search references..."
className="w-full pl-7 pr-3 py-1.5 text-sm border border-white/20 rounded-lg bg-white/10" />
</div>
<select aria-label="Select option" value={sourceFilter} onChange={e => setSourceFilter(e.target.value)}
className="w-full text-sm px-3 py-1.5 border border-white/20 rounded-lg bg-white/10">
<option value="all">All sources</option>
<option value="doi">DOI</option>
<option value="bibtex">BibTeX</option>
<option value="zotero">Zotero</option>
<option value="mendeley">Mendeley</option>
<option value="manual">Manual</option>
</select>
</>
)}
{tab === "import" && (
<div className="space-y-3">
<div>
<h5 className="text-xs font-semibold mb-1">Import by DOI</h5>
<div className="flex gap-1">
<input type="text" value={doi} onChange={e => setDoi(e.target.value)} placeholder="10.xxxx/xxxxx"
className="flex-1 text-xs px-3 py-1.5 bg-white/10 rounded-lg" />
<button onClick={() => { importFromDOI(doi.trim()); setDoi(""); }} disabled={!doi.trim() || importing}
className="btn btn-primary btn-xs">{importing ? <Loader2 className="w-3 h-3 animate-spin"/> : "Add"}</button>
</div>
</div>
<div>
<h5 className="text-xs font-semibold mb-1">Import BibTeX</h5>
<textarea value={bibtex} onChange={e => setBibtex(e.target.value)} placeholder="Paste BibTeX here..."
rows={4} className="w-full text-xs px-3 py-1.5 bg-white/10 rounded-lg font-mono" />
<button onClick={() => { importBibTeX(bibtex); setBibtex(""); }} disabled={!bibtex.trim() || importing}
className="btn btn-primary btn-xs mt-1 w-full">Import BibTeX</button>
</div>
<div>
<h5 className="text-xs font-semibold mb-1 flex items-center gap-1"><Database className="w-3 h-3"/> Connect Zotero</h5>
<input type="text" value={zoteroUser} onChange={e => setZoteroUser(e.target.value)} placeholder="User ID" className="w-full text-xs px-3 py-1.5 bg-white/10 rounded-lg mb-1"/>
<input type="text" value={zoteroKey} onChange={e => setZoteroKey(e.target.value)} placeholder="API Key" className="w-full text-xs px-3 py-1.5 bg-white/10 rounded-lg mb-1"/>
<button onClick={() => connectZotero(zoteroKey, zoteroUser)} disabled={!zoteroKey || !zoteroUser || importing}
className="btn btn-secondary btn-xs w-full">Connect Zotero</button>
</div>
<div>
<h5 className="text-xs font-semibold mb-1">Connect Mendeley</h5>
<input type="text" value={mendeleyToken} onChange={e => setMendeleyToken(e.target.value)} placeholder="Access Token" className="w-full text-xs px-3 py-1.5 bg-white/10 rounded-lg mb-1"/>
<button onClick={() => connectMendeley(mendeleyToken)} disabled={!mendeleyToken || importing}
className="btn btn-secondary btn-xs w-full">Connect Mendeley</button>
</div>
</div>
)}
</div>
{/* Reference list */}
<div className="lg:col-span-3 overflow-y-auto space-y-1.5">
{loading ? (
<div className="flex justify-center py-8"><Loader2 className="w-6 h-6 animate-spin text-gray-400"/></div>
) : references.length === 0 ? (
<div className="text-center py-8 text-gray-400"><BookOpen className="w-8 h-8 mx-auto mb-2 opacity-30"/><p className="text-sm">No references yet</p></div>
) : (
references.map(ref => (
<div key={ref.id} className="flex items-start justify-between p-2.5 rounded-lg hover:bg-white/5 transition text-sm">
<div className="flex-1 min-w-0">
<p className="font-semibold truncate">{ref.title}</p>
<p className="text-xs text-gray-500">{ref.authors.slice(0, 2).map((a: any) => `${a.given} ${a.family}`).join(", ")}{ref.authors.length > 2 ? " et al." : ""} ({ref.year})</p>
{ref.journal && <p className="text-xs text-gray-400 italic">{ref.journal}</p>}
<div className="flex items-center gap-2 mt-1">
<span className="text-[9px] px-1.5 py-0.5 rounded-full bg-white/10">{ref.source}</span>
{ref.doi && <a href={`https://doi.org/${ref.doi}`} target="_blank" className="text-[9px] text-primary hover:underline"><ExternalLink className="w-2.5 h-2.5 inline"/></a>}
</div>
</div>
<button onClick={() => removeReference(ref.id)} className="p-1 text-gray-400 hover:text-red-500"><Trash2 className="w-3.5 h-3.5"/></button>
</div>
))
)}
</div>
</div>
);
}

Xet Storage Details

Size:
7.02 kB
·
Xet hash:
7fbfd66c4089c23ee8a574280ae377dbdf06f1bf2f209893885ca1d3efc352d4

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.