diff --git a/web/src/components/dashboard/TrackSpotlightSection.tsx b/web/src/components/dashboard/TrackSpotlightSection.tsx
new file mode 100644
index 00000000..7d0983ce
--- /dev/null
+++ b/web/src/components/dashboard/TrackSpotlightSection.tsx
@@ -0,0 +1,92 @@
+"use client"
+
+import { useCallback, useState } from "react"
+
+import { TrackSpotlight } from "./TrackSpotlight"
+import type { AnchorPreviewItem, ResearchTrackSummary, TrackFeedItem } from "@/lib/types"
+
+interface TrackSpotlightSectionProps {
+ initialTracks: ResearchTrackSummary[]
+ initialActiveTrack: ResearchTrackSummary | null
+ initialFeedItems: TrackFeedItem[]
+ initialFeedTotal: number
+ initialAnchors: AnchorPreviewItem[]
+ userId?: string
+}
+
+export function TrackSpotlightSection({
+ initialTracks,
+ initialActiveTrack,
+ initialFeedItems,
+ initialFeedTotal,
+ initialAnchors,
+ userId = "default",
+}: TrackSpotlightSectionProps) {
+ const [tracks] = useState(initialTracks)
+ const [activeTrack, setActiveTrack] = useState(initialActiveTrack)
+ const [feedItems, setFeedItems] = useState(initialFeedItems)
+ const [feedTotal, setFeedTotal] = useState(initialFeedTotal)
+ const [anchors, setAnchors] = useState(initialAnchors)
+ const [isLoading, setIsLoading] = useState(false)
+
+ const handleSelectTrack = useCallback(
+ async (trackId: number) => {
+ const selectedTrack = tracks.find((t) => t.id === trackId)
+ if (!selectedTrack || selectedTrack.id === activeTrack?.id) return
+
+ setIsLoading(true)
+ setActiveTrack(selectedTrack)
+
+ try {
+ // Fetch feed and anchors for the new track
+ const [feedRes, anchorsRes] = await Promise.all([
+ fetch(`/api/research/tracks/${trackId}/feed?user_id=${encodeURIComponent(userId)}&limit=6`),
+ fetch(`/api/research/tracks/${trackId}/anchors/discover?user_id=${encodeURIComponent(userId)}&limit=4`),
+ ])
+
+ if (feedRes.ok) {
+ const feedData = await feedRes.json()
+ setFeedItems(feedData.items || [])
+ setFeedTotal(feedData.total || 0)
+ } else {
+ setFeedItems([])
+ setFeedTotal(0)
+ }
+
+ if (anchorsRes.ok) {
+ const anchorsData = await anchorsRes.json()
+ setAnchors(anchorsData.items || [])
+ } else {
+ setAnchors([])
+ }
+
+ // Optionally activate the track on the backend
+ await fetch(`/api/research/tracks/${trackId}/activate?user_id=${encodeURIComponent(userId)}`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: "{}",
+ })
+ } catch (error) {
+ console.error("Failed to fetch track data:", error)
+ setFeedItems([])
+ setFeedTotal(0)
+ setAnchors([])
+ } finally {
+ setIsLoading(false)
+ }
+ },
+ [tracks, activeTrack?.id, userId]
+ )
+
+ return (
+
+ )
+}
diff --git a/web/src/components/research/ResearchPageNew.tsx b/web/src/components/research/ResearchPageNew.tsx
index fc0bfb25..888c9dc2 100644
--- a/web/src/components/research/ResearchPageNew.tsx
+++ b/web/src/components/research/ResearchPageNew.tsx
@@ -14,14 +14,17 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
+import {
+ Sheet,
+ SheetContent,
+ SheetHeader,
+ SheetTitle,
+} from "@/components/ui/sheet"
import { Button } from "@/components/ui/button"
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { SearchBox } from "./SearchBox"
import { TrackPills } from "./TrackPills"
import { SearchResults } from "./SearchResults"
-import { FeedTab } from "./FeedTab"
-import { SavedTab } from "./SavedTab"
import { MemoryTab } from "./MemoryTab"
import { CreateTrackModal } from "./CreateTrackModal"
import { EditTrackModal } from "./EditTrackModal"
@@ -29,34 +32,6 @@ import { ManageTracksModal } from "./ManageTracksModal"
import type { Track } from "./TrackSelector"
import type { Paper } from "./PaperCard"
-type AnchorAuthor = {
- author_id: number
- author_ref?: string
- name: string
- slug: string
- anchor_score: number
- anchor_level: string
- intrinsic_score: number
- relevance_score: number
- score_breakdown?: {
- intrinsic: number
- relevance: number
- network: number
- personalization: number
- total: number
- }
- user_action?: "follow" | "ignore" | null
- evidence_status?: "ok" | "missing"
- evidence_note?: string | null
- evidence_papers: Array<{
- paper_id: number
- title: string
- year?: number | null
- url?: string | null
- citation_count?: number
- }>
-}
-
type ContextPack = {
context_run_id?: number | null
routing: {
@@ -95,18 +70,20 @@ export default function ResearchPageNew() {
const [tracks, setTracks] = useState