From d3fccbd4e44fec4ed0a42a37442ba7f027aca6cc Mon Sep 17 00:00:00 2001 From: Jenit Jain Date: Sun, 10 Aug 2025 01:02:35 -0700 Subject: [PATCH] Fix View Results tab --- web_app/frontend/src/App.js | 157 ++++++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 6 deletions(-) diff --git a/web_app/frontend/src/App.js b/web_app/frontend/src/App.js index 7aaf90ac..f2b2ee75 100644 --- a/web_app/frontend/src/App.js +++ b/web_app/frontend/src/App.js @@ -26,6 +26,64 @@ function App() { const [selectedTransformedData, setSelectedTransformedData] = useState(null); const [isLoadingTransformedData, setIsLoadingTransformedData] = useState(false); const [transformedDataError, setTransformedDataError] = useState(null); + const [activeDetailTab, setActiveDetailTab] = useState(null); + + // Fields to display as pretty cards in the Details modal + const detailFields = [ + '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(() => { @@ -62,6 +120,57 @@ function App() { 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 (showAnalysisModal) { + setShowAnalysisModal(false); + } + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [showDetailModal, showWidgetsView, showTransformedDataModal, showResultsModal, 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]); + const checkBackendStatus = async () => { try { await axios.get('/health'); @@ -639,12 +748,48 @@ function App() {
-
-

Analysis Data

-
-                    {JSON.stringify(resultDetail, null, 2)}
-                  
-
+ {/* 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)}
+
+
+ ); + })()}