Team Ai
Apppublic

Deepvest/ProfilingAI

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
AppContainer.tsx198 linesDownload Raw Back to components
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;