diff --git a/.gitignore b/.gitignore index 3369bad9..118bc9ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ .venv -results -env/ -__pycache__/ -.DS_Store +# Project specific +/results *.csv -src/ +*.json +*.log +*.db + +# Evaluation results eval_results/ -eval_data/ *.egg-info/ .env diff --git a/frontend/app/analysis/page.tsx b/frontend/app/analysis/page.tsx index 32f6e80b..6c5f06d2 100644 --- a/frontend/app/analysis/page.tsx +++ b/frontend/app/analysis/page.tsx @@ -4,15 +4,20 @@ "use client"; import { useState } from "react"; +import { useRouter } from "next/navigation"; import { AnalysisForm } from "@/components/analysis/AnalysisForm"; import { TradingDecision } from "@/components/analysis/TradingDecision"; import { AnalystReport } from "@/components/analysis/AnalystReport"; import { PriceChart } from "@/components/analysis/PriceChart"; import { LoadingSpinner } from "@/components/shared/LoadingSpinner"; +import { Button } from "@/components/ui/button"; import { useAnalysis } from "@/hooks/useAnalysis"; +import { useAnalysisContext } from "@/context/AnalysisContext"; import type { AnalysisRequest } from "@/lib/types"; export default function AnalysisPage() { + const router = useRouter(); + const { setAnalysisResult } = useAnalysisContext(); const { runAnalysis, loading, error, result } = useAnalysis(); const handleSubmit = async (request: AnalysisRequest) => { @@ -24,6 +29,13 @@ export default function AnalysisPage() { } }; + const handleViewResults = () => { + if (result) { + setAnalysisResult(result); + router.push("/analysis/results"); + } + }; + return (
@@ -49,6 +61,13 @@ export default function AnalysisPage() { {result && !loading && (
+ {/* 查看詳細結果按鈕 */} +
+ +
+ {/* 價格圖表 */} {result.price_data && result.price_stats && ( { + if (!analysisResult) { + router.push("/analysis"); + } + }, [analysisResult, router]); + + if (!analysisResult) { + return ( +
+
+

沒有分析結果

+

請先執行分析

+ +
+
+ ); + } + + const currentAnalyst = ANALYSTS.find(a => a.key === selectedAnalyst); + const currentReport = analysisResult.reports?.[currentAnalyst?.reportKey || ""]; + + return ( +
+
+ {/* Header */} +
+
+

+ {analysisResult.ticker} 詳細分析結果 +

+

+ 分析日期:{analysisResult.analysis_date} +

+
+ +
+ + {/* 分析師選擇 Tabs */} + + + {ANALYSTS.map(analyst => ( + + {analyst.label} + + ))} + + + {ANALYSTS.map(analyst => ( + +
+ {/* 價格圖表 - 每個分析師都有 */} + {analysisResult.price_data && analysisResult.price_stats && ( + + )} + + {/* 分析師報告 */} + + + {analyst.label} 報告 + + {analyst.key === "market" && "技術分析與市場趨勢評估"} + {analyst.key === "social" && "社群情緒與市場氛圍分析"} + {analyst.key === "news" && "新聞事件與影響分析"} + {analyst.key === "fundamentals" && "財務數據與基本面分析"} + + + + {currentReport ? ( +
+
+                          {currentReport}
+                        
+
+ ) : ( +
+

+ 此分析師沒有生成報告 +

+

+ 可能此分析師未被選擇或分析過程中未產生報告 +

+
+ )} +
+
+
+
+ ))} +
+
+
+ ); +} diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 68ebb866..5de855d1 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -3,6 +3,7 @@ import { Inter } from "next/font/google"; import "./globals.css"; import { Header } from "@/components/layout/Header"; import { Footer } from "@/components/layout/Footer"; +import { AnalysisProvider } from "@/context/AnalysisContext"; const inter = Inter({ subsets: ["latin"] }); @@ -19,13 +20,15 @@ export default function RootLayout({ return ( -
-
-
- {children} -
-
-
+ +
+
+
+ {children} +
+
+
+
); diff --git a/frontend/context/AnalysisContext.tsx b/frontend/context/AnalysisContext.tsx new file mode 100644 index 00000000..9d1492db --- /dev/null +++ b/frontend/context/AnalysisContext.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { createContext, useContext, useState, ReactNode } from "react"; +import type { AnalysisResponse } from "@/lib/types"; + +interface AnalysisContextType { + analysisResult: AnalysisResponse | null; + setAnalysisResult: (result: AnalysisResponse | null) => void; +} + +const AnalysisContext = createContext( + undefined +); + +export function AnalysisProvider({ children }: { children: ReactNode }) { + const [analysisResult, setAnalysisResult] = useState( + null + ); + + return ( + + {children} + + ); +} + +export function useAnalysisContext() { + const context = useContext(AnalysisContext); + if (!context) { + throw new Error("useAnalysisContext must be used within AnalysisProvider"); + } + return context; +}