/* * ============================================================================= * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform * ----------------------------------------------------------------------------- * File: client/src/pages/explore.tsx * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * Website: https://www.spboucher.ai * Demo: https://www.vquant.ai * License: MIT (see LICENSE) * * Copyright © 2026 Simon-Pierre Boucher. All rights reserved. * ============================================================================= */ import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useLocation } from "wouter"; import { Sparkles, BookOpen, Search, ArrowLeft, BarChart3, FileText, DollarSign, Users, Award, Newspaper } from "lucide-react"; import { ThemeToggle } from "@/components/theme-toggle"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; // New explore-specific components import { MarketOverview } from "@/components/explore/market-overview"; import { StockHeader } from "@/components/explore/stock-header"; import { FinancialStatementsView } from "@/components/explore/financial-statements-view"; import { MetricsView } from "@/components/explore/metrics-view"; import { AnalystView } from "@/components/explore/analyst-view"; import { OwnershipView } from "@/components/explore/ownership-view"; import { NewsView } from "@/components/explore/news-view"; import { AdvancedPriceChart } from "@/components/financial/advanced-price-chart"; import { MarketTicker } from "@/components/market-ticker"; export default function Explore() { const [, setLocation] = useLocation(); const [searchQuery, setSearchQuery] = useState(""); const [selectedTicker, setSelectedTicker] = useState(null); const [statementPeriod, setStatementPeriod] = useState<"annual" | "quarter">("annual"); // Fetch market overview data - Major global indices const { data: marketIndices } = useQuery({ queryKey: ['market-indices'], queryFn: async () => { // Major global indices to display const symbols = [ '^GSPC', // S&P 500 '^DJI', // Dow Jones Industrial Average '^IXIC', // NASDAQ Composite '^RUT', // Russell 2000 '^FTSE', // FTSE 100 (UK) '^GDAXI', // DAX (Germany) '^FCHI', // CAC 40 (France) '^N225', // Nikkei 225 (Japan) '^HSI', // Hang Seng (Hong Kong) '^STOXX50E', // EURO STOXX 50 '^AXJO', // S&P/ASX 200 (Australia) '^GSPTSE' // S&P/TSX Composite (Canada) ]; const symbolsParam = symbols.join(','); const res = await fetch(`https://financialmodelingprep.com/stable/quote?symbol=${symbolsParam}&apikey=${import.meta.env.VITE_FMP_API_KEY || 'JeWwQMjWS3H6hBGHaVxKy1WfGyU5ZeFq'}`); if (!res.ok) throw new Error('Failed to fetch market indices'); const data = await res.json(); // Transform data to match expected format with validation return data .filter((index: any) => index && index.symbol && index.price != null) .map((index: any) => ({ symbol: index.symbol, name: index.name || index.symbol, price: Number(index.price) || 0, change: Number(index.change) || 0, changesPercentage: Number(index.changesPercentage) || 0, dayLow: Number(index.dayLow) || 0, dayHigh: Number(index.dayHigh) || 0, yearHigh: Number(index.yearHigh) || 0, yearLow: Number(index.yearLow) || 0, volume: Number(index.volume) || 0, previousClose: Number(index.previousClose) || 0 })); }, refetchInterval: 60000, }); const { data: marketStatus } = useQuery({ queryKey: ['market-status'], queryFn: async () => { const res = await fetch('/api/fmp/market-hours'); if (!res.ok) throw new Error('Failed to fetch market status'); return res.json(); }, }); const { data: activeStocks } = useQuery({ queryKey: ['active-stocks'], queryFn: async () => { const res = await fetch('/api/fmp/actives'); if (!res.ok) throw new Error('Failed to fetch active stocks'); return res.json(); }, }); const { data: gainers } = useQuery({ queryKey: ['gainers'], queryFn: async () => { const res = await fetch('/api/fmp/gainers'); if (!res.ok) throw new Error('Failed to fetch gainers'); return res.json(); }, }); const { data: losers } = useQuery({ queryKey: ['losers'], queryFn: async () => { const res = await fetch('/api/fmp/losers'); if (!res.ok) throw new Error('Failed to fetch losers'); return res.json(); }, }); // Fetch stock data when ticker is selected const { data: companyProfile } = useQuery({ queryKey: ['company-profile', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/company-profile/${selectedTicker}`); if (!res.ok) throw new Error('Failed to fetch company profile'); return res.json(); }, enabled: !!selectedTicker, }); const { data: stockQuote } = useQuery({ queryKey: ['stock-quote', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/stock-quote/${selectedTicker}`); if (!res.ok) throw new Error('Failed to fetch stock quote'); return res.json(); }, enabled: !!selectedTicker, refetchInterval: 5000, }); const { data: historicalPrice } = useQuery({ queryKey: ['historical-price', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/historical-price/${selectedTicker}`); if (!res.ok) throw new Error('Failed to fetch historical price'); return res.json(); }, enabled: !!selectedTicker, }); const { data: incomeStatement } = useQuery({ queryKey: ['income-statement', selectedTicker, statementPeriod], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/income-statement/${selectedTicker}?period=${statementPeriod}&limit=5`); if (!res.ok) throw new Error('Failed to fetch income statement'); return res.json(); }, enabled: !!selectedTicker, }); const { data: balanceSheet } = useQuery({ queryKey: ['balance-sheet', selectedTicker, statementPeriod], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/balance-sheet/${selectedTicker}?period=${statementPeriod}&limit=5`); if (!res.ok) throw new Error('Failed to fetch balance sheet'); return res.json(); }, enabled: !!selectedTicker, }); const { data: cashFlow } = useQuery({ queryKey: ['cash-flow', selectedTicker, statementPeriod], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/cash-flow/${selectedTicker}?period=${statementPeriod}&limit=5`); if (!res.ok) throw new Error('Failed to fetch cash flow'); return res.json(); }, enabled: !!selectedTicker, }); const { data: keyMetrics } = useQuery({ queryKey: ['key-metrics', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/key-metrics/${selectedTicker}?period=annual&limit=5`); if (!res.ok) throw new Error('Failed to fetch key metrics'); return res.json(); }, enabled: !!selectedTicker, }); const { data: analystEstimates } = useQuery({ queryKey: ['analyst-estimates', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/analyst-estimates/${selectedTicker}?period=annual`); if (!res.ok) throw new Error('Failed to fetch analyst estimates'); return res.json(); }, enabled: !!selectedTicker, }); const { data: dividends } = useQuery({ queryKey: ['dividends', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/dividend-history/${selectedTicker}`); if (!res.ok) throw new Error('Failed to fetch dividends'); return res.json(); }, enabled: !!selectedTicker, }); const { data: institutionalHolders } = useQuery({ queryKey: ['institutional-holders', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/institutional-holders/${selectedTicker}`); if (!res.ok) throw new Error('Failed to fetch institutional holders'); return res.json(); }, enabled: !!selectedTicker, }); const { data: news } = useQuery({ queryKey: ['news', selectedTicker], queryFn: async () => { if (!selectedTicker) return null; const res = await fetch(`/api/fmp/financial-news/${selectedTicker}?limit=20`); if (!res.ok) throw new Error('Failed to fetch news'); return res.json(); }, enabled: !!selectedTicker, }); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); if (searchQuery.trim()) { setSelectedTicker(searchQuery.trim().toUpperCase()); } }; const handleTickerClick = (ticker: string) => { setSelectedTicker(ticker); setSearchQuery(ticker); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const handleBack = () => { setSelectedTicker(null); setSearchQuery(""); window.scrollTo({ top: 0, behavior: 'smooth' }); }; return (
{/* Header */}

Documentation

{/* Market Ticker Banner */} {/* Main Content */}
{/* Search Section */}

Exploration Boursière

Analysez en profondeur n'importe quel titre sur les marchés

{/* Search Bar */}
setSearchQuery(e.target.value.toUpperCase())} className="pl-4 pr-12 h-14 text-lg rounded-2xl" />
{/* Market Overview or Stock Dashboard */} {!selectedTicker ? ( ) : ( /* Stock Dashboard */
{/* Stock Header */} {/* Price Chart */} {historicalPrice && ( )} {/* Tabs for different sections */} États Financiers Financiers Métriques Clés Métriques Analystes Analystes Actionnariat Actions Actualités News {/* Financial Statements Tab */}

États Financiers Détaillés

Compte de résultat, bilan et flux de trésorerie

{/* Key Metrics Tab */}

Métriques et Ratios Financiers

Analyse approfondie des ratios de valorisation, rentabilité et efficacité

{/* Analysts Tab */}

Estimations et Dividendes

Prévisions des analystes et historique des dividendes

{/* Ownership Tab */}

Structure Actionnariale

Actionnaires institutionnels et changements de position

{/* News Tab */}
)}
); }