import React, { useState, useEffect, useMemo } from 'react'; import { LineChart, Wallet, ArrowUpRight, ArrowDownRight, Activity, DollarSign, PieChart, RefreshCw, AlertCircle, TrendingUp, CreditCard, Calendar } from 'lucide-react'; // --- Composants UI --- const Card = ({ children, className = '' }) => (
{children}
); const Badge = ({ children, type = 'neutral' }) => { const colors = { success: 'bg-emerald-100 text-emerald-700', danger: 'bg-rose-100 text-rose-700', neutral: 'bg-slate-100 text-slate-700', warning: 'bg-amber-100 text-amber-800' }; return ( {children} ); }; // --- Données Simulées (Fallback) --- const MOCK_DATA = { portfolio: { totalValue: 124592.45, dayChange: 1245.30, dayChangePercent: 1.01, cashBalance: 15400.00 }, assets: [ { id: 1, symbol: 'AAPL', name: 'Apple Inc.', quantity: 150, price: 178.35, change: 1.25, type: 'stock' }, { id: 2, symbol: 'MSFT', name: 'Microsoft Corp.', quantity: 80, price: 332.40, change: -0.45, type: 'stock' }, { id: 3, symbol: 'VTI', name: 'Vanguard Total Stock', quantity: 200, price: 225.10, change: 0.85, type: 'etf' }, { id: 4, symbol: 'BTC', name: 'Bitcoin', quantity: 0.45, price: 42150.00, change: 2.30, type: 'crypto' }, { id: 5, symbol: 'TSLA', name: 'Tesla Inc.', quantity: 45, price: 245.60, change: -1.20, type: 'stock' }, ], history: Array.from({ length: 14 }).map((_, i) => ({ date: new Date(Date.now() - (13 - i) * 24 * 60 * 60 * 1000).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short' }), value: 120000 + Math.random() * 5000 + (i * 300) })), transactions: [ { id: 101, type: 'buy', asset: 'VTI', amount: 2250.00, date: '2023-10-25', status: 'completed' }, { id: 102, type: 'dividend', asset: 'MSFT', amount: 45.20, date: '2023-10-24', status: 'completed' }, { id: 103, type: 'deposit', asset: 'CAD', amount: 1000.00, date: '2023-10-22', status: 'completed' }, { id: 104, type: 'sell', asset: 'TSLA', amount: 1245.50, date: '2023-10-20', status: 'completed' }, ] }; // --- Composant Principal --- export default function FinanceDashboard() { const [loading, setLoading] = useState(true); const [data, setData] = useState(null); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState('overview'); const [usingMock, setUsingMock] = useState(false); // Fonction pour charger les données (simule un appel API) const fetchData = async () => { setLoading(true); setError(null); try { // Tentative de connexion à l'API (Simulée ici pour échouer et passer au fallback) // Dans un cas réel, ce serait: const response = await fetch('https://finance.jeva.ca/api.php'); // Simulation d'un échec réseau pour démontrer le fallback const shouldFail = true; if (shouldFail) { throw new Error("API non trouvée (500)"); } // Si succès (code inatteignable ici) // const result = await response.json(); // setData(result); } catch (err) { console.warn("Mode API échoué, passage en mode Démo:", err.message); setUsingMock(true); // Délai artificiel pour l'UX await new Promise(resolve => setTimeout(resolve, 800)); setData(MOCK_DATA); } finally { setLoading(false); } }; useEffect(() => { fetchData(); }, []); // Calculs dérivés const assetAllocation = useMemo(() => { if (!data) return []; const types = data.assets.reduce((acc, asset) => { const value = asset.quantity * asset.price; acc[asset.type] = (acc[asset.type] || 0) + value; return acc; }, {}); const total = Object.values(types).reduce((a, b) => a + b, 0); return Object.entries(types).map(([name, value]) => ({ name, value, percent: (value / total) * 100 })); }, [data]); // Rendu du graphique simple en SVG const SimpleChart = ({ data, color = "#10b981" }) => { if (!data || data.length === 0) return null; const height = 160; // h-40 const width = 100; // pourcentage const min = Math.min(...data.map(d => d.value)); const max = Math.max(...data.map(d => d.value)); const range = max - min; const points = data.map((d, i) => { const x = (i / (data.length - 1)) * 100; const y = 100 - ((d.value - min) / range) * 100; return `${x},${y}`; }).join(' '); return (
{/* Tooltip simulé au survol pourrait être ajouté ici */}
); }; if (loading) { return (

Chargement du portefeuille...

); } return (
{/* Top Navigation */} {/* Main Content */}
{/* Header Stats */}

Tableau de Bord

+{data.portfolio.dayChangePercent}%

Valeur Totale

{data.portfolio.totalValue.toLocaleString('fr-FR', { style: 'currency', currency: 'CAD' })}

+{data.portfolio.dayChange.toLocaleString('fr-FR', { style: 'currency', currency: 'CAD' })} aujourd'hui

Répartition
{assetAllocation.map((item) => (
{item.name === 'stock' ? 'Actions' : item.name === 'etf' ? 'FNBs' : 'Crypto'}
{item.value.toLocaleString('fr-FR', { style: 'currency', currency: 'CAD', maximumFractionDigits: 0 })} {item.percent.toFixed(0)}%
))}
Performance (14J)
{/* Main Sections Tab */}
{/* Content based on Tab */} {activeTab === 'overview' && (

Top Mouvements

{data.assets.slice(0, 4).map(asset => (
{asset.symbol[0]}
{asset.symbol}
{asset.name}
{(asset.price).toLocaleString('fr-FR', { style: 'currency', currency: 'USD' })}
= 0 ? 'text-emerald-600' : 'text-rose-600'}`}> {asset.change >= 0 ? : } {asset.change > 0 ? '+' : ''}{asset.change}%
))}

Activité Récente

{data.transactions.map(tx => (
{tx.type === 'deposit' ? : tx.type === 'sell' ? : tx.type === 'buy' ? : }
{tx.type} {tx.asset}
{new Date(tx.date).toLocaleDateString()}
{tx.type === 'deposit' || tx.type === 'sell' || tx.type === 'dividend' ? '+' : '-'} {tx.amount.toLocaleString('fr-FR', { style: 'currency', currency: 'CAD' })}
))}
)} {activeTab === 'holdings' && (
{data.assets.map((asset) => ( ))}
Actif Prix Quantité Valeur Totale Variation (24h)
{asset.symbol[0]}
{asset.symbol}
{asset.name}
{asset.price.toLocaleString('fr-FR', { style: 'currency', currency: 'USD' })} {asset.quantity} {(asset.price * asset.quantity).toLocaleString('fr-FR', { style: 'currency', currency: 'CAD' })} = 0 ? 'success' : 'danger'}> {asset.change > 0 ? '+' : ''}{asset.change}%
)}
); }