From 6bc464acce30ab47b848234c933c2c57dc579c3b Mon Sep 17 00:00:00 2001 From: keshav-005 Date: Wed, 22 Apr 2026 23:37:34 +0530 Subject: [PATCH 1/3] Fix search ticker metadata --- components/SearchCommand.tsx | 8 ++--- lib/actions/finnhub.actions.ts | 56 +++++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/components/SearchCommand.tsx b/components/SearchCommand.tsx index f7bc62f..82b4098 100644 --- a/components/SearchCommand.tsx +++ b/components/SearchCommand.tsx @@ -46,7 +46,7 @@ export default function SearchCommand({ renderAs = 'button', label = 'Add stock' useEffect(() => { debouncedSearch(); - }, [searchTerm]); + }, [debouncedSearch, searchTerm]); const handleSelectStock = () => { setOpen(false); @@ -87,7 +87,7 @@ export default function SearchCommand({ renderAs = 'button', label = 'Add stock' {isSearchMode ? 'Search results' : 'Popular stocks'} {` `}({displayStocks?.length || 0}) - {displayStocks?.map((stock, i) => ( + {displayStocks?.map((stock) => (
  • - {stock.symbol} | {stock.exchange } | {stock.type} + {[stock.symbol, stock.exchange, stock.type].filter(Boolean).join(' | ')}
    @@ -114,4 +114,4 @@ export default function SearchCommand({ renderAs = 'button', label = 'Add stock' ) -} \ No newline at end of file +} diff --git a/lib/actions/finnhub.actions.ts b/lib/actions/finnhub.actions.ts index 2932bdc..18753b8 100644 --- a/lib/actions/finnhub.actions.ts +++ b/lib/actions/finnhub.actions.ts @@ -7,6 +7,25 @@ import { cache } from 'react'; const FINNHUB_BASE_URL = 'https://finnhub.io/api/v1'; const NEXT_PUBLIC_FINNHUB_API_KEY = process.env.NEXT_PUBLIC_FINNHUB_API_KEY ?? ''; +type FinnhubQuote = { + c?: number; + d?: number; + dp?: number; +}; + +type FinnhubCompanyProfile = { + currency?: string; + exchange?: string; + logo?: string; + marketCapitalization?: number; + name?: string; + ticker?: string; +}; + +type SearchStockCandidate = FinnhubSearchResult & { + __exchange?: string; +}; + async function fetchJSON(url: string, revalidateSeconds?: number): Promise { const options: RequestInit & { next?: { revalidate?: number } } = revalidateSeconds ? { cache: 'force-cache', next: { revalidate: revalidateSeconds } } @@ -22,12 +41,23 @@ async function fetchJSON(url: string, revalidateSeconds?: number): Promise export { fetchJSON }; +function getExchangeLabel(symbol: string, exchange?: string) { + if (exchange?.trim()) { + return exchange; + } + + const parts = symbol.split('.'); + const suffix = parts.length > 1 ? parts[parts.length - 1] : ''; + + return suffix || 'US'; +} + export async function getQuote(symbol: string) { try { const token = NEXT_PUBLIC_FINNHUB_API_KEY; const url = `${FINNHUB_BASE_URL}/quote?symbol=${encodeURIComponent(symbol)}&token=${token}`; // No caching for real-time price - return await fetchJSON(url, 0); + return await fetchJSON(url, 0); } catch (e) { console.error('Error fetching quote for', symbol, e); return null; @@ -39,7 +69,7 @@ export async function getCompanyProfile(symbol: string) { const token = NEXT_PUBLIC_FINNHUB_API_KEY; const url = `${FINNHUB_BASE_URL}/stock/profile2?symbol=${encodeURIComponent(symbol)}&token=${token}`; // Cache profile for 24 hours - return await fetchJSON(url, 86400); + return await fetchJSON(url, 86400); } catch (e) { console.error('Error fetching profile for', symbol, e); return null; @@ -160,7 +190,7 @@ export const searchStocks = cache(async (query?: string): Promise(url, 3600); - return { sym, profile } as { sym: string; profile: any }; + const profile = await fetchJSON(url, 3600); + return { sym, profile } as { sym: string; profile: FinnhubCompanyProfile | null }; } catch (e) { console.error('Error fetching profile2 for', sym, e); - return { sym, profile: null } as { sym: string; profile: any }; + return { sym, profile: null } as { sym: string; profile: FinnhubCompanyProfile | null }; } }) ); @@ -185,19 +215,16 @@ export const searchStocks = cache(async (query?: string): Promise Boolean(x)); + .filter((x): x is SearchStockCandidate => Boolean(x)); } else { const url = `${FINNHUB_BASE_URL}/search?q=${encodeURIComponent(trimmed)}&token=${token}`; const data = await fetchJSON(url, 1800); @@ -208,9 +235,8 @@ export const searchStocks = cache(async (query?: string): Promise { const upper = (r.symbol || '').toUpperCase(); const name = r.description || upper; - const exchangeFromDisplay = (r.displaySymbol as string | undefined) || undefined; - const exchangeFromProfile = (r as any).__exchange as string | undefined; - const exchange = exchangeFromDisplay || exchangeFromProfile || 'US'; + const exchangeFromProfile = r.__exchange; + const exchange = getExchangeLabel(upper, exchangeFromProfile); const type = r.type || 'Stock'; const item: StockWithWatchlistStatus = { symbol: upper, From 2c193bfacbc6afbf5effa41dd4eb1a1dd20ea149 Mon Sep 17 00:00:00 2001 From: keshav-005 Date: Wed, 22 Apr 2026 23:51:04 +0530 Subject: [PATCH 2/3] Tighten search exchange suffix fallback --- lib/actions/finnhub.actions.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/actions/finnhub.actions.ts b/lib/actions/finnhub.actions.ts index 18753b8..9262448 100644 --- a/lib/actions/finnhub.actions.ts +++ b/lib/actions/finnhub.actions.ts @@ -26,6 +26,13 @@ type SearchStockCandidate = FinnhubSearchResult & { __exchange?: string; }; +const FINNHUB_EXCHANGE_SUFFIXES = new Set([ + 'AS', 'AT', 'AX', 'BA', 'BK', 'BO', 'BR', 'CO', 'DE', 'F', 'HE', 'HK', + 'IL', 'IS', 'JK', 'JO', 'KL', 'KQ', 'KS', 'L', 'LS', 'MC', 'MI', 'MX', + 'NS', 'NZ', 'OL', 'PA', 'PR', 'SA', 'SI', 'SS', 'ST', 'SW', 'SZ', 'T', + 'TA', 'TO', 'TW', 'TWO', 'V', 'VI', 'WA', +]); + async function fetchJSON(url: string, revalidateSeconds?: number): Promise { const options: RequestInit & { next?: { revalidate?: number } } = revalidateSeconds ? { cache: 'force-cache', next: { revalidate: revalidateSeconds } } @@ -47,9 +54,17 @@ function getExchangeLabel(symbol: string, exchange?: string) { } const parts = symbol.split('.'); - const suffix = parts.length > 1 ? parts[parts.length - 1] : ''; + const suffix = parts.length > 1 ? parts[parts.length - 1].toUpperCase() : ''; - return suffix || 'US'; + if (!suffix) { + return 'US'; + } + + if (FINNHUB_EXCHANGE_SUFFIXES.has(suffix) || suffix.length >= 2) { + return suffix; + } + + return 'US'; } export async function getQuote(symbol: string) { From 80454b914118ff189f3b83b26ab82f1ee3020e22 Mon Sep 17 00:00:00 2001 From: keshav-005 Date: Thu, 23 Apr 2026 00:48:24 +0530 Subject: [PATCH 3/3] Refine search exchange label fallback --- lib/actions/finnhub.actions.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/actions/finnhub.actions.ts b/lib/actions/finnhub.actions.ts index 9262448..0c9fcb7 100644 --- a/lib/actions/finnhub.actions.ts +++ b/lib/actions/finnhub.actions.ts @@ -50,7 +50,7 @@ export { fetchJSON }; function getExchangeLabel(symbol: string, exchange?: string) { if (exchange?.trim()) { - return exchange; + return exchange.trim(); } const parts = symbol.split('.'); @@ -60,11 +60,7 @@ function getExchangeLabel(symbol: string, exchange?: string) { return 'US'; } - if (FINNHUB_EXCHANGE_SUFFIXES.has(suffix) || suffix.length >= 2) { - return suffix; - } - - return 'US'; + return FINNHUB_EXCHANGE_SUFFIXES.has(suffix) ? suffix : 'US'; } export async function getQuote(symbol: string) {