import React, { useState, useEffect, useMemo } from 'react'; import axios from 'axios'; import AnalysisDataAdapter from './components/AnalysisDataAdapter.tsx'; import TransformedDataAdapter from './components/TransformedDataAdapter.tsx'; import transformedDataService from './services/transformedDataService.ts'; function App() { const [backendStatus, setBackendStatus] = useState('checking'); const [companies, setCompanies] = useState([]); const [stats, setStats] = useState({ totalAnalyses: 0, companies: 0 }); const [showAnalysisModal, setShowAnalysisModal] = useState(false); const [showResultsModal, setShowResultsModal] = useState(false); const [showDetailModal, setShowDetailModal] = useState(false); const [showWidgetsView, setShowWidgetsView] = useState(false); const [showTransformedDataModal, setShowTransformedDataModal] = useState(false); const [showJobsModal, setShowJobsModal] = useState(false); const [analysisForm, setAnalysisForm] = useState({ symbol: '', date: '' }); const [isRunningAnalysis, setIsRunningAnalysis] = useState(false); const [selectedCompany, setSelectedCompany] = useState(null); const [companyResults, setCompanyResults] = useState([]); const [selectedResult, setSelectedResult] = useState(null); const [resultDetail, setResultDetail] = useState(null); // New state for transformed data const [transformedDataSummary, setTransformedDataSummary] = useState(null); const [selectedTransformedData, setSelectedTransformedData] = useState(null); const [isLoadingTransformedData, setIsLoadingTransformedData] = useState(false); const [transformedDataError, setTransformedDataError] = useState(null); const [selectedTransformedCompany, setSelectedTransformedCompany] = useState(null); const [transformedCompanyFiles, setTransformedCompanyFiles] = useState([]); const [activeDetailTab, setActiveDetailTab] = useState(null); // Job management state const [jobs, setJobs] = useState([]); const [jobsStats, setJobsStats] = useState({ total: 0, running: 0, completed: 0, failed: 0 }); const [isLoadingJobs, setIsLoadingJobs] = useState(false); // Fields to display as pretty cards in the Details modal const detailFields = useMemo(() => ([ 'market_report', 'sentiment_report', 'news_report', 'fundamentals_report', 'trader_investment_decision', 'investment_plan', 'final_trade_decision', 'investment_debate_state.bull_history', 'investment_debate_state.bear_history', 'investment_debate_state.history', 'investment_debate_state.current_response', 'investment_debate_state.judge_decision', 'risk_debate_state.risky_history', 'risk_debate_state.safe_history', 'risk_debate_state.neutral_history', 'risk_debate_state.history', 'risk_debate_state.judge_decision', 'company_of_interest', 'trade_date', ]), []); const fieldLabelMap = { market_report: 'Market Report', sentiment_report: 'Sentiment Report', news_report: 'News Report', fundamentals_report: 'Fundamentals Report', trader_investment_decision: 'Trader Investment Decision', investment_plan: 'Investment Plan', final_trade_decision: 'Final Trade Decision', 'investment_debate_state.bull_history': 'Investment Debate - Bull History', 'investment_debate_state.bear_history': 'Investment Debate - Bear History', 'investment_debate_state.history': 'Investment Debate - History', 'investment_debate_state.current_response': 'Investment Debate - Current Response', 'investment_debate_state.judge_decision': 'Investment Debate - Judge Decision', 'risk_debate_state.risky_history': 'Risk Debate - Risky History', 'risk_debate_state.safe_history': 'Risk Debate - Safe History', 'risk_debate_state.neutral_history': 'Risk Debate - Neutral History', 'risk_debate_state.history': 'Risk Debate - History', 'risk_debate_state.judge_decision': 'Risk Debate - Judge Decision', company_of_interest: 'Company of Interest', trade_date: 'Trade Date', }; // Helpers const getNested = (obj, path) => { if (!obj || !path) return undefined; return path.split('.').reduce((acc, key) => (acc != null ? acc[key] : undefined), obj); }; const prettyValue = (val) => { if (val === null || val === undefined) return ''; if (typeof val === 'string') return val; try { return JSON.stringify(val, null, 2); } catch { return String(val); } }; // Close only the topmost open modal on Escape, preserving underlying modals useEffect(() => { const handleKeyDown = (e) => { if (e.key === 'Escape') { if (showDetailModal) { setShowDetailModal(false); return; } if (showWidgetsView) { setShowWidgetsView(false); return; } if (showTransformedDataModal) { setShowTransformedDataModal(false); return; } if (showResultsModal) { setShowResultsModal(false); return; } if (showJobsModal) { setShowJobsModal(false); return; } if (showAnalysisModal) { setShowAnalysisModal(false); } } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [showDetailModal, showWidgetsView, showTransformedDataModal, showResultsModal, showJobsModal, showAnalysisModal]); useEffect(() => { checkBackendStatus(); fetchCompanies(); loadTransformedDataSummary(); }, []); useEffect(() => { const handleKeyDown = (e) => { if (e.key === 'Escape') { if (showDetailModal) { setShowDetailModal(false); return; } if (showWidgetsView) { setShowWidgetsView(false); return; } if (showTransformedDataModal) { setShowTransformedDataModal(false); return; } if (showResultsModal) { setShowResultsModal(false); return; } if (showJobsModal) { setShowJobsModal(false); return; } if (showAnalysisModal) { setShowAnalysisModal(false); } } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [showDetailModal, showWidgetsView, showTransformedDataModal, showResultsModal, showJobsModal, showAnalysisModal]); // Keep the active details tab in sync with available fields for the selected result useEffect(() => { const baseData = resultDetail?.data?.[selectedResult?.date]; if (!baseData) { setActiveDetailTab(null); return; } const available = (detailFields || []).filter((path) => { const val = path.split('.').reduce((acc, k) => (acc != null ? acc[k] : undefined), baseData); if (val === undefined || val === null) return false; if (typeof val === 'object') { if (Array.isArray(val)) return val.length > 0; return Object.keys(val).length > 0; } return true; }); if (available.length === 0) { setActiveDetailTab(null); } else if (!activeDetailTab || !available.includes(activeDetailTab)) { setActiveDetailTab(available[0]); } }, [resultDetail, selectedResult, detailFields, activeDetailTab]); const checkBackendStatus = async () => { try { await axios.get('/health'); setBackendStatus('connected'); } catch (error) { setBackendStatus('disconnected'); } }; const fetchCompanies = async () => { try { const response = await axios.get('/results/companies'); const companiesData = response.data.companies || []; setCompanies(companiesData); const totalAnalyses = companiesData.reduce((sum, company) => sum + company.total_analyses, 0); setStats({ totalAnalyses, companies: companiesData.length }); } catch (error) { console.error('Error fetching companies:', error); } }; const loadTransformedDataSummary = async () => { try { const summary = await transformedDataService.getDataSummary(); setTransformedDataSummary(summary); } catch (error) { console.error('Error loading transformed data summary:', error); setTransformedDataError('Failed to load transformed data'); } }; const fetchCompanyResults = async (symbol) => { try { const response = await axios.get(`/results/${symbol}`); setCompanyResults(response.data.results || []); setSelectedCompany(symbol); } catch (error) { console.error('Error fetching company results:', error); alert('Error loading company results'); } }; const openDetailModal = async (result) => { try { const response = await axios.get(`/results/${selectedCompany}/${result.date}`); setResultDetail(response.data); setSelectedResult({ ...result, company: selectedCompany }); setShowDetailModal(true); } catch (error) { console.error('Error fetching result detail:', error); alert('Error loading result details'); } }; const openWidgetsView = async (result) => { try { const response = await axios.get(`/results/${selectedCompany}/${result.date}`); setResultDetail(response.data); setSelectedResult({ ...result, company: selectedCompany }); setShowWidgetsView(true); } catch (error) { console.error('Error fetching result detail:', error); alert('Error loading analysis dashboard'); } }; const openTransformedWidgetsView = async (file) => { setIsLoadingTransformedData(true); setTransformedDataError(null); try { // Load by company and date to avoid relying on cached index const transformedData = await transformedDataService.loadByCompanyAndDate(selectedTransformedCompany, file.date); setSelectedTransformedData(transformedData); setShowWidgetsView(true); } catch (error) { console.error('Error loading transformed data:', error); setTransformedDataError(`Failed to load ${file.filename}: ${error.message}`); } finally { setIsLoadingTransformedData(false); } }; const handleStartAnalysis = () => { if (backendStatus !== 'connected') { alert('Backend is not connected. Please ensure the backend server is running.'); return; } setShowAnalysisModal(true); }; const handleViewResults = () => { if (backendStatus !== 'connected') { alert('Backend is not connected. Please ensure the backend server is running.'); return; } setShowResultsModal(true); setSelectedCompany(null); setCompanyResults([]); }; const handleViewTransformedData = async () => { setShowTransformedDataModal(true); setTransformedDataError(null); setSelectedTransformedCompany(null); setTransformedCompanyFiles([]); try { transformedDataService.clearCache(); const summary = await transformedDataService.getDataSummary(); setTransformedDataSummary(summary); } catch (e) { console.error('Failed to load transformed data on open:', e); setTransformedDataError('Failed to load transformed data'); } }; const runAnalysis = async () => { setIsRunningAnalysis(true); try { await axios.post('/analysis/start', analysisForm); alert('Analysis completed successfully!'); setShowAnalysisModal(false); setAnalysisForm({ symbol: '', date: '' }); fetchCompanies(); // Refresh the companies list } catch (error) { console.error('Error running analysis:', error); alert('Error running analysis. Please try again.'); } finally { setIsRunningAnalysis(false); } }; const closeAllModals = () => { setShowAnalysisModal(false); setShowResultsModal(false); setShowDetailModal(false); setShowWidgetsView(false); setShowTransformedDataModal(false); setShowJobsModal(false); setSelectedResult(null); setResultDetail(null); setSelectedTransformedData(null); setTransformedDataError(null); }; const fetchTransformedCompanyFiles = async (symbol) => { try { const response = await axios.get(`/transformed-results/${symbol}`); setTransformedCompanyFiles(response.data.results || []); setSelectedTransformedCompany(symbol); } catch (error) { console.error('Error fetching transformed company files:', error); setTransformedDataError('Error loading transformed company files'); } }; const fetchJobs = async () => { setIsLoadingJobs(true); try { const response = await axios.get('/jobs'); const jobsData = response.data.jobs || []; setJobs(jobsData); const stats = { total: jobsData.length, running: jobsData.filter((job) => job.status === 'running').length, completed: jobsData.filter((job) => job.status === 'completed').length, failed: jobsData.filter((job) => job.status === 'failed').length, }; setJobsStats(stats); } catch (error) { console.error('Error fetching jobs:', error); } finally { setIsLoadingJobs(false); } }; const handleViewJobResult = (job) => { setSelectedResult(job); setShowJobsModal(false); setShowResultsModal(true); }; return (
{/* Header */}

TradingAgents

{backendStatus === 'connected' ? '● Connected' : backendStatus === 'disconnected' ? '● Disconnected' : '● Checking...'}
{/* Main Content */}
{/* Stats Cards */}
📊
Total Analyses
{stats.totalAnalyses}
🏢
Companies
{stats.companies}
🔄
Transformed Data
{transformedDataSummary ? transformedDataSummary.totalFiles : '---'}
{/* Action Buttons */}
{/* Transformed Data Modal */} {showTransformedDataModal && (

{selectedTransformedCompany ? `${selectedTransformedCompany} Transformed Analyses` : 'Transformed Analysis Data'}

{selectedTransformedCompany && ( )}
{transformedDataError && (

{transformedDataError}

)} {transformedDataSummary && (

Data Summary

Total Files: {transformedDataSummary.totalFiles}
Companies: {transformedDataSummary.companies.join(', ')}
Date Range: {transformedDataSummary.dateRange.earliest} to {transformedDataSummary.dateRange.latest}
)}
{!selectedTransformedCompany ? ( // Company list view (only companies with transformed analyses) companies && companies.filter(c => (c.transformed_analyses || 0) > 0).length > 0 ? (
{companies.filter(c => (c.transformed_analyses || 0) > 0).map((company) => (
fetchTransformedCompanyFiles(company.symbol)} >
{company.symbol}
Transformed analyses: {company.transformed_analyses}
View Files
))}
) : (
No companies with transformed data found
Run the data transformation agent to generate transformed analyses
) ) : ( // File list for selected transformed company transformedCompanyFiles.length > 0 ? (
{transformedCompanyFiles.map((file, index) => (
{selectedTransformedCompany} - {file.date}
{file.filename}
))}
) : (
No transformed files found for {selectedTransformedCompany}
) )}
)} {/* Widgets View Modal */} {showWidgetsView && (

Analysis Dashboard {selectedResult && ` - ${selectedResult.company} (${selectedResult.date})`} {selectedTransformedData && ` - ${selectedTransformedData.metadata.company_ticker} (${selectedTransformedData.metadata.analysis_date})`}

{selectedTransformedData ? ( ) : resultDetail ? ( ) : (
Loading analysis data...
)}
)} {/* Analysis Modal */} {showAnalysisModal && (

Start New Analysis

setAnalysisForm({...analysisForm, symbol: e.target.value})} placeholder="e.g., AAPL, TSLA, NVDA" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" />
setAnalysisForm({...analysisForm, date: e.target.value})} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" />
)} {/* Results Modal */} {showResultsModal && (

{selectedCompany ? `${selectedCompany} Analysis Results` : "Analysis Results"}

{selectedCompany && ( )}
{!selectedCompany ? ( // Company list view companies.length > 0 ? (
{companies.map((company) => (
fetchCompanyResults(company.symbol)} >
{company.symbol}
{company.total_analyses} analyses
{company.latest_analysis}
))}
) : (
No analysis results available
Start your first analysis to see results here
) ) : ( // Company results view

{selectedCompany} Results

{companyResults.length > 0 ? (
{companyResults.map((result, index) => (
{result.filename}
{new Date(result.timestamp).toLocaleString()}
))}
) : (
No results found for {selectedCompany}
)}
)}
)} {/* Jobs Modal */} {showJobsModal && (

Job Management

{/* Job Statistics */}
{jobsStats.total}
Total Jobs
{jobsStats.running}
Running
{jobsStats.completed}
Completed
{jobsStats.failed}
Failed
{isLoadingJobs ? (
Loading jobs...
) : (
{jobs.length > 0 ? (
{jobs.map((job, index) => (
{job.status === 'running' && '🔄'} {job.status === 'completed' && '✅'} {job.status === 'failed' && '❌'} {job.status}
Job ID: {job.job_id}
{job.result && (
Symbol: {job.result.symbol} | Date: {job.result.date}
)} {job.progress && (
{job.progress}
)} {job.error && (
Error: {job.error}
)} {job.result && job.result.completed_at && (
Completed: {new Date(job.result.completed_at).toLocaleString()}
)}
{job.status === 'completed' && job.result && ( <> )}
))}
) : (
No jobs found
Run a new analysis to see jobs here
)}
)}
)} {/* Detail Modal */} {showDetailModal && (

{selectedResult?.company} Analysis Details

{/* Dropdown selector */} {(() => { const baseData = resultDetail?.data?.[selectedResult?.date]; if (!baseData) return null; const available = detailFields .map((path) => ({ path, val: getNested(baseData, path) })) .filter(({ val }) => { if (val === undefined || val === null) return false; if (typeof val === 'object') { if (Array.isArray(val)) return val.length > 0; return Object.keys(val).length > 0; } return true; }); if (available.length === 0) return null; const current = available.find(({ path }) => path === activeDetailTab) || available[0]; const activePath = current.path; const activeVal = current.val; return (
{/* Content */}

{fieldLabelMap[activePath] || activePath}

{prettyValue(activeVal)}
); })()}
)}
); } export default App;