spb/vquant Public MIT
VibeQuant — AI-powered institutional-grade financial intelligence platform.
TypeScript 84.3%
Python 11.7%
JavaScript 1.6%
CSS 1.5%
HTML 0.7%
1/*2 * =============================================================================3 * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 * File: client/src/components/chat/empty-state.tsx6 *7 * Author: Simon-Pierre Boucher8 * Contact: contact@spboucher.ai9 * Website: https://www.spboucher.ai10 * Demo: https://www.vquant.ai11 * License: MIT (see LICENSE)12 *13 * Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { useState } from "react";18import {19 TrendingUp, LineChart, DollarSign, PieChart, Sparkles,20 BarChart3, Activity, TrendingDown, Calculator, Target,21 Building2, Newspaper, Users, Globe, Coins, Gauge,22 Wand2, Crown, Gem, Atom, Feather, Rabbit, Bot,23 Brain, Cpu, Zap, Rocket, Bird, Sun, Earth, Moon24} from "lucide-react";25import {26 Select,27 SelectContent,28 SelectItem,29 SelectTrigger,30 SelectValue,31} from "@/components/ui/select";32import { AnimatePresence, motion } from "framer-motion";33import type { ClaudeModel } from "@/hooks/useChatSession";3435interface EmptyStateProps {36 onQuestionClick?: (question: string) => void;37 selectedModel?: ClaudeModel;38 onModelChange?: (model: ClaudeModel) => void;39}4041const MODEL_OPTIONS: { value: ClaudeModel; label: string; sub: string; icon: typeof Sparkles; disabled?: boolean }[] = [42 { value: "claude-fable-5", label: "Fable 5", sub: "Le plus intelligent (defaut)", icon: Wand2 },43 { value: "claude-opus-4-8", label: "Opus 4.8", sub: "Le plus puissant", icon: Crown },44 { value: "claude-opus-4-7", label: "Opus 4.7", sub: "Equilibre", icon: Gem },45 { value: "claude-opus-4-6", label: "Opus 4.6", sub: "Rapide", icon: Atom },46 { value: "claude-sonnet-4-6", label: "Sonnet 4.6", sub: "Polyvalent", icon: Feather },47 { value: "claude-haiku-4-5-20251001", label: "Haiku 4.5", sub: "Le plus rapide", icon: Rabbit },48 { value: "gpt-5.6-sol", label: "GPT-5.6 Sol", sub: "OpenAI - le plus puissant", icon: Sun },49 { value: "gpt-5.6-terra", label: "GPT-5.6 Terra", sub: "OpenAI - equilibre", icon: Earth },50 { value: "gpt-5.6-luna", label: "GPT-5.6 Luna", sub: "OpenAI - rapide et econome", icon: Moon },51 { value: "gpt-5.5", label: "GPT-5.5", sub: "OpenAI - puissant", icon: Bot },52 { value: "gpt-5.4", label: "GPT-5.4", sub: "OpenAI - contexte 1M", icon: Brain },53 { value: "gpt-5.2", label: "GPT-5.2", sub: "OpenAI - puissant", icon: Cpu },54 { value: "gpt-5.1", label: "GPT-5.1", sub: "OpenAI - equilibre", icon: Zap },55 { value: "gpt-5", label: "GPT-5", sub: "OpenAI - classique", icon: Sparkles },56 { value: "gpt-5-mini", label: "GPT-5 Mini", sub: "OpenAI - rapide", icon: Rocket },57 { value: "gpt-5-nano", label: "GPT-5 Nano", sub: "OpenAI - le plus rapide", icon: Bird },58 { value: "gpt-4.1", label: "GPT-4.1", sub: "OpenAI - contexte 1M", icon: Globe },59 { value: "gpt-4.1-mini", label: "GPT-4.1 Mini", sub: "OpenAI - econome", icon: Feather },60 { value: "gemini-3.5-flash", label: "Gemini 3.5 Flash", sub: "Google - le plus recent", icon: Sparkles },61 { value: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro", sub: "Google - le plus puissant", icon: Gem },62 { value: "gemini-3-flash-preview", label: "Gemini 3 Flash", sub: "Google - equilibre", icon: Zap },63 { value: "gemini-3.1-flash-lite", label: "Gemini 3.1 Flash Lite", sub: "Google - le plus rapide", icon: Bird },64];6566const QUESTION_CATEGORIES = [67 {68 category: "Analyse Fondamentale",69 icon: Building2,70 questions: [71 "Analyse complète d'Apple (AAPL)",72 "Profil et métriques clés de Microsoft (MSFT)",73 "Bilan financier et ratios de Tesla",74 "États financiers complets de Google (GOOGL)",75 "Croissance des revenus d'Amazon sur 5 ans",76 "Flux de trésorerie de Meta (META)",77 "Santé financière et ratios clés de Netflix",78 ]79 },80 {81 category: "Analyse Technique",82 icon: Activity,83 questions: [84 "Indicateurs techniques RSI et MACD pour Tesla",85 "Moyennes mobiles EMA et SMA pour AAPL",86 "Analyse ADX et tendance de NVIDIA",87 "Stochastique et Williams %R pour AMD",88 "Tous les indicateurs techniques pour SPY",89 "Analyse technique complète de Bitcoin",90 "Points d'entrée et sortie pour Amazon",91 ]92 },93 {94 category: "Performance et Historique",95 icon: LineChart,96 questions: [97 "Performance de Tesla sur l'année",98 "Historique des prix Apple sur 5 ans",99 "Comparaison AAPL vs MSFT sur 2 ans",100 "Performance du S&P 500 ce trimestre",101 "Évolution du NASDAQ depuis janvier",102 "Historique intraday de NVDA aujourd'hui",103 "Performance des FAANG cette année",104 ]105 },106 {107 category: "Dividendes et Actions Corporate",108 icon: DollarSign,109 questions: [110 "Historique des dividendes Microsoft",111 "Rendement des dividendes de Coca-Cola",112 "Splits d'actions d'Apple historique",113 "Rachats d'actions et dividendes de JPMorgan",114 "Entreprises avec les meilleurs dividendes en technologie",115 "Calendrier des dividendes pour ce mois",116 ]117 },118 {119 category: "Résultats et Estimations",120 icon: Target,121 questions: [122 "Prochains résultats trimestriels d'Apple",123 "Surprises de résultats pour Tesla Q3",124 "Estimations des analystes pour NVIDIA",125 "Calendrier des résultats cette semaine",126 "Objectifs de prix des analystes pour Meta",127 "Révisions à la hausse ou baisse pour Amazon",128 ]129 },130 {131 category: "Actualités et Événements",132 icon: Newspaper,133 questions: [134 "Dernières nouvelles sur Tesla",135 "Communiqués de presse Apple cette semaine",136 "Actualités économiques du jour",137 "Calendrier économique de la semaine",138 "Prochaines IPO à surveiller",139 "Événements importants en finance cette semaine",140 ]141 },142 {143 category: "Trading d'Initiés et Institutionnel",144 icon: Users,145 questions: [146 "Trading d'initiés récent pour Apple",147 "Achats d'actions par les dirigeants de Tesla",148 "Trading des membres du Congrès ce mois",149 "Positions institutionnelles dans NVIDIA",150 "Changements de participations dans Meta",151 ]152 },153 {154 category: "Marchés Globaux",155 icon: Globe,156 questions: [157 "Taux de change EUR/USD aujourd'hui",158 "Prix des commodités or et pétrole",159 "Taux du Trésor américain actuels",160 "Performance des indices mondiaux",161 "Analyse du marché Forex majeurs",162 "COT Report pour le pétrole",163 ]164 },165 {166 category: "Gestion de Portefeuille",167 icon: PieChart,168 questions: [169 "Optimiser portefeuille AAPL, MSFT, GOOGL",170 "Allocation optimale pour tech stocks",171 "Diversification avec 10 actions",172 "Frontière efficiente pour mon portefeuille",173 "Ratio risque/rendement optimal",174 ]175 },176 {177 category: "Analyse de Risque",178 icon: TrendingDown,179 questions: [180 "Value at Risk (VaR) pour Tesla",181 "Volatilité GARCH de Bitcoin",182 "Métriques de risque pour mon portefeuille",183 "Drawdown maximum de NVIDIA",184 "Corrélations entre FAANG stocks",185 "Beta et volatilité d'Amazon",186 ]187 },188 {189 category: "Simulations et Prévisions",190 icon: BarChart3,191 questions: [192 "Simulation Monte Carlo NVDA $100k",193 "Projection de croissance Apple 5 ans",194 "Scénarios d'investissement Tesla",195 "Simulation de portefeuille diversifié",196 "Prévisions basées sur tendances historiques",197 ]198 },199 {200 category: "Options et Dérivés",201 icon: Calculator,202 questions: [203 "Prix d'option call Apple strike 180",204 "Stratégie d'options pour Tesla",205 "Surface de volatilité implicite NVDA",206 "Greeks pour options Microsoft",207 "Valorisation Black-Scholes pour call",208 ]209 },210 {211 category: "Screener et Comparaisons",212 icon: Gauge,213 questions: [214 "Meilleures actions tech par P/E ratio",215 "Compagnies similaires à Apple",216 "Screener: dividendes > 3% et P/E < 15",217 "Top 10 actions momentum ce mois",218 "Comparaison des géants tech FAANG",219 ]220 },221];222223export function EmptyState({ onQuestionClick, selectedModel, onModelChange }: EmptyStateProps) {224 const [selectedCategoryIndex, setSelectedCategoryIndex] = useState<number | null>(null);225226 const handleCategoryChange = (value: string) => {227 const index = parseInt(value);228 setSelectedCategoryIndex(index);229 };230231 const selectedCategory = selectedCategoryIndex !== null232 ? QUESTION_CATEGORIES[selectedCategoryIndex]233 : null;234235 return (236 <div className="flex flex-col items-center justify-center min-h-[60vh] space-y-8 animate-fade-in">237 <div className="relative text-center space-y-4">238 <div aria-hidden className="pointer-events-none absolute left-1/2 top-[-3rem] -z-10 h-64 w-[40rem] -translate-x-1/2 bg-grid hero-mask opacity-70" />239 <div aria-hidden className="pointer-events-none absolute left-1/2 top-[-1rem] -z-10 h-56 w-56 -translate-x-1/2 rounded-full bg-primary/20 blur-3xl" />240 <div className="flex flex-col items-center justify-center gap-4 mb-2">241 <div className="relative grid place-items-center size-16 rounded-2xl bg-hot shadow-hot animate-float">242 <span className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/40 to-transparent opacity-60" />243 <Sparkles className="relative w-8 h-8 text-white" />244 </div>245 <h1 className="text-4xl sm:text-5xl font-extrabold tracking-tight">246 <span className="text-foreground">V</span><span className="gradient-text">Quant</span>247 </h1>248 <div className="inline-flex items-center gap-2 rounded-full border border-border bg-card/70 px-3.5 py-1.5 text-xs font-medium text-muted-foreground backdrop-blur">249 <span className="size-2 rounded-full bg-primary animate-pulse" />250 200+ outils financiers - Python integre - Propulse par Fable 5251 </div>252 </div>253 <p className="text-lg text-muted-foreground max-w-md">254 Votre assistant d'analyse financière propulsé par l'IA255 </p>256 </div>257258 <div className="w-full max-w-4xl space-y-6 px-4">259 <div className="text-center">260 <p className="text-sm text-muted-foreground mb-3">261 Choisissez une catégorie pour découvrir des exemples de questions262 </p>263 </div>264265 {/* Category dropdown */}266 <div>267 <Select onValueChange={handleCategoryChange}>268 <SelectTrigger className="w-full h-12 text-base font-medium bg-card border border-border rounded-lg transition-colors hover:border-primary/40">269 <div className="flex items-center gap-3 px-1">270 <Sparkles className="w-4 h-4 text-primary flex-shrink-0" />271 <SelectValue placeholder="Sélectionnez une catégorie d'analyse..." />272 </div>273 </SelectTrigger>274275 <SelectContent className="w-[var(--radix-select-trigger-width)] max-h-[450px] bg-card border border-border rounded-lg p-1">276 <div className="px-3 py-2 mb-1 border-b border-border">277 <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">278 {QUESTION_CATEGORIES.length} Catégories Disponibles279 </p>280 </div>281282 <div className="space-y-0.5">283 {QUESTION_CATEGORIES.map((category, idx) => {284 const Icon = category.icon;285 return (286 <SelectItem287 key={idx}288 value={idx.toString()}289 className="cursor-pointer py-3 px-3 text-base rounded-md transition-colors"290 >291 <div className="flex items-center gap-3">292 <div className="p-1.5 rounded-md bg-primary/10 border border-primary/20">293 <Icon className="w-4 h-4 text-primary" />294 </div>295 <div className="flex flex-col flex-1">296 <span className="font-medium text-foreground">297 {category.category}298 </span>299 <span className="text-xs text-muted-foreground">300 {category.questions.length} exemples301 </span>302 </div>303 </div>304 </SelectItem>305 );306 })}307 </div>308 </SelectContent>309 </Select>310 </div>311312 {/* Questions for selected category */}313 <AnimatePresence mode="wait">314 {selectedCategory && (315 <motion.div316 key={selectedCategoryIndex}317 initial={{ opacity: 0, y: 12 }}318 animate={{ opacity: 1, y: 0 }}319 exit={{ opacity: 0, y: -12 }}320 transition={{ duration: 0.2 }}321 className="space-y-4"322 >323 {/* Category header */}324 <div className="flex items-center gap-3 p-4 rounded-lg bg-card border border-border">325 {(() => {326 const Icon = selectedCategory.icon;327 return (328 <div className="p-2 rounded-md bg-primary/10 border border-primary/20">329 <Icon className="w-5 h-5 text-primary" />330 </div>331 );332 })()}333 <h3 className="text-xl font-bold text-foreground">334 {selectedCategory.category}335 </h3>336 </div>337338 {/* Questions grid */}339 <div className="grid grid-cols-1 md:grid-cols-2 gap-3">340 {selectedCategory.questions.map((question, qIdx) => (341 <button342 key={qIdx}343 onClick={() => onQuestionClick?.(question)}344 className="group p-4 text-left rounded-lg bg-card border border-border hover:border-primary/40 transition-colors"345 >346 <div className="flex items-start gap-3">347 <div className="flex-shrink-0 w-7 h-7 rounded-md bg-muted flex items-center justify-center font-medium text-sm text-muted-foreground">348 {qIdx + 1}349 </div>350 <p className="flex-1 text-sm font-medium leading-relaxed group-hover:text-foreground transition-colors">351 {question}352 </p>353 </div>354 </button>355 ))}356 </div>357 </motion.div>358 )}359 </AnimatePresence>360361 {/* Model selector */}362 <div className="pt-2">363 <p className="text-xs text-muted-foreground mb-2 text-center">364 Modele d IA utilise365 </p>366 <Select value={selectedModel} onValueChange={(v) => onModelChange?.(v as ClaudeModel)}>367 <SelectTrigger className="w-full h-12 text-base font-medium bg-card border border-border rounded-lg transition-colors hover:border-primary/40">368 <div className="flex items-center gap-3 px-1">369 {(() => {370 const cur = MODEL_OPTIONS.find((m) => m.value === selectedModel) ?? MODEL_OPTIONS[0];371 const Icon = cur.icon;372 return (373 <>374 <Icon className="w-4 h-4 text-primary flex-shrink-0" />375 <span className="text-foreground">{cur.label}</span>376 </>377 );378 })()}379 </div>380 </SelectTrigger>381 <SelectContent className="w-[var(--radix-select-trigger-width)] bg-card border border-border rounded-lg p-1">382 <div className="px-3 py-2 mb-1 border-b border-border">383 <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">384 Choisir un modele385 </p>386 </div>387 <div className="space-y-0.5">388 {MODEL_OPTIONS.map((m) => {389 const Icon = m.icon;390 return (391 <SelectItem392 key={m.value}393 value={m.value}394 textValue={m.label}395 disabled={m.disabled}396 className="cursor-pointer py-3 px-3 text-base rounded-md transition-colors data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed"397 >398 <div className="flex items-center gap-3">399 <div className="p-1.5 rounded-md bg-primary/10 border border-primary/20">400 <Icon className="w-4 h-4 text-primary" />401 </div>402 <div className="flex flex-col flex-1">403 <span className="font-medium text-foreground">{m.label}</span>404 <span className="text-xs text-muted-foreground">{m.sub}</span>405 </div>406 {m.disabled && (407 <span className="text-[10px] uppercase tracking-wider font-semibold text-amber-500 border border-amber-500/40 rounded px-1.5 py-0.5">408 Indispo409 </span>410 )}411 </div>412 </SelectItem>413 );414 })}415 </div>416 </SelectContent>417 </Select>418 </div>419 </div>420 </div>421 );422}423