'use client' import Link from 'next/link' import { Plus } from 'lucide-react' import { useEffect, useState } from 'react' import { getWatchlistQuote, type MappedQuote } from '@/lib/stockApi' import { getUserSymbols, getUserTopics, deleteUserInterest } from '@/lib/userInterestsApi' import { getNewsFeed } from '@/lib/newsApi' import { StockWatchlistRow } from '@/components/widgets/StockWatchlistRow' import { InterestHeadlineRow } from '@/components/widgets/InterestHeadlineRow' interface TopicItem { id: string topic: string headline: string } export function InterestsPage() { // Symbols state const [watchlist, setWatchlist] = useState<(MappedQuote & { id: string })[]>([]) const [loadingWatchlist, setLoadingWatchlist] = useState(true) const [watchlistError, setWatchlistError] = useState(null) const [watchlistEmpty, setWatchlistEmpty] = useState(false) // Topics state const [topics, setTopics] = useState([]) const [loadingTopics, setLoadingTopics] = useState(true) const [topicsError, setTopicsError] = useState(null) const [topicsEmpty, setTopicsEmpty] = useState(false) // Track which row is currently open (only one at a time) const [openRowId, setOpenRowId] = useState(null) // Fetch symbols from backend, then fetch quotes for each useEffect(() => { setLoadingWatchlist(true) setWatchlistError(null) setWatchlistEmpty(false) getUserSymbols() .then((symbols) => { if (symbols.length === 0) { setWatchlistEmpty(true) setWatchlist([]) setLoadingWatchlist(false) return } return Promise.all(symbols.map((sym) => getWatchlistQuote(sym))) .then((quotes) => setWatchlist(quotes.map((q, i) => ({ ...q, id: String(i + 1) }))) ) }) .catch((err) => setWatchlistError(err.message)) .finally(() => setLoadingWatchlist(false)) }, []) // Fetch topics from backend useEffect(() => { setLoadingTopics(true) setTopicsError(null) setTopicsEmpty(false) getUserTopics() .then(async (topicNames) => { if (topicNames.length === 0) { setTopicsEmpty(true) setTopics([]) return } // Fetch top headline for each topic concurrently (request-amount=1) const topicItems = await Promise.all( topicNames.map(async (t, i) => { let headline = '' try { const news = await getNewsFeed(t, 'frontend', 1) headline = news[0]?.title ?? '' } catch { // Non-fatal: card still renders without a headline preview } return { id: String(i + 1), topic: t, headline } }) ) setTopics(topicItems) }) .catch((err) => setTopicsError(err.message)) .finally(() => setLoadingTopics(false)) }, []) // Delete handlers — return Promise so SwipeableRow can manage async state const handleDeleteSymbol = async (symbol: string): Promise => { await deleteUserInterest('symbol', symbol) setWatchlist((prev) => { const next = prev.filter((s) => s.symbol !== symbol) if (next.length === 0) setWatchlistEmpty(true) return next }) setOpenRowId(null) } const handleDeleteTopic = async (topicName: string): Promise => { await deleteUserInterest('topic', topicName) setTopics((prev) => { const next = prev.filter((t) => t.topic !== topicName) if (next.length === 0) setTopicsEmpty(true) return next }) setOpenRowId(null) } return (
{/* Header */}

Interests

{/* Content - Split into two halves */}
{/* Top Half: Watchlist */}

Symbol Watchlist

{loadingWatchlist && (
Loading symbols...
)} {watchlistError && (
{watchlistError}
)} {!loadingWatchlist && !watchlistError && watchlistEmpty && (
You are not currently following any symbols. Please add a symbol first.
)} {!loadingWatchlist && !watchlistError && !watchlistEmpty && watchlist.map((stock) => ( handleDeleteSymbol(stock.symbol)} isOpen={openRowId === `symbol-${stock.symbol}`} onOpenChange={(open) => setOpenRowId(open ? `symbol-${stock.symbol}` : null)} /> ))}
{/* Bottom Half: Topic Watchlist */}

Topic Watchlist

{loadingTopics && (
Loading topics...
)} {topicsError && (
{topicsError}
)} {!loadingTopics && !topicsError && topicsEmpty && (
You are not currently following any topics. Please add a topic first.
)} {!loadingTopics && !topicsError && !topicsEmpty && topics.map((interest) => ( handleDeleteTopic(interest.topic)} isOpen={openRowId === `topic-${interest.topic}`} onOpenChange={(open) => setOpenRowId(open ? `topic-${interest.topic}` : null)} /> ))}
) }