refactor(research): replace hardcoded data with TanStack Query API integration

This commit is contained in:
anthrodjear 2026-05-08 08:23:21 +03:00
parent 999f0aab4f
commit cadd965796

View file

@ -1,74 +1,49 @@
"use client" "use client"
import { useState } from "react" import { useState } from "react"
import { useQuery } from "@tanstack/react-query"
import { IconBook, IconDatabase, IconCircleCheck, IconSparkles, IconShield, IconActivity, IconCpu, IconFileText, IconSettings } from "@tabler/icons-react" import { IconBook, IconDatabase, IconCircleCheck, IconSparkles, IconShield, IconActivity, IconCpu, IconFileText, IconSettings } from "@tabler/icons-react"
import { ResearchAgents } from "./research-agents" import { ResearchAgents } from "./research-agents"
import { ResearchGraph } from "./research-graph" import { ResearchGraph } from "./research-graph"
import { ResearchConfig } from "./research-config" import { ResearchConfig } from "./research-config"
import { ResearchReports } from "./research-reports" import { ResearchReports } from "./research-reports"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { listResearchAgents, listResearchGraph, listResearchReports, ResearchAgent, ResearchNode, ResearchReport } from "@/api/research"
interface ResearchAgent { const agentIcons: Record<string, React.ComponentType<{ className?: string }>> = {
id: string literature: IconBook,
name: string extractor: IconDatabase,
icon: React.ComponentType<{ className?: string }> validator: IconCircleCheck,
active: boolean synthesizer: IconSparkles,
progress: number
ram: string
} }
interface ResearchReport { function mapAgentIcon(type: string): React.ComponentType<{ className?: string }> {
id: string return agentIcons[type] || IconBook
title: string
status: "in-progress" | "complete"
timestamp: string
pages?: number
words?: number
} }
interface ResearchNode {
name: string
abbr: string
x: number
y: number
}
const defaultAgents: ResearchAgent[] = [
{ id: "literature", name: "Literature Analyzer", icon: IconBook, active: true, progress: 94, ram: "2.4GB" },
{ id: "extractor", name: "Data Extractor", icon: IconDatabase, active: true, progress: 87, ram: "1.8GB" },
{ id: "validator", name: "Fact Validator", icon: IconCircleCheck, active: true, progress: 76, ram: "1.2GB" },
{ id: "synthesizer", name: "Synthesizer", icon: IconSparkles, active: false, progress: 65, ram: "0.9GB" },
]
const defaultReports: ResearchReport[] = [
{ id: "1", title: "AI trends 2026", status: "in-progress", timestamp: "2 min ago", pages: 24, words: 7200 },
{ id: "2", title: "Quantum computing", status: "complete", timestamp: "1 hour ago", pages: 45, words: 13500 },
]
const defaultNodes: ResearchNode[] = [
{ name: "Neural Networks", abbr: "NN", x: 150, y: 80 },
{ name: "Transformers", abbr: "TR", x: 150, y: 120 },
{ name: "LLM Optimization", abbr: "LO", x: 150, y: 160 },
{ name: "Edge Computing", abbr: "EC", x: 150, y: 210 },
{ name: "Multi-Agent Systems", abbr: "MA", x: 150, y: 260 },
{ name: "Vision Models", abbr: "VM", x: 650, y: 100 },
{ name: "RAG Systems", abbr: "RA", x: 650, y: 200 },
{ name: "Knowledge Graphs", abbr: "KG", x: 400, y: 150 },
{ name: "Agent Architecture", abbr: "AA", x: 400, y: 180 },
{ name: "Fine-tuning Methods", abbr: "FT", x: 400, y: 250 },
]
export function ResearchPage() { export function ResearchPage() {
const [agents, setAgents] = useState<ResearchAgent[]>(defaultAgents)
const [researchType, setResearchType] = useState<string>("1.5") const [researchType, setResearchType] = useState<string>("1.5")
const [depth, setDepth] = useState<string>("1.5") const [depth, setDepth] = useState<string>("1.5")
const [restrictToGraph, setRestrictToGraph] = useState(false) const [restrictToGraph, setRestrictToGraph] = useState(false)
const [selectedNodes, setSelectedNodes] = useState<Set<string>>(new Set()) const [selectedNodes, setSelectedNodes] = useState<Set<string>>(new Set())
const { data: agents = [], isLoading: agentsLoading, error: agentsError } = useQuery({
queryKey: ["researchAgents"],
queryFn: listResearchAgents,
})
const { data: nodes = [], isLoading: nodesLoading, error: nodesError } = useQuery({
queryKey: ["researchGraph"],
queryFn: listResearchGraph,
})
const { data: reports = [], isLoading: reportsLoading, error: reportsError } = useQuery({
queryKey: ["researchReports"],
queryFn: listResearchReports,
})
const handleToggleAgent = (id: string) => { const handleToggleAgent = (id: string) => {
setAgents(agents.map(agent => console.log("Toggle agent:", id)
agent.id === id ? { ...agent, active: !agent.active } : agent
))
} }
const activeAgents = agents.filter(a => a.active) const activeAgents = agents.filter(a => a.active)
@ -76,6 +51,31 @@ export function ResearchPage() {
? Math.round(activeAgents.reduce((sum, a) => sum + a.progress, 0) / activeAgents.length) ? Math.round(activeAgents.reduce((sum, a) => sum + a.progress, 0) / activeAgents.length)
: 0 : 0
const completedReports = reports.filter(r => r.status === "complete")
const isLoading = agentsLoading || nodesLoading || reportsLoading
const hasError = agentsError || nodesError || reportsError
if (isLoading) {
return (
<div className="relative min-h-screen bg-[#050505] overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-[#F27D26] animate-pulse">Loading research data...</div>
</div>
</div>
)
}
if (hasError) {
return (
<div className="relative min-h-screen bg-[#050505] overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-red-500">Failed to load research data</div>
</div>
</div>
)
}
return ( return (
<div className="relative min-h-screen bg-[#050505] overflow-hidden"> <div className="relative min-h-screen bg-[#050505] overflow-hidden">
{/* Ghost Background Typography */} {/* Ghost Background Typography */}
@ -116,7 +116,7 @@ export function ResearchPage() {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<IconFileText className="w-4 h-4 text-white/40" /> <IconFileText className="w-4 h-4 text-white/40" />
<span className="text-xs text-white/60">Reports:</span> <span className="text-xs text-white/60">Reports:</span>
<span className="text-sm font-semibold text-[#F2F2F2]">{defaultReports.filter(r => r.status === "complete").length}</span> <span className="text-sm font-semibold text-[#F2F2F2]">{completedReports.length}</span>
</div> </div>
</div> </div>
</div> </div>
@ -135,7 +135,7 @@ export function ResearchPage() {
{/* Center Column - Graph */} {/* Center Column - Graph */}
<div className="flex-1 bg-[#050505] relative"> <div className="flex-1 bg-[#050505] relative">
<ResearchGraph <ResearchGraph
nodes={defaultNodes} nodes={nodes}
selectedNodes={selectedNodes} selectedNodes={selectedNodes}
onNodeToggle={(name: string) => { onNodeToggle={(name: string) => {
setSelectedNodes(prev => { setSelectedNodes(prev => {
@ -162,7 +162,7 @@ export function ResearchPage() {
setRestrictToGraph={setRestrictToGraph} setRestrictToGraph={setRestrictToGraph}
/> />
<ResearchReports <ResearchReports
reports={defaultReports} reports={reports}
/> />
</div> </div>
</main> </main>
@ -173,7 +173,7 @@ export function ResearchPage() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span>PicoClaw Research Engine v2.4.1</span> <span>PicoClaw Research Engine v2.4.1</span>
<span className="text-white/20">|</span> <span className="text-white/20">|</span>
<span>Nodes: {defaultNodes.length}</span> <span>Nodes: {nodes.length}</span>
<span className="text-white/20">|</span> <span className="text-white/20">|</span>
<span>Agents: {activeAgents.length} active</span> <span>Agents: {activeAgents.length} active</span>
</div> </div>