/* * ============================================================================= * VibeQuant (vquant) — AI-Powered Financial Intelligence Platform * ----------------------------------------------------------------------------- * File: client/src/components/explore/metrics-view.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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { TrendingUp, TrendingDown, DollarSign, Percent, TrendingUpDown, Calculator, PieChart, Activity } from "lucide-react"; interface KeyMetrics { date: string; symbol: string; period: string; revenuePerShare: number; netIncomePerShare: number; operatingCashFlowPerShare: number; freeCashFlowPerShare: number; cashPerShare: number; bookValuePerShare: number; tangibleBookValuePerShare: number; shareholdersEquityPerShare: number; interestDebtPerShare: number; marketCap: number; enterpriseValue: number; peRatio: number; priceToSalesRatio: number; pocfratio: number; pfcfRatio: number; pbRatio: number; ptbRatio: number; evToSales: number; enterpriseValueOverEBITDA: number; evToOperatingCashFlow: number; evToFreeCashFlow: number; earningsYield: number; freeCashFlowYield: number; debtToEquity: number; debtToAssets: number; netDebtToEBITDA: number; currentRatio: number; interestCoverage: number; incomeQuality: number; dividendYield: number; payoutRatio: number; salesGeneralAndAdministrativeToRevenue: number; researchAndDdevelopementToRevenue: number; intangiblesToTotalAssets: number; capexToOperatingCashFlow: number; capexToRevenue: number; capexToDepreciation: number; stockBasedCompensationToRevenue: number; grahamNumber: number; roic: number; returnOnTangibleAssets: number; grahamNetNet: number; workingCapital: number; tangibleAssetValue: number; netCurrentAssetValue: number; investedCapital: number; averageReceivables: number; averagePayables: number; averageInventory: number; daysSalesOutstanding: number; daysPayablesOutstanding: number; daysOfInventoryOnHand: number; receivablesTurnover: number; payablesTurnover: number; inventoryTurnover: number; roe: number; capexPerShare: number; [key: string]: any; } interface MetricsViewProps { keyMetrics?: KeyMetrics[]; } export function MetricsView({ keyMetrics }: MetricsViewProps) { const formatNumber = (value: number) => { if (!value) return "N/A"; const absValue = Math.abs(value); if (absValue >= 1e12) return `$${(value / 1e12).toFixed(2)}T`; if (absValue >= 1e9) return `$${(value / 1e9).toFixed(2)}B`; if (absValue >= 1e6) return `$${(value / 1e6).toFixed(2)}M`; if (absValue >= 1e3) return `$${(value / 1e3).toFixed(2)}K`; return `$${value.toFixed(2)}`; }; const formatPercent = (value: number) => { if (!value && value !== 0) return "N/A"; return `${(value * 100).toFixed(2)}%`; }; const formatRatio = (value: number) => { if (!value && value !== 0) return "N/A"; return value.toFixed(2); }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short' }); }; const getChangeIndicator = (current: number, previous: number) => { if (!current || !previous) return null; const change = ((current - previous) / Math.abs(previous)) * 100; const isPositive = change >= 0; return ( {isPositive ? : } {isPositive ? '+' : ''}{change.toFixed(1)}% ); }; if (!keyMetrics || keyMetrics.length === 0) { return ( Métriques Clés Aucune donnée disponible ); } const metricsSections = [ { title: "Métriques de Valorisation", icon: , description: "Ratios de valorisation et prix", metrics: [ { label: "Capitalisation Boursière", key: "marketCap", format: "currency" }, { label: "Valeur d'Entreprise", key: "enterpriseValue", format: "currency" }, { label: "P/E Ratio", key: "peRatio", format: "ratio" }, { label: "P/S Ratio", key: "priceToSalesRatio", format: "ratio" }, { label: "P/B Ratio", key: "pbRatio", format: "ratio" }, { label: "P/TBV Ratio", key: "ptbRatio", format: "ratio" }, { label: "P/FCF Ratio", key: "pfcfRatio", format: "ratio" }, { label: "EV/Sales", key: "evToSales", format: "ratio" }, { label: "EV/EBITDA", key: "enterpriseValueOverEBITDA", format: "ratio" }, { label: "EV/Operating CF", key: "evToOperatingCashFlow", format: "ratio" }, { label: "EV/Free CF", key: "evToFreeCashFlow", format: "ratio" }, { label: "Graham Number", key: "grahamNumber", format: "currency" }, ] }, { title: "Rentabilité & Rendements", icon: , description: "Indicateurs de rentabilité", metrics: [ { label: "ROE (Return on Equity)", key: "roe", format: "percent" }, { label: "ROIC (Return on Invested Capital)", key: "roic", format: "percent" }, { label: "Return on Tangible Assets", key: "returnOnTangibleAssets", format: "percent" }, { label: "Earnings Yield", key: "earningsYield", format: "percent" }, { label: "Free Cash Flow Yield", key: "freeCashFlowYield", format: "percent" }, { label: "Dividend Yield", key: "dividendYield", format: "percent" }, { label: "Payout Ratio", key: "payoutRatio", format: "percent" }, { label: "Income Quality", key: "incomeQuality", format: "ratio" }, ] }, { title: "Métriques par Action", icon: , description: "Données normalisées par action", metrics: [ { label: "Revenu par Action", key: "revenuePerShare", format: "currency" }, { label: "Bénéfice Net par Action", key: "netIncomePerShare", format: "currency" }, { label: "Operating CF par Action", key: "operatingCashFlowPerShare", format: "currency" }, { label: "Free CF par Action", key: "freeCashFlowPerShare", format: "currency" }, { label: "Cash par Action", key: "cashPerShare", format: "currency" }, { label: "Book Value par Action", key: "bookValuePerShare", format: "currency" }, { label: "Tangible BV par Action", key: "tangibleBookValuePerShare", format: "currency" }, { label: "Shareholders Equity par Action", key: "shareholdersEquityPerShare", format: "currency" }, { label: "Capex par Action", key: "capexPerShare", format: "currency" }, ] }, { title: "Santé Financière & Dette", icon: , description: "Indicateurs de solvabilité et liquidité", metrics: [ { label: "Dette/Capitaux Propres", key: "debtToEquity", format: "ratio" }, { label: "Dette/Actifs", key: "debtToAssets", format: "ratio" }, { label: "Dette Nette/EBITDA", key: "netDebtToEBITDA", format: "ratio" }, { label: "Ratio de Liquidité", key: "currentRatio", format: "ratio" }, { label: "Couverture des Intérêts", key: "interestCoverage", format: "ratio" }, { label: "Fonds de Roulement", key: "workingCapital", format: "currency" }, { label: "Capital Investi", key: "investedCapital", format: "currency" }, { label: "Tangible Asset Value", key: "tangibleAssetValue", format: "currency" }, ] }, { title: "Efficacité Opérationnelle", icon: , description: "Ratios de rotation et cycles d'exploitation", metrics: [ { label: "Rotation des Créances", key: "receivablesTurnover", format: "ratio" }, { label: "Rotation des Dettes", key: "payablesTurnover", format: "ratio" }, { label: "Rotation des Stocks", key: "inventoryTurnover", format: "ratio" }, { label: "Jours de Recouvrement (DSO)", key: "daysSalesOutstanding", format: "ratio" }, { label: "Jours de Paiement (DPO)", key: "daysPayablesOutstanding", format: "ratio" }, { label: "Jours de Stock (DIO)", key: "daysOfInventoryOnHand", format: "ratio" }, { label: "Créances Moyennes", key: "averageReceivables", format: "currency" }, { label: "Dettes Moyennes", key: "averagePayables", format: "currency" }, { label: "Stock Moyen", key: "averageInventory", format: "currency" }, ] }, { title: "Dépenses & Allocation", icon: , description: "Structure des dépenses en % du revenu", metrics: [ { label: "SG&A / Revenu", key: "salesGeneralAndAdministrativeToRevenue", format: "percent" }, { label: "R&D / Revenu", key: "researchAndDdevelopementToRevenue", format: "percent" }, { label: "Stock Comp / Revenu", key: "stockBasedCompensationToRevenue", format: "percent" }, { label: "Capex / Operating CF", key: "capexToOperatingCashFlow", format: "percent" }, { label: "Capex / Revenu", key: "capexToRevenue", format: "percent" }, { label: "Capex / Depreciation", key: "capexToDepreciation", format: "ratio" }, { label: "Intangibles / Total Assets", key: "intangiblesToTotalAssets", format: "percent" }, ] }, ]; return (
{metricsSections.map((section, sectionIdx) => ( {section.icon} {section.title} {section.description}
Métrique {keyMetrics.slice(0, 5).map((item, idx) => (
{formatDate(item.date)}
{item.period}
))}
{section.metrics.map((metric, metricIdx) => ( {metric.label} {keyMetrics.slice(0, 5).map((item, idx) => { const value = item[metric.key]; const prevValue = idx < keyMetrics.length - 1 ? keyMetrics[idx + 1][metric.key] : null; let formattedValue = "N/A"; if (value || value === 0) { switch (metric.format) { case "currency": formattedValue = formatNumber(value); break; case "percent": formattedValue = formatPercent(value); break; case "ratio": formattedValue = formatRatio(value); break; default: formattedValue = value.toString(); } } return (
{formattedValue}
{value && prevValue && getChangeIndicator(value, prevValue)}
); })}
))}
))}
); }