"use client"; /** Research dashboard: working-paper tracker + BibTeX library across all pages. */ import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { FlaskConical, Plus, Download, Search, BookMarked, Copy, ExternalLink, AlertTriangle } from "lucide-react"; import { api } from "@/lib/client/api"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge, Dialog, DialogContent, Spinner, Empty, Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/primitives"; import { toast } from "@/components/ui/toast"; import { Select, PAPER_STATUSES, STATUS_TONE, VerifiedBadge, copy, shortAuthors } from "./ui"; import { timeAgo, cn, fmtDate } from "@/lib/utils"; export function ResearchOverview() { const r = useRouter(); const qc = useQueryClient(); const { data: papers = [], isLoading } = useQuery({ queryKey: ["research-overview"], queryFn: () => api("/api/research/overview") }); const { data: refs = [] } = useQuery({ queryKey: ["research-refs", "all"], queryFn: () => api("/api/research/references") }); const [status, setStatus] = useState(""); const [q, setQ] = useState(""); const [newOpen, setNewOpen] = useState(false); const [title, setTitle] = useState(""); const [venue, setVenue] = useState(""); const [busy, setBusy] = useState(false); const list = papers.filter((p) => (!status || p.status === status) && (!q || p.title.toLowerCase().includes(q.toLowerCase()))); const refList = refs.filter((x) => !q || `${x.citeKey} ${x.title} ${(x.authors ?? []).join(" ")} ${x.journal ?? ""} ${x.year ?? ""}`.toLowerCase().includes(q.toLowerCase())); const pageTitle = (id: string | null) => papers.find((p) => p.id === id)?.title ?? (id ? "Other page" : "Library"); async function create() { if (busy) return; setBusy(true); try { const p = await api("/api/research/papers", { method: "POST", json: { title: title.trim() || undefined, venue: venue.trim() || undefined } }); qc.invalidateQueries({ queryKey: ["tree"] }); qc.invalidateQueries({ queryKey: ["research-overview"] }); toast("Working paper created", { tone: "success" }); setNewOpen(false); r.push(`/p/${p.id}`); } catch (e: any) { toast("Could not create", { description: e.message, tone: "danger" }); } finally { setBusy(false); } } const counts = PAPER_STATUSES.map((s) => [s, papers.filter((p) => p.status === s).length] as const); return (

Research

{papers.length} working paper{papers.length === 1 ? "" : "s"} · {refs.length} references
{counts.map(([s, n]) => )}
setQ(e.target.value)} placeholder="Filter papers and references" className="h-8 pl-7 text-[13px]" />
Working papersBibTeX library {isLoading ?
: !list.length ? } title={papers.length ? "No paper matches" : "No working paper yet"} hint="Create one with the LaTeX-ready template, or set the type of an existing page to research." action={!papers.length ? : undefined} /> : ( <> {list.map((p) => )}
PaperStatusVenueDeadlineRefsExp.Open tasksWordsLast activity
{list.map((p) =>
{p.icon ?? "🔬"}
{p.title}
{p.status ? {p.status} : no status}{p.venue && {p.venue}}{p.deadline && · due {p.deadline}}
{p.references} refs · {p.experiments} exp. · {p.openTasks} tasks · {p.wordCount} words · {timeAgo(p.lastActivity)}
)}
)}
{!refs.length ? } title="The library is empty" hint="References added to any working paper appear here; export them all as one .bib file." /> : ( <> {refList.map((x) => )}
KeyAuthorsYearTitleJournalPaperStatus
{shortAuthors(x.authors)}{x.year ?? "—"}{x.title}{x.journal ?? "—"}{x.pageId ? {pageTitle(x.pageId)} : —}{x.doi && }
{refList.map((x) =>
{x.title}
{shortAuthors(x.authors)} · {x.year ?? "n.d."}{x.journal ? ` · ${x.journal}` : ""}
{x.pageId && {pageTitle(x.pageId)}}
)}
{!refList.length &&
No reference matches “{q}”.
} )}
{ if (e.key === "Enter") create(); }}>
); } function Row({ p }: { p: any }) { const r = useRouter(); const soon = p.deadline && (new Date(p.deadline).getTime() - Date.now()) / 864e5 < 14; return ( r.push(`/p/${p.id}`)}> {p.icon ?? "🔬"} e.stopPropagation()}>{p.title} {p.status ? {p.status} : —} {p.venue ?? "—"} {p.deadline ? fmtDate(p.deadline) : "—"} {p.references} {p.experiments}{p.experimentsRunning ? ({p.experimentsRunning} running) : null} {p.openTasks}{p.overdueTasks ? : null} {p.wordCount} {timeAgo(p.lastActivity)} ); }