Deepvest/ProfilingAI
0
1import type { FC } from 'react';2import { useState, useEffect } from 'react';3import UserProfileForm from './UserProfileForm';4 5interface InvestmentPreferences {6 esg: boolean;7 crypto: boolean;8 real_estate: boolean;9 alternative: boolean;10}11 12interface ProfileData {13 // Informations personnelles14 age: string;15 annual_income: string;16 monthly_savings: string;17 family_status: string;18 dependents: number;19 20 // Profil d'investissement21 risk_tolerance: number;22 investment_horizon: number;23 financial_knowledge: number;24 income_stability: number;25 26 // Objectifs et préférences27 investment_goals: string[];28 life_events: string[];29 investment_preferences: InvestmentPreferences;30 31 // Situation financière32 total_assets: string;33 total_debt: string;34}35 36// Interface pour la communication avec Streamlit37declare global {38 interface Window {39 Streamlit: {40 setComponentValue: (value: any) => void;41 on: (type: string, callback: (event: any) => void) => void;42 };43 }44}45 46const AppContainer: FC = () => {47 // État pour stocker le profil courant48 const [currentProfile, setCurrentProfile] = useState<ProfileData | null>(null);49 // État pour gérer le chargement50 const [isLoading, setIsLoading] = useState(false);51 // État pour gérer les erreurs52 const [error, setError] = useState<string | null>(null);53 54 // Initialisation et écoute des événements Streamlit55 useEffect(() => {56 if (window.Streamlit) {57 // Écouter les mises à jour de Streamlit58 window.Streamlit.on('change', (event) => {59 if (event.type === 'PROFILE_DATA') {60 setCurrentProfile(event.data);61 }62 });63 }64 }, []);65 66 // Gestionnaire de soumission du profil67 const handleProfileSubmit = async (profileData: ProfileData) => {68 try {69 setIsLoading(true);70 setError(null);71 72 // Validation supplémentaire des données73 if (!validateProfileData(profileData)) {74 throw new Error("Données de profil invalides");75 }76 77 // Mise à jour de l'état local78 setCurrentProfile(profileData);79 80 // Envoi des données à Streamlit81 if (window.Streamlit) {82 window.Streamlit.setComponentValue({83 type: 'PROFILE_UPDATE',84 data: profileData,85 timestamp: new Date().toISOString()86 });87 }88 89 setIsLoading(false);90 } catch (err) {91 setError(err instanceof Error ? err.message : "Une erreur est survenue");92 setIsLoading(false);93 console.error('Error handling profile submission:', err);94 }95 };96 97 // Fonction de validation des données98 const validateProfileData = (data: ProfileData): boolean => {99 if (parseInt(data.age) < 18) return false;100 if (parseInt(data.annual_income) < 0) return false;101 if (data.investment_goals.length === 0) return false;102 if (data.risk_tolerance < 1 || data.risk_tolerance > 5) return false;103 return true;104 };105 106 return (107 <div className="min-h-screen bg-gray-50">108 <div className="max-w-7xl mx-auto px-4 py-8">109 {/* En-tête */}110 <header className="mb-8">111 <h1 className="text-3xl font-bold text-gray-900">112 DeepVest - Votre Assistant d'Investissement Personnalisé113 </h1>114 {currentProfile && (115 <p className="mt-2 text-gray-600">116 Profil créé le {new Date().toLocaleDateString()}117 </p>118 )}119 </header>120 121 {/* Message d'erreur */}122 {error && (123 <div className="mb-6 bg-red-50 border-l-4 border-red-500 p-4">124 <div className="flex">125 <div className="flex-shrink-0">126 <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">127 <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />128 </svg>129 </div>130 <div className="ml-3">131 <p className="text-sm text-red-700">{error}</p>132 </div>133 </div>134 </div>135 )}136 137 {/* Indicateur de chargement */}138 {isLoading && (139 <div className="flex justify-center items-center my-4">140 <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>141 </div>142 )}143 144 {/* Formulaire de profil */}145 <div className="bg-white shadow rounded-lg">146 <UserProfileForm 147 onSubmit={handleProfileSubmit} 148 />149 </div>150 151 {/* Affichage du profil actuel */}152 {currentProfile && (153 <div className="mt-8 bg-white shadow rounded-lg p-6">154 <h2 className="text-xl font-semibold mb-4">Profil actuel</h2>155 <div className="grid grid-cols-1 md:grid-cols-2 gap-4">156 <div>157 <h3 className="font-medium text-gray-900">Informations personnelles</h3>158 <dl className="mt-2 text-sm text-gray-600">159 <div className="mt-1">160 <dt className="inline">Âge:</dt>161 <dd className="inline ml-1">{currentProfile.age} ans</dd>162 </div>163 <div className="mt-1">164 <dt className="inline">Revenu annuel:</dt>165 <dd className="inline ml-1">{parseInt(currentProfile.annual_income).toLocaleString()}€</dd>166 </div>167 <div className="mt-1">168 <dt className="inline">Situation familiale:</dt>169 <dd className="inline ml-1">{currentProfile.family_status}</dd>170 </div>171 </dl>172 </div>173 <div>174 <h3 className="font-medium text-gray-900">Profil d'investissement</h3>175 <dl className="mt-2 text-sm text-gray-600">176 <div className="mt-1">177 <dt className="inline">Tolérance au risque:</dt>178 <dd className="inline ml-1">{currentProfile.risk_tolerance}/5</dd>179 </div>180 <div className="mt-1">181 <dt className="inline">Horizon:</dt>182 <dd className="inline ml-1">{currentProfile.investment_horizon} ans</dd>183 </div>184 <div className="mt-1">185 <dt className="inline">Objectifs:</dt>186 <dd className="inline ml-1">{currentProfile.investment_goals.join(", ")}</dd>187 </div>188 </dl>189 </div>190 </div>191 </div>192 )}193 </div>194 </div>195 );196};197 198export default AppContainer;